PDA

View Full Version : Reading Chat in LUA


Elliss11
11-15-2015, 09:38 PM
Hi.. i´m not a developer but I want to convert an existing plugin for own use.
can someone tell me how I have to write it that the counter will only count if a certain text found ?

for example :
if "test" match in Chat(PlayerCounter) then Count+1


ChatMonitor = Turbine.Chat;
ChatMonitor.Received = function(f, args)
local msg = args.Message;
if(args.ChatType == Turbine.ChatType.PlayerCombat) then
count=Count+1
do_update()
end
end


thanks for helping
Elliss11

Garan
11-16-2015, 05:45 AM
Before addressing how to match a specific chat message, I would like to point out that there are two errors in the code snippet above. The first is relatively minor but would still cause the code to fail - Lua is case sensitive, the line "count = Count + 1" will not work as intended since the second "Count" starts with a capital letter. I also assume you initialize the value somewhere otherwise you will get an error about adding 1 to nil.

The second error is the more significant error and will not only affect your plugin but any other plugin in the same environment. The line that starts "ChatMonitor.Recieved = " assigns a specific value to a shared object. All Turbine objects are shared and their event handlers should never be assigned a specific function or you will clobber any other already loaded plugin's event handlers. Additionally, the Lua environment does not always exit nicely if event handlers are not removed prior to plugins unloading. To avoid this, we use a pair of functions, "AddCallback" and "RemoveCallback" to allow multiple plugins to share event handlers and to safely clean up prior to unloading. This is all discussed in greater detail in the post:
https://www.lotro.com/forums/showthread.php?428196-Writing-LoTRO-Lua-Plugins-for-Noobs&p=5784203#post5784203

As to matching a specific message, that is fairly simple. The code snippet you posted already tests for a specific channel. The only thing left to do is add a comparison for the message:
if args.Message=="some message" then
-- do something
end

It is generally more desirable to match a "pattern" or sub string of the message. That gets a tiny bit more complicated as you use the string.match() function and Lua patterns (similar to but not identical to Regular Expressions):
matches={string.match(args.Message,"some pattern")}
Lua patterns are described in greater detail in section 5.4.1 of the online Lua 5.1 manual:
http://www.lua.org/manual/5.1/manual.html#5.4.1

Elliss11
11-16-2015, 07:54 AM
OK . I must admit that I'm going to get it not alone.
there are other things that I have not observed.
I would like to not have the complete message as a hit ,only a part.


Also
Is in CombatPlayer Channel the word "test" found , then Count+1

Example:
This is a test

count+1

can someone help me there?

thanks
have a nice day

Garan
11-16-2015, 09:20 AM
OK . I must admit that I'm going to get it not alone.
there are other things that I have not observed.
I would like to not have the complete message as a hit ,only a part.


Also
Is in CombatPlayer Channel the word "test" found , then Count+1

Example:
This is a test

count+1

can someone help me there?

thanks
have a nice day

A simplified version of the string.find function will work for this specific case since you are looking for a static substring, in this case the word "test":

if string.find (args.Message,"test",1,true)~=nil then
end

so, your code snippet becomes:

count=0; -- initialize variable
-- define the AddCallback function
function AddCallback(object, event, callback)
if (object[event] == nil) then
object[event] = callback;
else
if (type(object[event]) == "table") then
table.insert(object[event], callback);
else
object[event] = {object[event], callback};
end
end
return callback;
end

ChatMonitor = Turbine.Chat;
ChatReceived = function(f, args)
local msg = args.Message;
if(args.ChatType == Turbine.ChatType.PlayerCombat) then
if string.find (args.Message,"test",1,true)~=nil then
count=count+1
do_update()
end
end
end
AddCallback(Turbine.Chat, "Received", ChatReceived)


Note, you should still read about AddCallback and RemoveCallback and use RemoveCallback in your plugin's unload handler to avoid crashing the client (only in very rare circumstances now that Turbine has fixed a number of their bugs) when unloading the plugin.

Elliss11
11-16-2015, 10:06 AM
it works .... thank you for your time

Elliss11
11-16-2015, 11:13 AM
it works fantastic...But one thing I'd like to improve it.

