Handling buffs by ID as well as name

Runes of Magic/Radiant Arcana (http://www.runesofmagic.com)
Post Reply
Message
Author
dx876234
Posts: 188
Joined: Sat Jul 24, 2010 6:13 am

Handling buffs by ID as well as name

#1 Post by dx876234 » Fri May 22, 2015 5:36 am

Currently the CPawn handles buffs by name only, as rom client also uses buffs without names for event management I had to rewrite some core methods, namely CPawn:getBuff and CPawn:updateBuffs, to accept buffs with no names and to check on these.

I've tried to make these compatible with the current implementation to make a minimal footprint and attached them below in case its suitable to include in standard rombot.

Also, I suggest we include one new address offset 'addresses.pawnCasting_offset - 0x04' which is the ID of the cast being done. I.E. when pawn.Casting is true the address above contains the ID of skill being cast.

best regards
DX

Code: Select all

function CPawn:updateBuffs()
	if not self:hasAddress() then
		self.buffs = {};
		return
	end

	local proc = getProc()
	local BuffSize = 0x54
	local buffStart = memoryReadRepeat("uint", proc, self.Address + addresses.pawnBuffsStart_offset);
	local buffEnd = memoryReadRepeat("uint", proc, self.Address + addresses.pawnBuffsEnd_offset);

	self.Buffs = {} -- clear old values
	if buffStart == nil or buffEnd == nil or buffStart == 0 or buffEnd == 0 then return end
	if (buffEnd - buffStart)/ BuffSize > 50 then -- Something wrong, too many buffs
		return
	end

	for i = buffStart, buffEnd - 4, BuffSize do
		local tmp = {}
		tmp.Id = memoryReadRepeat("int", proc, i + addresses.pawnBuffId_offset);
		tmp.Count = 1
		
		local name = GetIdName(tmp.Id)
		if name ~= nil and name ~= "" then
			tmp.Name, tmp.Count = parseBuffName(name)
		end
		tmp.TimeLeft = memoryReadRepeat("float", proc, i + addresses.pawnBuffTimeLeft_offset);
		tmp.Level = memoryReadRepeat("int", proc, i + addresses.pawnBuffLevel_offset);

		table.insert(self.Buffs,tmp)
	end
end

Code: Select all

function CPawn:getBuff(buffnamesorids, count)
	self:updateBuffs()

	if type(buffnamesorids) == "number" then
		buffnamesorids = {buffnamesorids}
	elseif type(buffnamesorids) == "string" then
		local buffs = {}
		for buffname in string.gmatch(buffnamesorids,"[^,]+") do
			table.insert(buffs, buffname)
		end
		buffnamesorids = buffs
	end
					
	-- for each buff the pawn has
	for i, buff in pairs(self.Buffs) do
		for j,buffname in pairs(buffnamesorids) do
			if (buffname == buff.Name or buffname == buff.Id) and (count == nil or buff.Count >= count) then
				return buff
			end
		end
	end

	return false
end

User avatar
rock5
Posts: 12173
Joined: Tue Jan 05, 2010 3:30 am
Location: Australia

Re: Handling buffs by ID as well as name

#2 Post by rock5 » Fri May 22, 2015 9:14 am

Wow, I'm surprised updateBuffs doesn't save the id. Adding that is fine.

As I understand it, the old updateBuffs assumes an id, with a name of nil or "", is invalid and doesn't add it to player.Buffs. It seems that you have completely bypassed that and now allow all ids to be accepted. I think we can narrow it down a bit. I think if you use an invalid id with GetIdName, it returns nil. I suspect if you use a valid id with no name, such as your noname buffs, then it will return "". Maybe we should still exclude nil values, eg.

Code: Select all

local name = GetIdName(tmp.Id)
if name ~= nil then
    tmp.Name, tmp.Count = parseBuffName(name)
    tmp.TimeLeft = memoryReadRepeat("float", proc, i + addresses.pawnBuffTimeLeft_offset);
    tmp.Level = memoryReadRepeat("int", proc, i + addresses.pawnBuffLevel_offset);

    table.insert(self.Buffs,tmp)
end
In this case we don't need to default Count to 1 because parseBuffName should return "",1 for buffnames of "".



I'm not quite sure what you are doing with GetBuff. GetBuff can already accept a name, a number or a collection as a string eg. ("name1,name2,id3,id4"). It looks like you are putting buffnamesorids into a table first. That might make it a bit more efficient because iterating through a table might be faster than iterating though a string but it should have worked the way it was. All you really needed was to add the id check. eg.

Code: Select all

if buffname == buff.Name or tonumber(buffname) == buff.Id and ( count == nil or buff.Count >= count ) then
I just remembered the buffs compare only names by design. The reason being, sometimes the buff id (and sometimes the skill ids) change with an update but as long as the name remains the same existing scripts will continue to work even though the ids have changed. Making it compare by id would break some things.

We could still do it but it would have to be more specific eg. pseudo code

Code: Select all

if buffname is a number then use GetIdName to get the name
    if the name gotten is not "" then compare the name else compare the id
That way it will still function as it used to and be able to handle updated ids but can also be used with buffs with no names. In that case it will compare by id because it has no name. What do you think.
  • Please consider making a small donation to me to support my continued contributions to the bot and this forum. Thank you. Donate
  • I check all posts before reading PMs. So if you want a fast reply, don't PM me but post a topic instead. PM me for private or personal topics only.
  • How to: copy and paste in micromacro
    ________________________
    Quote:
    • “They say hard work never hurt anybody, but I figure, why take the chance.”
          • Ronald Reagan

dx876234
Posts: 188
Joined: Sat Jul 24, 2010 6:13 am

Re: Handling buffs by ID as well as name

#3 Post by dx876234 » Fri May 22, 2015 11:45 am

If we can simplify the code Im all for it, I didn't know about the differences in GetIdName with unknown or no-name buffs, that's a nice way of differentiating it.

The changes in GetBuff was to make it accept a real table of names/numbers and, to keep compatibility i split the string into a table. This is not really nessesary, just simplified some code for me where I tested for multiple buffs and could make a table instead of string formatting them.

Code: Select all

CPawn:GetBuff{123, "Speed", 45a6}
CPawn:GetBuff("123,Speed,45a6")
I expect using real tables is slightly faster but prolly has no real significance in speed.
A small advantage is that the interpreter will catch any syntax errors for table version,
while the string version will fail in execution. But again, real minor advantage.

The pseudo code example looks like a good way of handling the test :)

