CreatureEvent [TFS 1.1] Random Item Stats

zbizu

Legendary OT User
Joined
Nov 22, 2010
Messages
2,543
Best answers
1
Reaction score
1,442
Location
Poland
Original thread(0.3+): http://otland.net/threads/mod-random-item-stats.130295/
Original author: @Cykotitan

This is an old and great 0.3+ mod(from Jun 1, 2011) rewritten to 1.1, it makes monsters drop upgraded items.
Doesn't work with other upgrade systems.

normal item:
15:19 You see a morning star (Atk:25, Def:11).
It weighs 54.00 oz.
rare:
15:19 You see a [sharpened] morning star (Atk:29, Def:11).
It weighs 54.00 oz.
[atk: +18%]
creaturescripts.xml:
Code:
<event type="login" name="randomstats_register" script="randomstats.lua"/>
   <event type="kill" name="randomstats_loot" script="randomstats.lua"/>
randomstats.lua:
Code:
local rare_popup = true
local rare_text = "*rare*"
local rare_effect = true
local rare_effect_id = CONST_ME_MAGIC_GREEN

local tiers = {
	[1] = {
		prefix = 'rare',
		showattr = true, -- attr prefix will be shown instead
		extra = {0, 0},
		chance = {
			[1] = 10000, -- chance for basic stat
			[2] = 5000 -- chance for second stat
		}
	},

	[2] = {
		prefix = 'epic',
		extra = {7, 20}, -- additional percent bonus
		chance = {
			[1] = 3333,
			[2] = 25000
		}
	},

	[3] = {
		prefix = 'legendary',
		extra = {20, 35},
		chance = {
			[1] = 1000,
			[2] = 100000 -- 2 bonuses always
		}
	},
}

--! attributes
local attr = {
	atk = {
		name = 'atk',
		prefix = 'sharpened',
		percent = {7, 25},
	},
	def = {
		name = 'def',
		prefix = 'fortified',
		percent = {7, 25},
	},
	extradef = {
		name = 'extra def',
		prefix = 'balanced',
		percent = {7, 25},
	},
	arm = {
		name = 'arm',
		prefix = 'flawless',
		percent = {7, 20},
	},
	hitchance = {
		name = 'accuracy',
		prefix = 'accurate',
		percent = {10, 25},
	},
	shootrange = {
		name = 'range',
		prefix = 'powerful',
		percent = {17, 34},
	},
	charges = {
		name = 'charges',
		prefix = 'charged',
		percent = {30, 45},
	},
	duration = {
		name = 'time',
		prefix = 'unique',
		percent = {35, 50},
	},

	--[[ not available in 1.1
	attackSpeed = {},
	extraAttack = {},
	]]
}

local stats = {
	[1] = {ITEM_ATTRIBUTE_ATTACK, attr.atk},
	[2] = {ITEM_ATTRIBUTE_DEFENSE, attr.def},
	[3] = {ITEM_ATTRIBUTE_EXTRADEFENSE, attr.extradef},
	[4] = {ITEM_ATTRIBUTE_ARMOR, attr.arm},
	[5] = {ITEM_ATTRIBUTE_HITCHANCE, attr.hitchance},
	[6] = {ITEM_ATTRIBUTE_SHOOTRANGE, attr.shootrange},
	[7] = {ITEM_ATTRIBUTE_CHARGES, attr.charges},
	[8] = {ITEM_ATTRIBUTE_DURATION, attr.duration},
	-- not available in 1.1
	-- [9] = {ITEM_ATTRIBUTE_ATTACKSPEED, attr.attackSpeed},
	-- [10] = {ITEM_ATTRIBUTE_EXTRAATTACK, attr.extraAttack},
}

function stat_getItemDuration(item)
	local it_id = item:getId()
	local tid = ItemType(it_id):getTransformEquipId()
	if tid > 0 then
		item:transform(tid)
		local vx = item:getAttribute(ITEM_ATTRIBUTE_DURATION)
		item:transform(it_id)
		item:removeAttribute(ITEM_ATTRIBUTE_DURATION)
		return vx
	end
	return 0
end

function loot_attrToVal(item, attr)
	local id = ItemType(item:getId())
	local v = {
		[ITEM_ATTRIBUTE_ATTACK] = id:getAttack(),
		[ITEM_ATTRIBUTE_DEFENSE] = id:getDefense(),
		[ITEM_ATTRIBUTE_EXTRADEFENSE] = id:getExtraDefense(),
		[ITEM_ATTRIBUTE_ARMOR] = id:getArmor(),
		[ITEM_ATTRIBUTE_HITCHANCE] = id:getHitChance(),
		[ITEM_ATTRIBUTE_SHOOTRANGE] = id:getShootRange(),
		[ITEM_ATTRIBUTE_CHARGES] = id:getCharges(),
		[ITEM_ATTRIBUTE_DURATION] = stat_getItemDuration(item),

		-- not available in 1.1
		-- [ITEM_ATTRIBUTE_ATTACKSPEED] = item:getAttackSpeed(),
		-- [ITEM_ATTRIBUTE_EXTRAATTACK] = item:getExtraAttack(),
	}
	return v[attr]