<<<<<<<<<<<<if Out of Combat then
BodyStatus:SetText("6")
else



count=0; -- initialize variable
-- define the AddCallback function
function AddCallback(object, event, callback)
if (object[event] == nil) then
object[event] = callback;
else
if (type(object[event]) == "table") then
table.insert(object[event], callback);
else
object[event] = {object[event], callback};
end
end
return callback;
end

ChatMonitor = Turbine.Chat;
ChatReceived = function(f, args)
local msg = args.Message;

<<<<<<<<<<<<
if(args.ChatType == Turbine.ChatType.PlayerCombat) then
if string.find (args.Message,"Verbesserter Gnadenschuss",1,true)~=nil then
BodyStatus:SetText("6")
else
if killcount==0 then
do_update()
else
if string.find (args.Message,"Verbesserter Schneller Bogen",1,true)~=nil then
killcount=killcount-0.333
do_update()

end
end
end
end
end

AddCallback(Turbine.Chat, "Received", ChatReceived)


can you help me there too?

have a nice day
elliss11

Garan
11-16-2015, 11:59 AM
The test for combat state is localPlayer:IsInCombat() where localPlayer is an instance of the Turbine.Gameplay.LocalPlayer object.
I'm not sure of your intent, but it looks like you should put the condition you are testing in the chat handler (definitely not outside the function definition). That does leave some situations where you could leave combat without generating any messages in that channel which might not always be the result you are after.
I believe you are after something like:


localPlayer=Turbine.Gameplay.LocalPlayer:GetInstan ce();
count=0; -- initialize variable
-- define the AddCallback function
function AddCallback(object, event, callback)
if (object[event] == nil) then
object[event] = callback;
else
if (type(object[event]) == "table") then
table.insert(object[event], callback);
else
object[event] = {object[event], callback};
end
end
return callback;
end

ChatMonitor = Turbine.Chat;
ChatReceived = function(f, args)
local msg = args.Message;

if localPlayer:IsInCombat() then
if(args.ChatType == Turbine.ChatType.PlayerCombat) then
if string.find (args.Message,"Verbesserter Gnadenschuss",1,true)~=nil then
BodyStatus:SetText("6")
else
if killcount==0 then
do_update()
else
if string.find (args.Message,"Verbesserter Schneller Bogen",1,true)~=nil then
killcount=killcount-0.333
do_update()

end
end
end
end
else
BodyStatus:SetText("6")
end
end

AddCallback(Turbine.Chat, "Received", ChatReceived)



If as I assumed earlier, you are trying to default the text to "6" whenever you exit combat then the correct approach is to use the InCombatChanged handler to set the default text:

localPlayer=Turbine.Gameplay.LocalPlayer:GetInstan ce();
count=0; -- initialize variable
-- define the AddCallback function
function AddCallback(object, event, callback)
if (object[event] == nil) then
object[event] = callback;
else
if (type(object[event]) == "table") then
table.insert(object[event], callback);
else
object[event] = {object[event], callback};
end
end
return callback;
end

CombatChanged = function(sender, args)
if not localPlayer:IsInCombat() then
BodyStatus:SetText("6")
end
end
AddCallback(localPlayer, "InCombatChanged", CombatChanged)

ChatMonitor = Turbine.Chat;
ChatReceived = function(f, args)
local msg = args.Message;

if(args.ChatType == Turbine.ChatType.PlayerCombat) then
if string.find (args.Message,"Verbesserter Gnadenschuss",1,true)~=nil then
BodyStatus:SetText("6")
else
if killcount==0 then
do_update()
else
if string.find (args.Message,"Verbesserter Schneller Bogen",1,true)~=nil then
killcount=killcount-0.333
do_update()
end
end
end
end
end

AddCallback(Turbine.Chat, "Received", ChatReceived)

Elliss11
11-16-2015, 12:34 PM
The second text is working with a small change . but you had me already pointed


THANKS !!!!