best regards
DX

User avatar
rock5
Posts: 12173
Joined: Tue Jan 05, 2010 3:30 am
Location: Australia

Re: Handling buffs by ID as well as name

#4 Post by rock5 » Fri May 22, 2015 12:47 pm

dx876234 wrote:The changes in GetBuff was to make it accept a real table of names/numbers
I didn't think of that. That would be useful for users that would prefer to use tables.

Ok, so in updateBuffs we only need to add the id and change

Code: Select all

if name ~= nil and name ~= "" then
to

Code: Select all

if name ~= nil then
In getBuff we need to add your table code (I modified it a bit) and the logic I mentioned. This is what I came up with.

Code: Select all

function CPawn:updateBuffs()
	if not self:hasAddress() then
		self.buffs = {};
		return
	end

	local proc = getProc()
	local BuffSize = 0x54
	local buffStart = memoryReadRepeat("uint", proc, self.Address + addresses.pawnBuffsStart_offset);
	local buffEnd = memoryReadRepeat("uint", proc, self.Address + addresses.pawnBuffsEnd_offset);

	self.Buffs = {} -- clear old values
	if buffStart == nil or buffEnd == nil or buffStart == 0 or buffEnd == 0 then return end
	if (buffEnd - buffStart)/ BuffSize > 50 then -- Something wrong, too many buffs
		return
	end

	for i = buffStart, buffEnd - 4, BuffSize do
		local tmp = {}
		--yrest(1)
		tmp.Id = memoryReadRepeat("int", proc, i + addresses.pawnBuffId_offset);
		local name = GetIdName(tmp.Id)

		if name ~= nil then
			tmp.Name, tmp.Count = parseBuffName(name)
			tmp.TimeLeft = memoryReadRepeat("float", proc, i + addresses.pawnBuffTimeLeft_offset);
			tmp.Level = memoryReadRepeat("int", proc, i + addresses.pawnBuffLevel_offset);

			table.insert(self.Buffs,tmp)
		end
	end
end

Code: Select all

function CPawn:getBuff(buffnamesorids, count)
	self:updateBuffs()

	if type(buffnamesorids) ~= "table" then
		local buffs = {}
		for buffname in string.gmatch(buffnamesorids,"[^,]+") do
			table.insert(buffs, buffname)
		end
		buffnamesorids = buffs
	end

	-- for each buff the pawn has
	for i, buff in pairs(self.Buffs) do
		-- compare against each 'buffname'
		for j,buffname in pairs(buffnamesorids) do
			if type(tonumber(buffname)) == "number" then
				buffname = tonumber(buffname)
				-- Get name from id
				local tmpbuffname = GetIdName(buffname)
				-- Take of end numbers
				tmpbuffname = parseBuffName(tmpbuffname)
				-- Use only if id has a name
				if tmpbuffname ~= "" then
					buffname = tmpbuffname
				end
			end
			if (buffname == buff.Name or buffname == buff.Id) and ( count == nil or buff.Count >= count ) then
				return buff
			end
		end
	end

	return false
end
Can't be bothered testing it at the moment.
  • Please consider making a small donation to me to support my continued contributions to the bot and this forum. Thank you. Donate
  • I check all posts before reading PMs. So if you want a fast reply, don't PM me but post a topic instead. PM me for private or personal topics only.
  • How to: copy and paste in micromacro
    ________________________
    Quote:
    • “They say hard work never hurt anybody, but I figure, why take the chance.”
          • Ronald Reagan

dx876234
Posts: 188
Joined: Sat Jul 24, 2010 6:13 am

Re: Handling buffs by ID as well as name

#5 Post by dx876234 » Fri May 22, 2015 1:13 pm

I dropped it unmodified into my pawn.lua and it worked as a charm!

Nice work :)

-dx

Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 4 guests