end

function assign_loot_Stat(c)
	local rares = 0
	local h = c:getItemHoldingCount()
	if h > 0 then
		for i = 1, h do
			local available_stats = {}
			local it_u = c:getItem(i - 1)
			local it_id = ItemType(it_u:getId())
			if it_u:isContainer() then
				local crares = assign_loot_Stat(it_u)
				rares = rares + crares
			else
				if not it_id:isStackable() then
					local wp = it_id:getWeaponType()
					if wp > 0 then
						if wp == WEAPON_SHIELD then -- type shield
							table.insert(available_stats, stats[2])
						elseif wp == WEAPON_DISTANCE then -- type bow
							table.insert(available_stats, stats[1])
							table.insert(available_stats, stats[5])
							table.insert(available_stats, stats[6])
							-- not available in 1.1
							-- table.insert(available_stats, stats[9])
						elseif wp == WEAPON_WAND then -- type wand
							table.insert(available_stats, stats[6])
						-- not available in 1.1
						-- table.insert(available_stats, stats[9])
						elseif isInArray({WEAPON_SWORD, WEAPON_CLUB, WEAPON_AXE}, wp) then -- melee weapon

							if it_id:getAttack() > 0 then
								table.insert(available_stats, stats[1])
							end
							
							if it_id:getDefense() > 0 then
								table.insert(available_stats, stats[2])
							end
							
							if it_id:getExtraDefense() ~= 0 then
								table.insert(available_stats, stats[3])
							end
							-- not available in 1.1
							-- table.insert(available_stats, stats[10])
						end
					else -- armors, amulets, runes and rings
						if it_id:getArmor() > 0 then
							table.insert(available_stats, stats[4])
						end

						if it_id:getCharges() > 0 then
							table.insert(available_stats, stats[7])
						end

						local eq_id = it_id:getTransformEquipId()
						if eq_id > 0 then
							table.insert(available_stats, stats[8])
						end
					end
				end
			end

			if #available_stats > 0 then
				-- skips it all if it's empty
				local tier = math.random(1, #tiers)
				if #tiers[tier].chance > 0 then
					local statsStored = 0
					local stats_used = {}
					for stat = 1, #tiers[tier].chance do
						if #available_stats > 0 then
							-- stops if no more stats available
							if stat - 1 == statsStored then
								-- checks when it's time to stop adding stats
								if math.random(1, 100000) <= tiers[tier].chance[stat] then
									statsStored = statsStored + 1

									local selected_stat = math.random(1, #available_stats)
									table.insert(stats_used, available_stats[selected_stat])
									table.remove(available_stats, selected_stat)
								end
							end
						end
					end

					if #stats_used > 0 then
						rares = rares + 1
						local stat_desc = {}
						for stat = 1, #stats_used do
							local v = math.random(
								stats_used[stat][2].percent[1],
								stats_used[stat][2].percent[2]
							) + math.random(
								tiers[tier].extra[1],
								tiers[tier].extra[2]
							)
							local basestat = loot_attrToVal(it_u, stats_used[stat][1])
							it_u:setAttribute(stats_used[stat][1], basestat + math.abs(basestat * v / 100))
							table.insert(stat_desc, '[' .. stats_used[stat][2].name .. ': +' .. v .. '%]')
						end

						if tiers[tier].showattr then
							for stat = 1, #stats_used do
								it_u:setAttribute(ITEM_ATTRIBUTE_NAME, "[" .. stats_used[stat][2].prefix .. "]" .. it_u:getAttribute(ITEM_ATTRIBUTE_NAME))
							end
							it_u:setAttribute(ITEM_ATTRIBUTE_NAME, it_u:getAttribute(ITEM_ATTRIBUTE_NAME) .. " " .. it_id:getName())
						else
							it_u:setAttribute(ITEM_ATTRIBUTE_NAME, tiers[tier].prefix .. " " .. it_id:getName())
						end

						it_u:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, table.concat(stat_desc, "\n"))
					end
				end
			end
		end
	end
	return rares
end

function find_loot_Container(pos)
	local rares = 0
	local c = Tile(pos):getTopDownItem()
	if c ~= nil then
		if c:isContainer() then
			rares = rares + assign_loot_Stat(c)
			if rares > 0 then
				if rare_popup then
					local spectators = Game.getSpectators(pos, false, true, 7, 7, 5, 5)
					for i = 1, #spectators do
						spectators[i]:say(rare_text, TALKTYPE_MONSTER_SAY, false, spectators[i], pos)
					end
				end

				if rare_effect then
					pos:sendMagicEffect(rare_effect_id)
				end
			end
			return true
		end
	end
end

function onKill(player, target, lastHit)
	if (not isSummon(target)) then
		addEvent(find_loot_Container, 2, target:getPosition())
	end
	return true
end

function onLogin(player)
	player:registerEvent("randomstats_loot")
	return true
end
 
Last edited:

strutZ

Australian OT Member {AKA Beastn}
Joined
Nov 16, 2014
Messages
1,341
Best answers
6
Reaction score
477
wow wee!!! Will definitely test this! Thanks!
 

Eldin

Eldin Projects
Joined
Jun 12, 2008
Messages
1,302
Best answers
0
Reaction score
525
Location
Sweden
.............. :eek:

So, I wont be able to use the "Slot" System then?

Kind Regards,
Eldin.
 
OP
zbizu

zbizu

Legendary OT User
Joined
Nov 22, 2010
Messages
2,543
Best answers
1
Reaction score
1,442
Location
Poland
@Eldin
Not yet, but it's possible after some rescripting and messing with strings. I got it to work on 0.4 so I can do it here too.
 
Last edited:

Eldin

Eldin Projects
Joined
Jun 12, 2008
Messages
1,302
Best answers
0
Reaction score
525
Location
Sweden
@zbizu - Great, I would love to use them both on all of my servers, random loot have Always been a Dream without source editing. :)
Keep up the great work mate, the Slot system has as an example been loved on my servers, players spent hours and loads of gold to gain the maximum stats on Rookville.