localPlayer=Turbine.Gameplay.LocalPlayer:GetInstan ce();
count=0; -- initialize variable
-- define the AddCallback function
function AddCallback(object, event, callback)
if (object[event] == nil) then
object[event] = callback;
else
if (type(object[event]) == "table") then
table.insert(object[event], callback);
else
object[event] = {object[event], callback};
end
end
return callback;
end

CombatChanged = function(sender, args)
if not localPlayer:IsInCombat() then
--BodyStatus:SetText("6")
killcount=6;
do_update()
end
end
AddCallback(localPlayer, "InCombatChanged", CombatChanged)

ChatMonitor = Turbine.Chat;
ChatReceived = function(f, args)
local msg = args.Message;

if(args.ChatType == Turbine.ChatType.PlayerCombat) then
if string.find (args.Message,"Verbesserter Gnadenschuss",1,true)~=nil then
killcount=6;
do_update()
else
if killcount<=0 then
do_update()
else
if string.find (args.Message,"Verbesserter Schneller Bogen",1,true)~=nil then
killcount=killcount-0.333
do_update()
end
end
end
end
end

AddCallback(Turbine.Chat, "Received", ChatReceived)

Elliss11
11-16-2015, 02:32 PM
CombatChanged = function(sender, args)
if not localPlayer:IsInCombat() then
--BodyStatus:SetText("6")

<<<<<<<

killcount=6;
do_update()
end
end
AddCallback(localPlayer, "InCombatChanged", CombatChanged)



Is it still possible here with a sleep function to install ??

because the buff after end of the fight still goes 9sec

Elliss11
11-21-2015, 03:12 PM
#push

Garan
11-21-2015, 05:15 PM
CombatChanged = function(sender, args)
if not localPlayer:IsInCombat() then
--BodyStatus:SetText("6")

<<<<<<<

killcount=6;
do_update()
end
end
AddCallback(localPlayer, "InCombatChanged", CombatChanged)



Is it still possible here with a sleep function to install ??

because the buff after end of the fight still goes 9sec
This is a tiny bit more complicated - instead of testing the combat state whenever you exit combat, it sounds as though you want to determine when the buff ends. That raises a new question, do you want the status changed whenever the buff ends, or only when it ends out of combat? In either case, you want an event handler that tracks the player EffectList, not the combat state change. Unfortunately, I haven't played with effect lists much and without knowing exactly which effect you are trying to track, I can't provide the exact code, but the basic idea is that you want to handle the EffectRemoved event for the local player EffectList and test for the specific effect you are using as a condition.

The general idea is something like:

localPlayer=Turbine.Gameplay.LocalPlayer:GetInstan ce();
effectList=localPlayer:GetEffects()
EffectRemovedHandler=function(sender,args)
if args.Effect:GetName()=="some effect" then
killcount=6;
do_update()
end
end
AddCallback(effectList,"EffectRemoved",EffectRemovedHandler)

where "some effect" is the actual name of the Effect you are looking for.
Note, I didn't have a chance to test this in game so the args.Effect element may not exist in which case you would have to check what the actual elements in the args table are. To list the elements, use:

EffectRemoved=function(sender,args)
for k,v in pairs(args) do
Turbine.Shell.WriteLine(tostring(k)..":"..tostring(v))
end
end

If you only want to set the status when out of combat then just add a test for localPlayer:IsInCombat() to the handler:

localPlayer=Turbine.Gameplay.LocalPlayer:GetInstan ce();
effectList=localPlayer:GetEffects()
EffectRemovedHandler=function(sender,args)
if args.Effect:GetName()=="some effect" and not localPlayer:IsInCopmbat() then
killcount=6;
do_update()
end
end
AddCallback(effectList,"EffectRemoved",EffectRemovedHandler)

Elliss11
11-21-2015, 07:50 PM
hi

if i have not the buff then
Killcount=6;
do_update()


the buff calls "Schnelle Erlösung" in german Client
"Schnelle Erl\195\182sung"

i try something but it doesn´t work. i try it with other buffs too.

localPlayer=Turbine.Gameplay.LocalPlayer:GetInstan ce();
effectList=localPlayer:GetEffects()
EffectRemovedHandler=function(sender,args)
if args.Effect:GetName()=="Schnelle Erl\195\182sung" then
killcount=6;
do_update()
end
end
AddCallback(effectList,"EffectRemoved",EffectRemovedHandler)

