• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

CreatureEvent [TFS 1.1] Random Item Stats

zbizu

Legendary OT User
Joined
Nov 22, 2010
Messages
3,323
Solutions
26
Reaction score
2,690
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:
wow wee!!! Will definitely test this! Thanks!
 
.............. :eek:

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

Kind Regards,
Eldin.
 
@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:
@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.
 
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.
 
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
 
Code:
if (not isSummon(target:getId())) then
isSummon requests cid, not the userdata that's why it gives the error (atleast in 1.0)
 
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:
Use newest tfs and global, compat lua files
 
Last edited:
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
 
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:
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:
Code:
local target = Creature(target)
if (not target:getMaster()) then
should work now -.-

update to newest tfs and you won't get errors
 
i get a similar error when i login, and it loggs people out instantly so no one can play
 
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
 
@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.
 
Back
Top