Kind Regards,
Eldin.
 

Jeffro

Alpha
Joined
Jan 17, 2015
Messages
235
Best answers
0
Reaction score
256
Location
New York
Code:
Lua Script Error: [CreatureScript Interface]
data/creaturescripts/scripts/randomstats.lua:onKill
data/compat.lua:34: attempt to index a nil value
stack traceback:
        [C]: in function '__index'
        data/compat.lua:34: in function 'isSummon'
        data/creaturescripts/scripts/randomstats.lua:229: in function <data/crea
turescripts/scripts/randomstats.lua:227>
Code:
function onKill(player, lastHit, target)

   if (not isSummon(target)) then
     addEvent(find_loot_Container, 2, player, target:getPosition())
   end
return true
end

function onLogin(cid)

    local player = Player(cid)

   player:registerEvent("randomstats_loot")
   return true
end
I'm running this on TFS 1.0.
 

strutZ

Australian OT Member {AKA Beastn}
Joined
Nov 16, 2014
Messages
1,341
Best answers
6
Reaction score
477
Code:
Lua Script Error: [CreatureScript Interface]
data/creaturescripts/scripts/randomstats.lua:onKill
data/compat.lua:34: attempt to index a nil value
stack traceback:
        [C]: in function '__index'
        data/compat.lua:34: in function 'isSummon'
        data/creaturescripts/scripts/randomstats.lua:229: in function <data/crea
turescripts/scripts/randomstats.lua:227>
Code:
function onKill(player, lastHit, target)

   if (not isSummon(target)) then
     addEvent(find_loot_Container, 2, player, target:getPosition())
   end
return true
end

function onLogin(cid)

    local player = Player(cid)

   player:registerEvent("randomstats_loot")
   return true
end
I'm running this on TFS 1.0.
update your compat.lua
 

Evil Hero

Legacy Member
TFS Developer
Joined
Dec 12, 2007
Messages
1,179
Best answers
15
Reaction score
553
Location
Germany
Code:
if (not isSummon(target:getId())) then
isSummon requests cid, not the userdata that's why it gives the error (atleast in 1.0)
 

Jeffro

Alpha
Joined
Jan 17, 2015
Messages
235
Best answers
0
Reaction score
256
Location
New York
Code:
if (not isSummon(target:getId())) then
isSummon requests cid, not the userdata that's why it gives the error (atleast in 1.0)
I appreciate your help. Now 'target' is a nil value and spitting back errors.

[EDIT] I was able to fix the target/nil value issue(I changed target to creature):
Code:
function onKill(Creature, player, lastHit)

   if (not isSummon(Creature)) then
     addEvent(find_loot_Container, 2, player, getCreaturePos())
  end
return true
end
Now, I'm returned with an error further up the line, line 206, indexing a nil value.
Code:
Lua Script Error: [Main Interface]
in a timer event called from:
(Unknown scriptfile)
data/creaturescripts/scripts/randomstats.lua:206: attempt to index a nil value
stack traceback:
        [C]: in function '__index'
        data/creaturescripts/scripts/randomstats.lua:206: in function <data/crea