EffectRemoved=function(sender,args)
for k,v in pairs(args) do
Turbine.Shell.WriteLine(tostring(k)..":"..tostring(v))
end
end

... of the Rings Online\Plugins\BodyCount\BodyCount.lua:147: attempt to index field 'Effect' (a nil value)



thanks for your time

Garan
11-21-2015, 10:19 PM
Yep, as I noted in my earlier post, I wasn't sure what the args table contained for EffectRemoved. It turns out it contains the index of the effect being removed. Unfortunately, it is also very buggy. Many times when more than one effect are being removed simultaneously the effect has already been removed from the list before the event fires so the index is useless and there is no way to determine the effect that was removed. I will play with it some more when I find time.

Elliss11
11-22-2015, 05:23 AM
Thanks

Thurallor
11-22-2015, 03:08 PM
Yep, as I noted in my earlier post, I wasn't sure what the args table contained for EffectRemoved. It turns out it contains the index of the effect being removed. Unfortunately, it is also very buggy. Many times when more than one effect are being removed simultaneously the effect has already been removed from the list before the event fires so the index is useless and there is no way to determine the effect that was removed. I will play with it some more when I find time.
I played with this a while back. Based on my observations, the EffectRemoved event also supplies an Effect argument, but only if you've previously read the effects list (with a effectsObject:Get() loop). You can use that argument to get the name of the effect, although that is by no means a unique identifier. (Lots of different effects have the same names.) It may be good enough for the OP's purposes, though.

Garan
11-22-2015, 06:39 PM
I played with this a while back. Based on my observations, the EffectRemoved event also supplies an Effect argument, but only if you've previously read the effects list (with a effectsObject:Get() loop). You can use that argument to get the name of the effect, although that is by no means a unique identifier. (Lots of different effects have the same names.) It may be good enough for the OP's purposes, though.
Ah, thanks Thurallor, that explains some of the weirdness I was seeing. It seems if multiple effects are removed simultaneously the second (or subsequent) EffectRemoved contains the args.Effect since the args.Index is no longer valid. That makes the whole thing workable.

The code would then look like:

localPlayer=Turbine.Gameplay.LocalPlayer:GetInstan ce();
effectList=localPlayer:GetEffects()

count=0; -- initialize variable
-- define the AddCallback function
function AddCallback(object, event, callback)
if (object[event] == nil) then
object[event] = callback;
else
if (type(object[event]) == "table") then
table.insert(object[event], callback);
else
object[event] = {object[event], callback};
end
end
return callback;
end

EffectRemovedHandler=function(sender,args)

local effectName=""
Turbine.Shell.WriteLine("args.Effect:"..tostring(args.Effect))
if args.Effect==ni then
effectName=effectList:Get(args.Index):GetName()
else
effectName=args.Effect:GetName()
end
if effectName=="Schnelle Erl\195\182sung" then
killcount=6;
do_update()
end
-- uncomment the following line to see the name of every effect as it is removed in case the name is not "Schnelle Erl\195\182sung"
-- Turbine.Shell.WriteLine("effect:"..effectName)
end

AddCallback(effectList,"EffectRemoved",EffectRemovedHandler)

ChatMonitor = Turbine.Chat;
ChatReceived = function(f, args)
local msg = args.Message;

if(args.ChatType == Turbine.ChatType.PlayerCombat) then
if string.find (args.Message,"Verbesserter Gnadenschuss",1,true)~=nil then
killcount=6;
do_update()
else
if killcount==0 then
do_update()
else
if string.find (args.Message,"Verbesserter Schneller Bogen",1,true)~=nil then
killcount=killcount-0.333
do_update()
end
end
end
end
end

AddCallback(Turbine.Chat, "Received", ChatReceived)


Note, the commented line " Turbine.Shell.WriteLine("effect:"..effectName)" can be uncommented to verify what the effect name is in case it is not "Schnelle Erl\195\182sung"

Elliss11
11-23-2015, 04:52 PM
thx ... i will test it