turescripts/scripts/randomstats.lua:204>
Code:
function find_loot_Container(player, pos)
   local rares = 0
   local c = Tile(pos):getTopDownItem()   Line 206
   if c ~= nil then
     if c:isContainer() then
       rares = rares + assign_loot_Stat(c)
       if rares > 0 then
         if rare_popup then
           local spectators = Game.getSpectators(pos, false, true, 7, 7, 5, 5)
           for _, pid in ipairs(spectators) do
             player:say(rare_text, TALKTYPE_MONSTER_SAY, false, pid, pos)
           end
         end

         if rare_effect then
           pos:sendMagicEffect(rare_effect_id)
         end
       end
       return true
     end
   end
end
 
Last edited:
OP
zbizu

zbizu

Legendary OT User
Joined
Nov 22, 2010
Messages
2,543
Best answers
1
Reaction score
1,442
Location
Poland
Use newest tfs and global, compat lua files
 
Last edited:

7804364

Member
Joined
Mar 6, 2010
Messages
472
Best answers
0
Reaction score
6
can
Use newest tfs and global, compat lua files
Wait for fix before applying this to online servers, I forgot to do something in this script.
im using latest tfs, where can i get recent global and compat because when i use this script it shows up as nil values
 
OP
zbizu

zbizu

Legendary OT User
Joined
Nov 22, 2010
Messages
2,543
Best answers
1
Reaction score
1,442
Location
Poland
From data folder o_O

edit: Updated first post, fixed stat bug

to those who have problem with isSummon
replace:
Code:
if (not isSummon(target)) then
to:
Code:
if (not target:getMaster()) then
or if it still gives error try this:
Code:
if (not Creature(target):getMaster()) then
 
Last edited:

Jeffro

Alpha
Joined
Jan 17, 2015
Messages
235
Best answers
0
Reaction score
256
Location
New York
Thank you zbizu. I'll check it out and report back to you.
 

Jeffro

Alpha
Joined
Jan 17, 2015
Messages
235
Best answers
0
Reaction score
256
Location
New York
can

im using latest tfs, where can i get recent global and compat because when i use this script it shows up as nil values
Here you go. https://github.com/otland/forgottenserver/tree/master/data

[EDIT] Sorry for the double post there.

I've tried what you gave me and still the same issue as before.
Code:
Lua Script Error: [CreatureScript Interface]
data/creaturescripts/scripts/randomstats.lua:onKill
data/creaturescripts/scripts/randomstats.lua:249: attempt to index local 'target
' (a number value)
stack traceback:
        [C]: in function '__index'
        data/creaturescripts/scripts/randomstats.lua:249: in function <data/crea
turescripts/scripts/randomstats.lua:246>
Code:
function onKill(cid, target, player)-246
-247
   if (not Creature(target):getMaster()) then-248
     addEvent(find_loot_Container, 2, player, target:getPosition()) -249
   end
return true
end
[EDIT2]
Now wouldn't this statement be correct to use?
Code:
if (not getCreatureMaster(target)) then
 
Last edited:
OP
zbizu

zbizu

Legendary OT User
Joined
Nov 22, 2010
Messages
2,543
Best answers
1
Reaction score
1,442
Location
Poland
Code:
local target = Creature(target)
if (not target:getMaster()) then
should work now -.-

update to newest tfs and you won't get errors
 

7804364

Member
Joined
Mar 6, 2010
Messages
472
Best answers
0
Reaction score
6
i get a similar error when i login, and it loggs people out instantly so no one can play
 

Jeffro

Alpha
Joined
Jan 17, 2015
Messages
235
Best answers
0
Reaction score
256
Location
New York
i get a similar error when i login, and it loggs people out instantly so no one can play
Replace the whole onLogin function at the bottom with this.
Code:
function onLogin(cid)

local player = Player(cid)

   player:registerEvent("randomstats_loot")
   return true
end
 
OP
zbizu

zbizu

Legendary OT User
Joined
Nov 22, 2010
Messages
2,543
Best answers
1
Reaction score
1,442
Location
Poland
@7804364 @Jeffro
I'm using TFS sources from Feb 14 2015. My script should work on newer versions so I assume you're using older one.
 

Jeffro

Alpha
Joined
Jan 17, 2015
Messages
235
Best answers
0
Reaction score
256
Location
New York
@7804364 @Jeffro
I'm using TFS sources from Feb 14 2015. My script should work on newer versions so I assume you're using older one.
I've gotten rid of all the errors finally. I think you're right. I wanted to at least try to make it work. I'll update today and let you know.
 
Top