• 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!
  • New resources must be posted under Resources tab. A discussion thread will be created automatically, you can't open threads manually anymore.

TalkAction Darkhaos' Pet System

Joined
Apr 17, 2008
Messages
1,922
Solutions
1
Reaction score
188
Location
Venezuela
Darkhaos' Pet System 2.0

This pet system brings a lot of features, of course more features will be added with the new versions.


V1.0 Rev: 4.
Update Log:
Code:
[B]V2.0 Rev: 1:[/B]
	[B]Rev: 1 - [01 / 10 / 2011][/B]
		Added posibility to buy pets, and also have more than one pet.
		Fixed some bugs.
[B]V1.0 Rev: 4:[/B]
	[B]Rev: 4 - [08 / 08 / 2011][/B]
		Fixed some bugs and changes were made, lib was updated.
		Added a new script to preven pet attacks owner (petstats.lua).
	[B]Rev: 3 - [07 / 08 / 2011][/B]
		Fixed some bugs, lib was updated.
		Added a new command only for gamemasters.
		Now pets uses mana to attack. Added the new variable 'mana' to the attacks config.
		Now you can't call pet on protection zone.
		Fixed some text erros.
	[B]Rev: 2 - [07 / 08 / 2011][/B]
		Optimized playerDeath.lua
		Optimized playerKill.lua
		Lib was updated, added a missing function.
//TODO
  • Level system. Pet gain level and experience.
  • Pet gain ticks like players (Health, Mana) you can configure time and amount.
  • Pet gain health/mana for each level, like players. You can configure how many.
  • You can configure the pet's exp-rate.
  • Pets can carry items. You can configure how many.
  • You can block items, so pets won't carry these items.
  • You can configure how much cost to revive a pet.
  • You can add how many pets you want.
  • You can configure level required for each pet.
  • You can configure vocations that can use that pet.
  • You can add/remove and configure attacks for each pet.
  • This pet system supports area spells and distance spells.
  • You can configure the level that a pet needs to use an attack.
  • You can confugre the level that player needs to use an attack.
  • You can configure range (Distance attacks) for each attack.
  • For now, attacks works only with normal areas (Circles). Support for wave/beam areas will be added soon.
  • You can move your pet with a command.
  • You can make pets says the text you want.

First of all, add this lib to your server.
Lua:
--Circles
AREA_CIRCLE2X2 = 
{
	{0, 1, 1, 1, 0},
	{1, 1, 1, 1, 1},
	{1, 1, 3, 1, 1},
	{1, 1, 1, 1, 1},
	{0, 1, 1, 1, 0}
}
 
AREA_CIRCLE3X3 = 
{
	{0, 0, 1, 1, 1, 0, 0},
	{0, 1, 1, 1, 1, 1, 0},
	{1, 1, 1, 1, 1, 1, 1},
	{1, 1, 1, 3, 1, 1, 1},
	{1, 1, 1, 1, 1, 1, 1},
	{0, 1, 1, 1, 1, 1, 0},
	{0, 0, 1, 1, 1, 0, 0}
}
 
-- Crosses
AREA_CROSS1X1 =
{
	{0, 1, 0},
	{1, 3, 1},
	{0, 1, 0}
}
 
AREA_CROSS5X5 =
{
	{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
	{0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
	{0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0},
	{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0},
	{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0},
	{1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1},
	{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0},
	{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0},
	{0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0},
	{0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
	{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}
}
 
AREA_CROSS6X6 =
{
	{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0},
	{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
	{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0},
	{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0},
	{1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1},
	{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0},
	{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0},
	{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
	{0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}
}
 
--Squares
AREA_SQUARE1X1 =
{
	{1, 1, 1},
	{1, 3, 1},
	{1, 1, 1}
}
 
-- Walls
AREA_WALLFIELD = {
	{1, 1, 3, 1, 1}
}
 
AREADIAGONAL_WALLFIELD =
{
	{0, 0, 0, 0, 1},
	{0, 0, 0, 1, 1},
	{0, 1, 3, 1, 0},
	{1, 1, 0, 0, 0},
	{1, 0, 0, 0, 0},
}
 
emoteAttack = true
petEmoteAttack = true
refillStatsAtLevel = true
 
TYPE_NEAR = 1
TYPE_DISTANCE = 2
 
petExhaust = 3 --in seconds
petSayExhaust = 3 --in seconds
 
petGainTicks =
{
	health = {func = doCreatureAddHealth, time = 3000, count = 1},
	mana = {func = doCreatureAddMana, time = 3000, count = 2}
}
 
petGainHealth = 30
petGainMana = 30
petExpRate = 1.3
 
carryItems = 10
 
petItemsBase = 1250
petItems = {}
blockedItems = {6132, 2195}
 
for i = 1, carryItems do
	table.insert(petItems, petItemsBase + i)
end
 
petBase = 6120
petStorages =
{
	pet = petBase + 1000,
	level = petBase + 2000,
	exp = petBase + 3000,
	items = petBase + 4000,
	isPet = petBase + 5000,
	isDead = petBase + 6000,
	exhaust = petBase + 7000,
	sayExhaust = petBase + 8000,
	moveExhaust = petBase + 9000,
	health = petBase + 10000,
	mana = petBase + 11000,
	buyed = petBase + 12000
}
 
tables =
{
	pet = {},
	level = {},
	exp = {},
	items = {},
	isDead = {},
	exhaust = {},
	health = {},
	mana = {},
	buyed = {}
}
 
reviveCost = 1000
 
pets =
{
	[1] =
	{
		monster = "orc spearman",
		vocations = {1, 2, 3, 4},
		level = 30,
		attacks =
		{
			[1] = {name = "", level = 30, petLevel = 30, mana = 50, type = TYPE_DISTANCE, range = 10, combat = COMBAT_PHYSICALDAMAGE, effect = CONST_ME_NONE, distEffect = CONST_ANI_SPEAR, damageMin = 0.7, damageMax = 1.0}
		}
	},
	[2] =
	{
		monster = "fire devil",
		vocations = {1, 2},
		level = 60,
		attacks = 
		{
			[1] = {name = "Fire Strike", level = 60, petLevel = 60, mana = 100, type = TYPE_DISTANCE, combat = COMBAT_FIREDAMAGE, effect = CONST_ME_FIREDAMAGE, distEffect = CONST_ANI_FIRE, damageMin = 1.5, damageMax = 2.0}
		}
	},
	[3] =
	{
		monster = "minotaur guard",
		vocations = {3, 4},
		level = 60,
		cost = 50,
		attacks = 
		{
			[1] = {name = "Punch", level = 60, petLevel = 60, mana = 55, type = TYPE_NEAR, combat = COMBAT_PHYSICALDAMAGE, effect = CONST_ME_BLOOD, distEffect = CONST_ANI_NONE, damageMin = 1.5, damageMax = 2.0}
		}
	},
	[4] =
	{
		monster = "water elemental",
		vocations = {2},
		level = 95,
		cost = 15000,
		attacks = 
		{
			[1] = {name = "Ice Bomb", level = 100, petLevel = 100, mana = 250, type = TYPE_DISTANCE, range = 3, combat = COMBAT_ICEDAMAGE, effect = CONST_ME_ICEAREA, distEffect = CONST_ANI_ICE, damageMin = 1.5, damageMax = 2.0, area = AREA_CROSS1X1},
			[2] = {name = "Poison Strike", level = 95, petLevel = 95, mana = 80, type = TYPE_DISTANCE, range = 8, combat = COMBAT_POISONDAMAGE, effect = CONST_ME_GREENRINGS, distEffect = CONST_ANI_POISON, damageMin = 1.2, damageMax = 1.8}	
		}
	},
	[5] =
	{
		monster = "fire elemental",
		vocations = {1},
		level = 95,
		attacks = 
		{
			[1] = {name = "Fire Bomb", level = 100, petLevel = 100, mana = 250, type = TYPE_DISTANCE, range = 3, combat = COMBAT_FIREDAMAGE, effect = CONST_ME_FIRE, distEffect = CONST_ANI_FIRE, damageMin = 1.5, damageMax = 2.0, area = AREA_CROSS1X1},
			[2] = {name = "Fire Strike", level = 95, petLevel = 95, mana = 80, type = TYPE_DISTANCE, range = 3, combat = COMBAT_FIREDAMAGE, effect = CONST_ME_FIREDAMAGE, distEffect = CONST_ANI_FIRE, damageMin = 1.2, damageMax = 1.8}	
		}
	},
	[6] = 
	{
		monster = "orc warlord",
		vocations = {4},
		level = 95,
		attacks = 
		{
			[1] = {name = "Throw Knife", level = 95, petLevel = 95, mana = 80, type = TYPE_DISTANCE, combat = COMBAT_PHYSICALDAMAGE, effect = CONST_ME_BLOOD, distEffect = CONST_ANI_THROWINGKNIFE, damageMin = 1.5, damageMax = 2.0}
		}
	},
	[7] =
	{
		monster = "golem",
		vocations = {3},
		level = 95,
		attacks = 
		{
			[1] = {name = "Throw Stone", level = 95, petLevel = 95, mana = 80, type = TYPE_DISTANCE, combat = COMBAT_PHYSICALDAMAGE, effect = CONST_ME_BLOOD, distEffect = CONST_ANI_LARGEROCK, damageMin = 1.5, damageMax = 2.0}
		}
	},
	[8] =
	{
		monster = "wyrm",
		vocations = {1, 2, 3, 4},
		level = 135
	},
	[9] =
	{
		monster = "dragon lord",
		vocations = {1, 2, 3, 4},
		level = 200,
		attacks = 
		{
			[1] = {name = "Fire Bomb", level = 200, petLevel = 200, mana = 300, type = TYPE_DISTANCE, range = 3, combat = COMBAT_FIREDAMAGE, effect = CONST_ME_FIREAREA, distEffect = CONST_ANI_FIRE, damageMin = 1.5, damageMax = 2.0, area = AREA_CROSS1X1},
			[2] = {name = "Fire Storm", level = 205, petLevel = 220, mana = 700, type = TYPE_NEAR, range = 8, combat = COMBAT_FIREDAMAGE, effect = CONST_ME_FIREAREA, distEffect = CONST_ANI_FIRE, damageMin = 1.8, damageMax = 2.5, area = AREA_CROSS5X5},
			[3] = {name = "Fire Explosion", level = 203, petLevel = 210, mana = 450, type = TYPE_DISTANCE, range = 6, combat = COMBAT_FIREDAMAGE, effect = CONST_ME_EXPLOSIONHIT, distEffect = CONST_ANI_FIRE, damageMin = 1.5, damageMax = 2.1, area = AREA_CIRCLE2X2},
			[4] = {name = "Scratch", level = 201, petLevel = 205, mana = 150, type = TYPE_NEAR, range = 1, combat = COMBAT_PHYSICALDAMAGE, effect = CONST_ME_HITAREA, distEffect = CONST_ANI_FIRE, damageMin = 1.3, damageMax = 1.8},
			[5] = {name = "Fire Strike", level = 203, petLevel = 210, mana = 100, type = TYPE_DISTANCE, range = 3, combat = COMBAT_FIREDAMAGE, effect = CONST_ME_EXPLOSIONAREA, distEffect = CONST_ANI_FIRE, damageMin = 1.8, damageMax = 1.9}	
		}
	}
}
 
for i = 1, #pets do
	table.insert(tables.pet, petStorages.pet + i)
	table.insert(tables.level, petStorages.level + i)
	table.insert(tables.exp, petStorages.exp + i)
	table.insert(tables.items, petStorages.items + i)
	table.insert(tables.isDead, petStorages.isDead + i)
	table.insert(tables.exhaust, petStorages.exhaust + i)
	table.insert(tables.health, petStorages.health + i)
	table.insert(tables.mana, petStorages.mana + i)
	table.insert(tables.buyed, petStorages.buyed + i)
end
 
function gainStat(pid, stat)
 
	if pid and pid > 0 and isMonster(pid) then
		stat.func(pid, stat.count)
	end
	addEvent(gainStat, stat.time, pid, stat)
end
 
function getLevelByExp(exp)
    return math.floor((math.sqrt(3) * math.sqrt(243*(exp+1)^2-48600*(exp+1)+3680000)+27 * (exp+1)-2700)^(1/3)/30^(2/3)-(5*10^(2/3))/(3^(1/3)*(math.sqrt(3)*math.sqrt(243*(exp+1)^2-48600*(exp+1)+3680000)+27*(exp+1)-2700)^(1/3))+2)
end
 
function getPetInfo(pet)
	if isNumber(pet) then
		return pets[pet] or false
	else
		for _, v in pairs(pets) do
			if pet:lower() == v.monster then
				return v
			end
		end
	end
	return false
end
 
function getPetByLevel(cid)
	local level = getPlayerLevel(cid)
	local pet
	for i = 1, #pets do
		v = pets[i]
		if level >= v.level and isInArray(v.vocations, getPlayerVocation(cid)) then
			pet = v
		end
	end
	return (pet ~= nil and pet or false)
end
 
function getAttackFormula(pid, attack)
    return {
			min = ((getPetLevel(pid) * 2) * (1 + attack.damageMin) + getPetLevel(pid)) / 2.5,
			max = ((getPetLevel(pid) * 3) * (1 + attack.damageMax) + getPetLevel(pid)) / 2.5
           }
end
 
function doAttack(pid, target, param)
 
	local pet = getPetInfo(getCreatureName(pid))
	if pet then
		if pet.attacks and pet.attacks[param] then
			local attack = pet.attacks[param]
			if target > 0 and pid ~= getCreatureTarget(getCreatureMaster(pid)) then
				if attack.type and attack.type == TYPE_DISTANCE and getDistanceBetween(getCreaturePosition(pid), getCreaturePosition(target)) > (attack.range or 3) or attack.type == TYPE_NEAR and getDistanceBetween(getCreaturePosition(pid), getCreaturePosition(target)) > (attack.range or 1) then
					return doPlayerSendCancel(getCreatureMaster(pid), "Target is too far.") and doSendMagicEffect(getCreaturePosition(pid), CONST_ME_POFF)
				end
				if not isSightClear(getCreaturePosition(pid), getCreaturePosition(target), true) then
					return doPlayerSendCancel(getCreatureMaster(pid), "There is not enough room.") and doSendMagicEffect(getCreaturePosition(pid), CONST_ME_POFF)
				end
			else
				return doPlayerSendCancel(getCreatureMaster(pid), "Please select a target first.") and doSendMagicEffect(getCreaturePosition(pid), CONST_ME_POFF)
			end
			if getPlayerLevel(getCreatureMaster(pid)) < attack.level then
				return doPlayerSendCancel(getCreatureMaster(pid), "You need level " .. attack.level .. " or higher to use this attack.") and doSendMagicEffect(getCreaturePosition(pid), CONST_ME_POFF)
			end
			if getPetLevel(pid) < attack.petLevel then
				return doPlayerSendCancel(getCreatureMaster(pid), "Your pet needs level " .. attack.petLevel .. " or higher to use this attack.") and doSendMagicEffect(getCreaturePosition(pid), CONST_ME_POFF)
			end
			if attack.mana and getCreatureMana(pid) < attack.mana then
				return doPlayerSendCancel(getCreatureMaster(pid), "Your pet does not have enough mana.") and doSendMagicEffect(getCreaturePosition(pid), CONST_ME_POFF)
			end
			if attack.area then
				doCastAreaAttack(pid, target, attack.area, attack)
			else
				doTargetCombatHealth(pid, target, attack.combat, -getAttackFormula(pid, attack).min, -getAttackFormula(pid, attack).max, attack.effect)
			end
			doCreatureAddMana(pid, -attack.mana)
			if attack.name ~= "" then
 				if emoteAttack then
					doCreatureSay(getCreatureMaster(pid), getCreatureName(pid) .. ", use " .. attack.name .. "!", TALKTYPE_SAY)
				end
				if petEmoteAttack then
					doCreatureSay(pid, attack.name, TALKTYPE_MONSTER)
				end
			end
			return doSendDistanceShoot(getCreaturePosition(pid), (attack.type == TYPE_DISTANCE and getCreaturePosition(target) or getCreaturePosition(pid)), attack.distEffect)
		else
			return doSendMagicEffect(getCreaturePosition(pid), CONST_ME_POFF)
		end
	else
		return doSendMagicEffect(getCreaturePosition(pid), CONST_ME_POFF) and doPlayerSendCancel(getCreatureMaster(pid), "There is a tecnical problem, please contact a gamemaster.")
	end
end
 
function callPet(cid, petId)
 
	for i = 1, #tables do
		for y = 1, #tables[i] do
			if getCreatureStorage(cid, tables[i][y]) < 0 then
				doCreatureSetStorage(cid, tables[i][y], 0)
			end
		end
	end
 
	local pet = getPetInfoById(petId)
	if not pet then
		return doPlayerSendCancel(cid, "Something is wrong.")
	end
 
	if getPlayerLevel(cid) < pet.level then
		return doPlayerSendCancel(cid, "You need level " .. pet.level .. " or higher to call this pet.") 
	end
	if pet.cost and pet.cost > 0 then
		if getCreatureStorage(cid, tables.buyed[petId]) < 1 then
			return doPlayerSendCancel(cid, "You need to buy this pet first.") and doSendMagicEffect(getCreaturePosition(cid), CONST_ME_POFF)
		end
	end
 
	local ret = doCreateMonster(pet.monster, getCreaturePosition(cid))
 
	if getCreatureStorage(cid, tables.level[petId]) < 1 then
		doCreatureSetStorage(cid, tables.level[petId], pet.level)
	end
 
	if getCreatureStorage(cid, tables.exp[petId]) < 1 then
		doCreatureSetStorage(cid, tables.exp[petId], getExperienceForLevel(pet.level))
	end
 
	if getCreatureStorage(cid, tables.level[petId]) < pet.level then
		doCreatureSetStorage(cid, tables.level[petId], pet.level)
	elseif getPlayerLevel(cid) * 3 < getCreatureStorage(cid, tables.level[petId]) then
		doCreatureSetStorage(cid, tables.level[petId], pet.level)
		doCreatureSetStorage(cid, tables.exp[petId], getExperienceForLevel(pet.level))
	end
 
	if getLevelByExp(getCreatureStorage(cid, tables.exp[petId])) < getCreatureStorage(cid, tables.level[petId]) then
		doCreatureSetStorage(cid, tables.exp[petId], getExperienceForLevel(getCreatureStorage(cid, tables.level[petId])))
	end
 
	if getCreatureStorage(cid, tables.health[petId]) < 1 then
		doCreatureSetStorage(cid, tables.health[petId], getCreatureStorage(cid, tables.level[petId]) * petGainHealth)
	end
 
	if getCreatureStorage(cid, tables.mana[petId]) < 1 then
		doCreatureSetStorage(cid, tables.mana[petId], getCreatureStorage(cid, tables.level[petId]) * petGainMana)
	end
 
	doCreatureSetStorage(ret, tables.level[petId], getCreatureStorage(cid, tables.level[petId]))
	doCreatureSetStorage(ret, tables.exp[petId], getCreatureStorage(cid, tables.exp[petId]))
 
	doConvinceCreature(cid, ret)
	doCreatureSetStorage(ret, petStorages.isPet, 1)
	doSendMagicEffect(getCreaturePosition(ret), CONST_ME_TELEPORT)
	setCreatureMaxHealth(ret, getPetLevel(ret) * petGainHealth)
	doCreatureAddHealth(ret, -getCreatureHealth(ret) + getCreatureStorage(cid, tables.health[petId]))
	setCreatureMaxMana(ret, getPetLevel(ret) * petGainMana)
	doCreatureAddMana(ret, -getCreatureMana(ret) + getCreatureStorage(cid, tables.mana[petId]))
	for _, v in pairs(petGainTicks) do
		gainStat(ret, v)
	end
	return doCreatureSay(cid, "Go pet!", TALKTYPE_SAY)
end
 
function doCastAreaAttack(pid, target, area, attack)
	local center = {}
	local areaxx = {}
	center.y = math.floor(#area/2)+1
	for y = 1, #area do
		for x = 1, #area[y] do
			local number = area[y][x]
			if number > 0 then
				center.x = math.floor(table.getn(area[y])/2)+1
				if attack.type == TYPE_DISTANCE then
					table.insert(areaxx, {x = getCreaturePosition(target).x + x - center.x, y = getCreaturePosition(target).y + y - center.y, z = getCreaturePosition(target).z})
				else
					table.insert(areaxx, {x = getCreaturePosition(pid).x + x - center.x, y = getCreaturePosition(pid).y + y - center.y, z = getCreaturePosition(pid).z})
				end				
			end
		end
	end
	for i = 1, #areaxx do
		doAreaCombatHealth(pid, attack.combat, areaxx[i], 0, -getAttackFormula(pid, attack).min, -getAttackFormula(pid, attack).max, attack.effect)
	end
end
 
function isPet(pid)
	local id = getPetIdByName(getCreatureName(pid))
	return getCreatureStorage(pid, petStorages.isPet) > 0 and true or false
end
 
function doPetAddExperience(pid, exp)
 
	local id = getPetIdByName(getCreatureName(pid))
	exp = math.ceil(exp)
	doCreatureSetStorage(getCreatureMaster(pid), tables.exp[id], getPetExperience(pid) + exp)
	doSendAnimatedText(getCreaturePosition(pid), exp, getConfigValue("gainExperienceColor"))
	return true
end
 
function getPetExperience(pid)
	local id = getPetIdByName(getCreatureName(pid))
	return getCreatureStorage(getCreatureMaster(pid), tables.exp[id])
end
 
function getPetLevel(pid)
	local id = getPetIdByName(getCreatureName(pid))
	return getCreatureStorage(getCreatureMaster(pid), tables.level[id])
end
 
function doPetSetLevel(pid, level)
 
	local id = getPetIdByName(getCreatureName(pid))
	doCreatureSetStorage(getCreatureMaster(pid), tables.level[id], level)
	setCreatureMaxHealth(pid, getPetLevel(pid) * petGainHealth)
	setCreatureMaxMana(pid, getPetLevel(pid) * petGainMana)
	return true
end
 
function getPlayerPet(cid)
	local pet
	if #getCreatureSummons(cid) < 1 then
		pet = false
	end
 
	for _, it in ipairs(getCreatureSummons(cid)) do
		if isPet(it) then
			pet = it
			break
		end
	end
	return pet
end
 
function getPetIdByName(name)
 
	local id = 0
	for k, it in pairs(pets) do
		if it.monster:lower() == name:lower() then
			id = k
			break
		end
	end
	return (id > 0 and id or false)
end
 
function getTopItem(p)
	p.stackpos = 0
	local v = getThingFromPos(p)
	repeat
		p.stackpos = p.stackpos + 1
		v = getThingFromPos(p)
	until v.itemid == 0
	p.stackpos = p.stackpos - 1
	return getThingFromPos(p)
end
 
function getPetInfoById(pet)
	for k, v in pairs(pets) do
		if k == pet then
			return v
		end
	end
	return false
end

if not getCreatureStorage then
	getCreatureStorage = getPlayerStorageValue
	doCreatureSetStorage = doPlayerSetStorageValue
end

Now create a file on data/talkactions/scripts called pet.lua and paste:
Lua:
local l =
{
	["north"] = NORTH,
	["east"] = EAST,
	["south"] = SOUTH,
	["west"] = WEST,
	["southwest"] = SOUTHWEST,
	["southeast"] = SOUTHEAST,
	["northwest"] = NORTHWEST,
	["northeast"] = NORTHEAST
}
 
function onSay(cid, words, param, channel)
	if(param == '') then
		doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command param required.")
		return true
	end
 
	if param:lower() == "info" then
		local pet = getPlayerPet(cid)
		if pet then
			local pet_ = getPetInfo(getCreatureName(pet))
			local attacks = ""
			if pet_.attacks and #pet_.attacks > 0 then
				for i = 1, #pet_.attacks do
					attacks = attacks .. "Attack ID: " .. i .. "\n" .. (pet_.attacks[i].name ~= "" and "    Name: " .. pet_.attacks[i].name .. "\n" or "") .. "    Level: " .. pet_.attacks[i].level .. "\n    Pet level: " .. pet_.attacks[i].petLevel .. "\n    Pet mana: " .. pet_.attacks[i].mana .. "\n"
				end
			end
			return doShowTextDialog(cid, 1948, "Here is your pet info:\n" .. 
				"\nName: " .. getCreatureName(pet) ..
				"\nHealth: " .. getCreatureHealth(pet) .. "-" .. getCreatureMaxHealth(pet) ..
				"\nMana: " .. getCreatureMana(pet) .. "-" .. getCreatureMaxMana(pet) ..
				"\nLevel: " .. getPetLevel(pet) ..
				"\nExperience: " .. getPetExperience(pet) ..
				"\n-----Attacks-----\n" .. (attacks ~= "" and attacks or "No attacks"))
		else
			return doPlayerSendCancel(cid, "Please call your pet first.")
		end
	elseif param:lower() == "take" then
		local pet = getPlayerPet(cid)
		if not pet then
			return doPlayerSendCancel(cid, "Please call your pet first.")
		end
 
		local slot = 0
		for i = 1, carryItems do
			if getCreatureStorage(cid, petItems[i]) < 1 then
				slot = i
				break
			end
		end
 
		if slot == 0 then
			return doPlayerSendCancel(cid, "You only can carry " .. carryItems .. " items.")
		end
 
		local item = getTopItem(getCreaturePosition(pet))
		if getItemWeightById(item.itemid, 1) and getItemWeightById(item.itemid, 1) < 1 then
			return doPlayerSendCancel(cid, "There is no item to carry.")
		end
 
		if isInArray(blockedItems, item.itemid) then
			return doPlayerSendCancel(cid, "You cannot carry this item.")
		end
		doCreatureSetStorage(cid, petItems[slot], item.itemid * 1000 + (item.type > 0 and item.type or 1))
		doRemoveItem(item.uid)
		doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "You take an item.")
		return true
	elseif param:lower() == "items" then
		local list = "Here is the list of items:\n"
		for i = 1, carryItems do
			if getCreatureStorage(cid, petItems[i]) < 1 then
				list = list .. "\n" .. i .. ". Empty."
			else
				local thing = getCreatureStorage(cid, petItems[i])
				local item = math.floor(thing / 1000)
				local count = thing - item * 1000
				list = list .. "\n" .. i .. ". x" .. count .. " " .. getItemNameById(item) .. "."
			end
		end
 
		return doShowTextDialog(cid, 1948, list)
	elseif param:lower() == "back" then
		local pet = getPlayerPet(cid)
		if not pet then
			return doPlayerSendCancel(cid, "You don't have any pet released.")
		end
		local petId = getPetIdByName(getCreatureName(pet))
		doCreatureSetStorage(cid, tables.health[petId], getCreatureHealth(pet))
		doCreatureSetStorage(cid, tables.mana[petId], getCreatureMana(pet))		
		doSendMagicEffect(getCreaturePosition(pet), CONST_ME_POFF)
		doRemoveCreature(pet)
		return doCreatureSay(cid, "It's enough!", TALKTYPE_SAY)
	else
		param = string.explode(param, ":")
		if param[1]:lower() == "call" then
			if not param[2] or param[2] == "" then
				return doPlayerSendCancel(cid, "No pet specified.")
			end
			if getTilePzInfo(getCreaturePosition(cid)) then
				return doPlayerSendCancel(cid, "You cannot call a pet in protection zone.")
			end
			if #getCreatureSummons(cid) > 0 then
				return doPlayerSendCancel(cid, "You only can call a pet.")
			end

			local pet = getPetIdByName(param[2])
			if pet then
				if getCreatureStorage(cid, tables.isDead[pet]) > 0 then
					return doPlayerSendCancel(cid, "You need to revive this pet first.")
				end

				return callPet(cid, pet)
			else
				return doPlayerSendCancel(cid, "There is not pet with that name.")
			end
		elseif param[1]:lower() == "attack" then
			local pet = getPlayerPet(cid)
			if not pet then
				return doPlayerSendCancel(cid, "Please call your pet first.")
			end
			if not param[2] or not isNumber(param[2]) or tonumber(param[2]) < 1 then
				return doPlayerSendCancel(cid, "No attack index specified.")
			end
 
			if exhaustion.get(cid, petStorages.exhaust) then
				return doPlayerSendCancel(cid, "You are exhausted.") and doSendMagicEffect(getCreaturePosition(getCreatureSummons(cid)[1]), CONST_ME_POFF)
			end
 
			doAttack(pet, getCreatureTarget(cid), tonumber(param[2]))
			exhaustion.set(cid, petStorages.exhaust, petExhaust)
		elseif param[1]:lower() == "give" then
			local pet = getPlayerPet(cid)
			if not pet then
				return doPlayerSendCancel(cid, "Please call your pet first.")
			end
			if not param[2] or not isNumber(param[2]) or tonumber(param[2]) < 1 then
				return doPlayerSendCancel(cid, "No slot specified.")
			end
 
			if getCreatureStorage(cid, petItems[tonumber(param[2])]) > 0 then
				local thing = getCreatureStorage(cid, petItems[tonumber(param[2])])
				local item = math.floor(thing/1000)
				local count = thing-item*1000
				doPlayerAddItem(cid, item, count)
				doCreatureSetStorage(cid, petItems[tonumber(param[2])], 0)
				doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "You get an item.")
			else
				return doPlayerSendCancel(cid, "This slot is empty.")
			end
		elseif param[1]:lower() == "say" then
			local pet = getPlayerPet(cid)
			if not pet then
				return doPlayerSendCancel(cid, "Please call your pet first.")
			end
			if not param[2] or param[2] == "" then
				return doPlayerSendCancel(cid, "Command param required.")
			end
 
			if exhaustion.get(cid, petStorages.sayExhaust) then
				return doPlayerSendCancel(cid, "You are exhausted.") and doSendMagicEffect(getCreaturePosition(getCreatureSummons(cid)[1]), CONST_ME_POFF)
			end
 
			doCreatureSay(pet, param[2], TALKTYPE_MONSTER)
			exhaustion.set(cid, petStorages.sayExhaust, petExhaust)
		elseif param[1]:lower() == "move" then
			local pet = getPlayerPet(cid)
			if not pet then
				return doPlayerSendCancel(cid, "Please call your pet first.")
			end
			if not param[2] or param[2] == "" then
				return doPlayerSendCancel(cid, "Command param required.")
			end
 
			if exhaustion.get(cid, petStorages.moveExhaust) then
				return doPlayerSendCancel(cid, "You are exhausted.") and doSendMagicEffect(getCreaturePosition(getCreatureSummons(cid)[1]), CONST_ME_POFF)
			end
 
			local dir
			if l[param[2]:lower()] then
				dir = l[param[2]:lower()]
				local toPos = getPosByDir(getCreaturePosition(pet), dir, 1)
				local ret = queryTileAddThing(pet, toPos)
				if ret == RETURNVALUE_NOERROR then
					doMoveCreature(pet, dir)
					exhaustion.set(cid, petStorages.moveExhaust, petExhaust)
				else
					return doPlayerSendCancel(cid, "There is not enough room.") and doSendMagicEffect(getCreaturePosition(pet), CONST_ME_POFF)
				end
			else
				return doPlayerSendCancel(cid, "No direction specified.")
			end
		elseif isInArray({"mana", "health"}, param[1]:lower()) then
			if getPlayerGroupId(cid) < 3 then
				return false
			end
			local pet = getPlayerPet(cid)
			if not pet then
				return doPlayerSendCancel(cid, "Please call your pet first.")
			end
			if not param[2] or not isNumber(param[2]) then
				return doPlayerSendCancel(cid, "Command param required.")
			end
 
			if param[1]:lower() == "health" then
				doCreatureAddHealth(pet, tonumber(param[2]))
			elseif param[1]:lower() == "mana" then
				doCreatureAddMana(pet, tonumber(param[2]))
			end
			return doSendMagicEffect(getCreaturePosition(pet), (param[1]:lower() == "health" and CONST_ME_MAGIC_RED or CONST_ME_MAGIC_BLUE))
		end
	end
	return true
end

Paste this at talkactions.xml:
XML:
	<talkaction words="!pet" event="script" value="pet.lua"/>

Create a fil called petkill.lua at data/creaturescripts/scripts and paste:
Lua:
function onKill(cid, target, lastHit)
 
	if not isMonster(target) or getConfigValue("rateExperience") < 0.1 or getMonsterInfo(getCreatureName(target)) and getMonsterInfo(getCreatureName(target)).experience < 1 then return true end
 
	local pet = getPlayerPet(cid)
	if pet then
		doPetAddExperience(pet, getMonsterInfo(getCreatureName(target)).experience * petExpRate)
		if getLevelByExp(getPetExperience(pet)) > getPetLevel(pet) then
			doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE, "Your pet advanced from level " .. getPetLevel(pet) .. " to level " .. getLevelByExp(getPetExperience(pet)) .. ".")
			doPetSetLevel(pet, getLevelByExp(getPetExperience(pet)))
			if refillStatsAtLevel then
				doCreatureAddHealth(pet, getCreatureMaxHealth(pet) - getCreatureHealth(pet))
				doCreatureAddMana(pet, getCreatureMaxMana(pet) - getCreatureMana(pet))
			end
		end
	end
	return true
end

Create a file called petdeath.lua at data/creaturescripts/scripts and paste:
Lua:
function onDeath(cid, corpse, deathList)
 
	if not isPet(cid) then return true end
 
	local id = getPetIdByName(getCreatureName(cid))
	doCreatureSetStorage(getCreatureMaster(cid), tables.isDead[id], 1)
	doPlayerSendTextMessage(getCreatureMaster(cid), MESSAGE_STATUS_CONSOLE_BLUE, "Your pet is dead.")
	return true
end

Create a file called petstats.lua at data/creaturescripts/scripts and paste:
Lua:
function onStatsChange(cid, attacker, type, combat, value)
 
	if getPlayerPet(cid) and getPlayerPet(cid) == attacker then
		return false
	end
	return true
end

Add this to your creaturescripts/scripts/login.lua:
Lua:
	registerCreatureEvent(cid, "petKill")
	registerCreatureEvent(cid, "petDeath")
	registerCreatureEvent(cid, "petStats")

Now paste this at creaturescripts.xml:
XML:
	<event type="kill" name="petKill" event="script" value="petkill.lua"/>
	<event type="death" name="petDeath" event="script" value="petdeath.lua"/>
	<event type="statschange" name="petStats" event="script" value="petstats.lua"/>

You need to paste this variable on every monster file that you use as pet, after </flags>.
XML:
<script>
	<event name="petDeath"/>
</script>

This is the script to revive the pet via npc:
Lua:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)
local talkState = {}
 
function onCreatureAppear(cid)                          npcHandler:onCreatureAppear(cid) end
function onCreatureDisappear(cid)                       npcHandler:onCreatureDisappear(cid) end
function onCreatureSay(cid, type, msg)                  npcHandler:onCreatureSay(cid, type, msg) end
function onThink()                                      npcHandler:onThink() end

	local dead = 0
	local pet
function creatureSayCallback(cid, type, msg)

	if(not npcHandler:isFocused(cid)) then
		return false
	end
	local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_PRIVATE and 0 or cid
	if msgcontains(msg, 'offer') then
		local b = false
		local c = 0
		local response = "Here %s the following %s that you can buy:"
		for _, it in pairs(pets) do
			if it.cost and it.cost > 0 then
				c = c + 1
				b = true
				local voc, sep = "", ", "
				for i = 1, #it.vocations do
					if i == #it.vocations - 1 then
						sep = " and "
					elseif i == #it.vocations then
						sep = ""
					end
					voc = voc .. getVocationInfo(it.vocations[i]).name .. sep
				end
				response = response .. "\n    Name: " .. it.monster .. ", Cost: " .. it.cost .. ", Level: " .. it.level .. ", Vocations: " .. voc .. "."
			end
		end
		response = string.format(response, (c > 1 and "are" or "is"), (c > 1 and "pets" or "pet"))
		npcHandler:say(response, cid)
		
		
	elseif msgcontains(msg, 'revive') then

		for i = 1, #pets do
			if getCreatureStorage(cid, tables.isDead[i]) > 0 then
				dead = dead + 1
			end
		end
		if dead > 0 then
			npcHandler:say('Do you want to revive ' .. dead .. ' of your pets for ' .. reviveCost * dead .. ' gold coins?.', cid)
			talkState[talkUser] = 1
		else
			npcHandler:say('Any of your pet is dead.', cid)
			talkState[talkUser] = 0
		end
	elseif msgcontains(msg, 'buy') then
		npcHandler:say('Please tell me the name of the pet that you can buy.', cid)
		talkState[talkUser] = 2
	elseif msgcontains(msg, 'yes') and talkState[talkUser] == 1 then
		if doPlayerRemoveMoney(cid, reviveCost * dead) then
			for i = 1, #tables.isDead do
				doCreatureSetStorage(cid, tables.isDead[i], 0)
			end
			npcHandler:say('Your pets has been revived.', cid)
			talkState[talkUser] = 0
		else
			npcHandler:say('You do not have enough money.', cid)
			talkState[talkUser] = 0
		end
	elseif talkState[talkUser] == 2 then
		pet = getPetIdByName(msg)
		if not pet then
			npcHandler:say('There is not any pet with that name.', cid)
			talkState[talkUser] = 0
			return true
		end

		if getCreatureStorage(cid, tables.buyed[pet]) > 0 then
			npcHandler:say('You already bought this pet.', cid)
			talkState[talkUser] = 0
			return true
		end

		if pets[pet].cost and pets[pet].cost > 0 then
			npcHandler:say('Do you want to buy ' .. pets[pet].monster .. ' [' .. pets[pet].level .. '] for ' .. pets[pet].cost .. ' gold coins?', cid)
			talkState[talkUser] = 3
		else
			npcHandler:say('This pet is not buyable, please tell me the name of the pet that you can buy.', cid)
			talkState[talkUser] = 2
		end
	elseif msgcontains(msg, 'yes') and talkState[talkUser] == 3 then
		if doPlayerRemoveMoney(cid, pets[pet].cost) then
			doCreatureSetStorage(cid, tables.buyed[pet], 1)
			npcHandler:say('Congratulations! you bought ' .. pets[pet].monster .. ' [' .. pets[pet].level .. '].', cid)
			talkState[talkUser] = 0
		else
			npcHandler:say('You do not have enough money to buy this pet.', cid)
			talkState[talkUser] = 0
		end
	end
	return true
end
 
npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())

Monster should have <flag convinceable="1"/>

This is how you configure a new pet (Water Elemental used as example, only one attack used as example):
Lua:
[4] = --Id, should be in order
	{
		monster = "water elemental", --The monster name.
		vocations = {2}, --Vocations that can use this pet.
		level = 95, --Level required to use this pet.
		[cost, = 15000] --you can declare it or not. If you declare it and cost > 0 then pet will be buyable
		attacks = 
		{
			[1] = 
			{
				name = "Ice Bomb", --Attack name, pet will say the name when you use the attack.
				level = 100, --Level that player needs to use the attack.
				petLevel = 100, --Level that pets needs to use the attack.
				mana = 200, --Mana that the pets needs to use the attack.
				type = TYPE_DISTANCE, --(TYPE_NEAR/TYPE_DISTANCE) _NEAR = Used for melee attacks. _NEAR = Used for distance attacks. (Declare this correct or you'll get some bugs.)
				range = 3, --Attack range, used only for distance attacks.
				combat = COMBAT_ICEDAMAGE, --Damage type.
				effect = CONST_ME_ICEAREA, --Floor effect.
				distEffect = CONST_ANI_ICE, --Distance effect.
				damageMin = 1.5, --Damage min.
				damageMax = 2.0, --Damage max.
				area = AREA_CROSS1X1 --Area for the attack, if you want an attack without area, remove this param.
			}
		}
	},

The pet system commands are:
  • !pet call:pet_name // Call pet.
  • !pet back // Call pet back.
  • !pet info // It shows your pet info (Name, health, mana, level, experience and attacks)
  • !pet take // It takes the item above the pet.
  • !pet items // It shows the items you carry.
  • !pet attack:index // Attack with your pet. Ex: !pet attack:2
  • !pet give:index // Get a carried item. Ex: !pet give:4
  • !pet say:text // Makes your pet says something. Ex: !pet say:Hello World
  • !pet move:direction //Makes your moves. Ex: !pet move:northeast
  • !pet mana/health:amount // Add/remove mana/health to your pet (Only for gamemasters)


NOTE: I used tibia creatures, remember that these creatures already has attacks, you can create new creatures to use it as pet.

Any bug you find, post here.
Enjoy.
 
Last edited:
Nice. Nice. I will for sure use in a future server I create xD

Edit this though.
Lua:
			return doPlayerSendCancel(cid, "You need to revive your pet firsts.")

to

Lua:
			return doPlayerSendCancel(cid, "You need to revive your pet first.")
 
Nice. Nice. I will for sure use in a future server I create xD

Edit this though.
Lua:
			return doPlayerSendCancel(cid, "You need to revive your pet firsts.")

to

Lua:
			return doPlayerSendCancel(cid, "You need to revive your pet first.")

Thanks
 
The system it's self is awesome, but i don't like how it got like.. 10 commands, a player wud hotkey them and just forget about his spells :( You shud add a pet channel or something, still pet channel wud be lame, i dunno what suits pets better than !talkactions..

EDIT: It's still awesome tho, works for 0.3.6?

ANOTHER EDIT: Npc.xml plz, a bug, i say !pet call, tells me you do not have enough level to call a pet, i maxed my self to 717k, still tells me the same, plus how do i buy a pet lol?
 
Last edited:
Tittle says all.

This pet system brings a lot of features, of course more features will be added with the new versions.

//TODO
  • Level system. Pet gain level and experience.
  • Pet gain ticks like players (Health, Mana) you can configure time and amount.
  • Pet gain health/mana for each level, like players. You can configure how many.
  • You can configure the pet's exp-rate.
  • Pets can carry items. You can configure how many.
  • You can block items, so pets won't carry these items.
  • You can configure how much cost to revive a pet.
  • You can add how many pets you want.
  • You can configure level required for each pet.
  • You can configure vocations that can use that pet.
  • You can add/remove and configure attacks for each pet.
  • This pet system supports area spells and distance spells.
  • You can configure the level that a pet needs to use an attack.
  • You can confugre the level that player needs to use an attack.
  • You can configure range (Distance attacks) for each attack.
  • For now, attacks works only with normal areas (Circles). Support for wave/beam areas will be added soon.
  • You can move your pet with a command.
  • You can make pets says the text you want.

First of all, add this lib to your server.
Lua:
--Circles
AREA_CIRCLE2X2 = 
{
	{0, 1, 1, 1, 0},
	{1, 1, 1, 1, 1},
	{1, 1, 3, 1, 1},
	{1, 1, 1, 1, 1},
	{0, 1, 1, 1, 0}
}

AREA_CIRCLE3X3 = 
{
	{0, 0, 1, 1, 1, 0, 0},
	{0, 1, 1, 1, 1, 1, 0},
	{1, 1, 1, 1, 1, 1, 1},
	{1, 1, 1, 3, 1, 1, 1},
	{1, 1, 1, 1, 1, 1, 1},
	{0, 1, 1, 1, 1, 1, 0},
	{0, 0, 1, 1, 1, 0, 0}
}

-- Crosses
AREA_CROSS1X1 =
{
	{0, 1, 0},
	{1, 3, 1},
	{0, 1, 0}
}

AREA_CROSS5X5 =
{
	{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
	{0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
	{0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0},
	{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0},
	{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0},
	{1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1},
	{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0},
	{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0},
	{0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0},
	{0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
	{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}
}

AREA_CROSS6X6 =
{
	{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0},
	{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
	{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0},
	{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0},
	{1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1},
	{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0},
	{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0},
	{0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
	{0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}
}

--Squares
AREA_SQUARE1X1 =
{
	{1, 1, 1},
	{1, 3, 1},
	{1, 1, 1}
}

-- Walls
AREA_WALLFIELD = {
	{1, 1, 3, 1, 1}
}

AREADIAGONAL_WALLFIELD =
{
	{0, 0, 0, 0, 1},
	{0, 0, 0, 1, 1},
	{0, 1, 3, 1, 0},
	{1, 1, 0, 0, 0},
	{1, 0, 0, 0, 0},
}

TYPE_NEAR = 1
TYPE_DISTANCE = 2

petExhaust = 3 --in seconds
petSayExhaust = 3 --in seconds

petGainTicks =
{
	health = {func = doCreatureAddHealth, time = 3000, count = 1},
	mana = {func = doCreatureAddMana, time = 3000, count = 2}
}

petGainHealth = 30
petGainMana = 30
petExpRate = 1.3

carryItems = 10

petItemsBase = 1250
petItems = {}
blockedItems = {6132, 2195}

for i = 1, carryItems do
	table.insert(petItems, petItemsBase + i)
end

petBase = 61200
petStorages =
{
	pet = petBase + 1,
	level = petBase + 2,
	exp = petBase + 3,
	items = petBase + 4,
	isPet = petBase + 5,
	isDead = petBase + 6,
	exhaust = petBase + 7,
	sayExhaust = petBase + 8,
	moveExhaust = petBase + 9
}

reviveCost = 1000

pets =
{
	[1] =
	{
		monster = "orc spearman",
		vocations = {1, 2, 3, 4},
		level = 30,
		attacks =
		{
			[1] = {name = "", level = 30, petLevel = 30, type = TYPE_DISTANCE, range = 10, combat = COMBAT_PHYSICALDAMAGE, effect = CONST_ME_NONE, distEffect = CONST_ANI_SPEAR, damageMin = 0.7, damageMax = 1.0}
		}
	},
	[2] =
	{
		monster = "fire devil",
		vocations = {1, 2},
		level = 60,
		attacks = 
		{
			[1] = {name = "Fire Strike", level = 60, petLevel = 60, type = TYPE_DISTANCE, combat = COMBAT_FIREDAMAGE, effect = CONST_ME_FIREDAMAGE, distEffect = CONST_ANI_FIRE, damageMin = 1.5, damageMax = 2.0}
		}
	},
	[3] =
	{
		monster = "minotaur guard",
		vocations = {3, 4},
		level = 60,
		attacks = 
		{
			[1] = {name = "Punch", level = 60, petLevel = 60, type = TYPE_NEAR, combat = COMBAT_PHYSICALDAMAGE, effect = CONST_ME_BLOOD, distEffect = CONST_ANI_NONE, damageMin = 1.5, damageMax = 2.0}
		}
	},
	[4] =
	{
		monster = "water elemental",
		vocations = {2},
		level = 95,
		attacks = 
		{
			[1] = {name = "Ice Bomb", level = 100, petLevel = 100, type = TYPE_DISTANCE, range = 3, combat = COMBAT_ICEDAMAGE, effect = CONST_ME_ICEAREA, distEffect = CONST_ANI_ICE, damageMin = 1.5, damageMax = 2.0, area = AREA_CROSS1X1},
			[2] = {name = "Poison Strike", level = 95, petLevel = 95, type = TYPE_DISTANCE, range = 8, combat = COMBAT_EARTHDAMAGE, effect = CONST_ME_GREENRINGS, distEffect = CONST_ANI_POISON, damageMin = 1.2, damageMax = 1.8}	
		}
	},
	[5] =
	{
		monster = "fire elemental",
		vocations = {1},
		level = 95,
		attacks = 
		{
			[1] = {name = "Fire Bomb", level = 100, petLevel = 100, type = TYPE_DISTANCE, range = 3, combat = COMBAT_FIREDAMAGE, effect = CONST_ME_FIREDAMAGE, distEffect = CONST_ANI_FIRE, damageMin = 1.5, damageMax = 2.0, area = {{0, 0, 1, 0, 0}, {0, 1, 3, 1, 0}, {0, 0, 1, 0, 0}}},
			[2] = {name = "Fire Strike", level = 95, petLevel = 95, type = TYPE_DISTANCE, range = 3, combat = COMBAT_FIREDAMAGE, effect = CONST_ME_FIREDAMAGE, distEffect = CONST_ANI_FIRE, damageMin = 1.2, damageMax = 1.8}	
		}
	},
	[6] = 
	{
		monster = "orc warlord",
		vocations = {4},
		level = 95,
		attacks = 
		{
			[1] = {name = "Throw Knife", level = 95, petLevel = 95, type = TYPE_DISTANCE, combat = COMBAT_PHYSICALDAMAGE, effect = CONST_ME_BLOOD, distEffect = CONST_ANI_THROWINGKNIFE, damageMin = 1.5, damageMax = 2.0}
		}
	},
	[7] =
	{
		monster = "golem",
		vocations = {3},
		level = 95,
		attacks = 
		{
			[1] = {name = "Throw Stone", level = 95, petLevel = 95, type = TYPE_DISTANCE, combat = COMBAT_PHYSICALDAMAGE, effect = CONST_ME_BLOOD, distEffect = CONST_ANI_LARGEROCK, damageMin = 1.5, damageMax = 2.0}
		}
	},
	[8] =
	{
		monster = "wyrm",
		vocations = {1, 2, 3, 4},
		level = 135
	},
	[9] =
	{
		monster = "dragon lord",
		vocations = {1, 2, 3, 4},
		level = 200,
		attacks = 
		{
			[1] = {name = "Fire Bomb", level = 200, petLevel = 200, type = TYPE_DISTANCE, range = 3, combat = COMBAT_FIREDAMAGE, effect = CONST_ME_FIREAREA, distEffect = CONST_ANI_FIRE, damageMin = 1.5, damageMax = 2.0, area = AREA_CROSS1X1},
			[2] = {name = "Fire Explosion", level = 205, petLevel = 220, type = TYPE_NEAR, range = 8, combat = COMBAT_ICEDAMAGE, effect = CONST_ME_FIREAREA, distEffect = CONST_ANI_FIRE, damageMin = 1.8, damageMax = 2.5, area = AREA_CROSS5X5}	
		}
	}
}

function gainStat(pid, stat)

	if pid and pid > 0 and isMonster(pid) then
		if not getTilePzInfo(getCreaturePosition(pid)) then
			stat.func(pid, stat.count)
		end
	end
	addEvent(gainStat, stat.time, pid, stat)
end

function getLevelByExp(exp)
    return math.floor((math.sqrt(3) * math.sqrt(243*(exp+1)^2-48600*(exp+1)+3680000)+27 * (exp+1)-2700)^(1/3)/30^(2/3)-(5*10^(2/3))/(3^(1/3)*(math.sqrt(3)*math.sqrt(243*(exp+1)^2-48600*(exp+1)+3680000)+27*(exp+1)-2700)^(1/3))+2)
end

function getPetInfo(pet)
	if isNumber(pet) then
		return pets[pet] or false
	else
		for _, v in pairs(pets) do
			if pet:lower() == v.monster then
				return v
			end
		end
	end
	return false
end

function getPetByLevel(cid)
	local level = getPlayerLevel(cid)
	local pet
	for i = 1, #pets do
		v = pets[i]
		if level >= v.level and isInArray(v.vocations, getPlayerVocation(cid)) then
			pet = v
		end
	end
	return (pet ~= nil and pet or false)
end

function getAttackFormula(pid, attack)
    return {
			min = ((getPetLevel(pid) * 2) * (1 + attack.damageMin) + getPetLevel(pid)) / 2.5,
			max = ((getPetLevel(pid) * 3) * (1 + attack.damageMax) + getPetLevel(pid)) / 2.5
           }
end

function doAttack(pid, target, param)

	local pet = getPetInfo(getCreatureName(pid))
	if pet then
		if pet.attacks and pet.attacks[param] then
			local attack = pet.attacks[param]
			if target > 0 and attack.type and attack.type == TYPE_DISTANCE and getDistanceBetween(getCreaturePosition(pid), getCreaturePosition(target)) > (attack.range or 3) then
				return doPlayerSendCancel(getCreatureMaster(pid), "Target is too far.") and doSendMagicEffect(getCreaturePosition(pid), CONST_ME_POFF)
			end
			if target > 0 and not isSightClear(getCreaturePosition(pid), getCreaturePosition(target), true) then
				return doPlayerSendCancel(getCreatureMaster(pid), "There is not enough room.") and doSendMagicEffect(getCreaturePosition(pid), CONST_ME_POFF)
			end
			if target == 0 and attack.type == TYPE_DISTANCE then
				return doPlayerSendCancel(getCreatureMaster(pid), "Please select a target first.") and doSendMagicEffect(getCreaturePosition(pid), CONST_ME_POFF)
			end
			if getPlayerLevel(getCreatureMaster(pid)) < attack.level then
				return doPlayerSendCancel(getCreatureMaster(pid), "You need level " .. attack.level .. " or higher to use this attack.") and doSendMagicEffect(getCreaturePosition(pid), CONST_ME_POFF)
			end
			if getPetLevel(pid) < attack.petLevel then
				return doPlayerSendCancel(getCreatureMaster(pid), "Your pet needs level " .. attack.petLevel .. " or higher to use this attack.") and doSendMagicEffect(getCreaturePosition(pid), CONST_ME_POFF)
			end
			if attack.area then
				doCastAreaAttack(pid, target, attack.area, attack)
			else
				doTargetCombatHealth(pid, target, attack.combat, -getAttackFormula(pid, attack).min, -getAttackFormula(pid, attack).max, attack.effect)
			end
			return doSendDistanceShoot(getCreaturePosition(pid), (attack.type == TYPE_DISTANCE and getCreaturePosition(target) or getCreaturePosition(pid)), attack.distEffect) and doCreatureSay(pid, attack.name, TALKTYPE_MONSTER)
		else
			return doSendMagicEffect(getCreaturePosition(pid), CONST_ME_POFF)
		end
	else
		return doSendMagicEffect(getCreaturePosition(pid), CONST_ME_POFF) and doPlayerSendCancel(getCreatureMaster(pid), "There is a tecnical problem, please contact a gamemaster.")
	end
end

function callPet(cid)

	for i = 1, #petStorages do
		if getCreatureStorage(cid, petStorages[i]) < 0 then
			doCreatureSetStorage(cid, petStorages[i], 0)
		end
	end

	local pet = getPetByLevel(cid)
	if not pet then
		return doPlayerSendCancel(cid, "You do not have enough level to call a pet.")
	end

	local ret = doCreateMonster(pet.monster, getCreaturePosition(cid))

	if getCreatureStorage(cid, petStorages.level) < 1 then
		doCreatureSetStorage(cid, petStorages.level, pet.level)
	end

	if getCreatureStorage(cid, petStorages.exp) < 1 then
		doCreatureSetStorage(cid, petStorages.exp, getExperienceForLevel(pet.level))
	end

	if getCreatureStorage(cid, petStorages.level) < pet.level then
		doCreatureSetStorage(cid, petStorages.level, pet.level)
	elseif getPlayerLevel(cid) * 2 < getCreatureStorage(cid, petStorages.level) then
		doCreatureSetStorage(cid, petStorages.level, (getPetByLevel(cid).level or 0))
		doCreatureSetStorage(cid, petStorages.exp, getExperienceForLevel((getPetByLevel(cid).level or 0)))
	end

	doCreatureSetStorage(ret, petStorages.level, getCreatureStorage(cid, petStorages.level))
	doCreatureSetStorage(ret, petStorages.exp, getCreatureStorage(cid, petStorages.exp))
	
	doConvinceCreature(cid, ret)
	doCreatureSetStorage(ret, petStorages.isPet, 1)
	doSendMagicEffect(getCreaturePosition(ret), CONST_ME_TELEPORT)
	setCreatureMaxHealth(ret, getPetLevel(ret) * petGainHealth)
	doCreatureAddHealth(ret, -getCreatureHealth(ret) + getPetLevel(ret) * petGainHealth)
	setCreatureMaxMana(ret, getPetLevel(ret) * petGainMana)
	doCreatureAddMana(ret, -getCreatureMana(ret) + getPetLevel(ret) * petGainMana)
	for _, v in pairs(petGainTicks) do
		gainStat(ret, v)
	end
end

function doCastAreaAttack(pid, target, area, attack)
	local center = {}
	local areaxx = {}
	center.y = math.floor(#area/2)+1
	for y = 1, #area do
		for x = 1, #area[y] do
			local number = area[y][x]
			if number > 0 then
				center.x = math.floor(table.getn(area[y])/2)+1
				if attack.type == TYPE_DISTANCE then
					table.insert(areaxx, {x = getCreaturePosition(target).x + x - center.x, y = getCreaturePosition(target).y + y - center.y, z = getCreaturePosition(target).z})
				else
					table.insert(areaxx, {x = getCreaturePosition(pid).x + x - center.x, y = getCreaturePosition(pid).y + y - center.y, z = getCreaturePosition(pid).z})
				end				
			end
		end
	end
	for i = 1, #areaxx do
		doAreaCombatHealth(pid, attack.combat, areaxx[i], 0, (doComparePositions(areaxx[i], getCreaturePosition(getCreatureMaster(pid))) and 0 or -getAttackFormula(pid, attack).min), (doComparePositions(areaxx[i], getCreaturePosition(getCreatureMaster(pid))) and 0 or -getAttackFormula(pid, attack).max), attack.effect)
	end
end

function isPet(pid)
	return getCreatureStorage(pid, petStorages.isPet) > 0 and true or false
end

function doPetAddExperience(pid, exp)

	doCreatureSetStorage(pid, petStorages.exp, getCreatureStorage(pid, petStorages.exp) + exp)
	doCreatureSetStorage(getCreatureMaster(pid), petStorages.exp, getPetExperience(pid))
	doSendAnimatedText(getCreaturePosition(pid), exp, getConfigValue("gainExperienceColor"))
	return true
end

function getPetExperience(pid)
	return getCreatureStorage(pid, petStorages.exp)
end

function getPetLevel(pid)
	return getCreatureStorage(pid, petStorages.level)
end

function doPetSetLevel(pid, level)

	doCreatureSetStorage(pid, petStorages.level, level)
	doCreatureSetStorage(getCreatureMaster(pid), petStorages.level, level)
	setCreatureMaxHealth(pid, getPetLevel(pid) * petGainHealth)
	setCreatureMaxMana(pid, getPetLevel(pid) * petGainMana)
	return true
end

function getPlayerPet(cid)
	local pet
	if #getCreatureSummons(cid) < 1 then
		pet =  false
	end

	for _, it in ipairs(getCreatureSummons(cid)) do
		if isPet(it) then
			pet = it
			break
		end
	end
	return pet
end

Now create a file on data/talkactions/scripts called pet.lua and paste:
Lua:
function onSay(cid, words, param, channel)
	if(param == '') then
		doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command param required.")
		return true
	end

	if param:lower() == "call" then
		if getCreatureStorage(cid, petStorages.isDead) > 0 then
			return doPlayerSendCancel(cid, "You need to revive your pet first.")
		end
		local pet = getPlayerPet(cid)
		if not pet then
			callPet(cid)
			return doCreatureSay(cid, "Go pet!", TALKTYPE_SAY)
		else			
			doSendMagicEffect(getCreaturePosition(pet), CONST_ME_POFF)
			doRemoveCreature(pet)
			return doCreatureSay(cid, "It's enough!", TALKTYPE_SAY)
		end
	elseif param:lower() == "info" then
		local pet = getPlayerPet(cid)
		if pet then
			local pet_ = getPetInfo(getCreatureName(pet))
			local attacks = ""
			if pet_.attacks and #pet_.attacks > 0 then
				for i = 1, #pet_.attacks do
					attacks = attacks .. "Attack ID: " .. i .. "\n" .. (pet_.attacks[i].name ~= "" and "    Name: " .. pet_.attacks[i].name .. "\n" or "") .. "    Level: " .. pet_.attacks[i].level .. "\n    petLevel: " .. pet_.attacks[i].petLevel .. "\n"
				end
			end
			return doShowTextDialog(cid, 1948, "Here is your pet info:\n" .. 
				"\nName: " .. getCreatureName(pet) ..
				"\nHealth: " .. getCreatureHealth(pet) .. "-" .. getCreatureMaxHealth(pet) ..
				"\nMana: " .. getCreatureMana(pet) .. "-" .. getCreatureMaxMana(pet) ..
				"\nLevel: " .. getPetLevel(pet) ..
				"\nExperience: " .. getPetExperience(pet) ..
				"\n-----Attacks-----\n" .. (attacks ~= "" and attacks or "No attacks"))
		else
			return doPlayerSendCancel(cid, "Please call your pet first.")
		end
	elseif param:lower() == "take" then
		local pet = getPlayerPet(cid)
		if not pet then
			return doPlayerSendCancel(cid, "Please call your pet first.")
		end

		local slot = 0
		for i = 1, carryItems do
			if getCreatureStorage(cid, petItems[i]) < 1 then
				slot = i
				break
			end
		end
		
		if slot == 0 then
			return doPlayerSendCancel(cid, "You only can carry " .. carryItems .. " items.")
		end

		local item = getTopItem(getCreaturePosition(pet))
		if getItemWeightById(item.itemid, 1) and getItemWeightById(item.itemid, 1) < 1 then
			return doPlayerSendCancel(cid, "There is no items to carry.")
		end
		
		if isInArray(blockedItems, item.itemid) then
			return doPlayerSendCancel(cid, "You cannot carry this item.")
		end
		doCreatureSetStorage(cid, petItems[slot], item.itemid * 1000 + (item.type > 0 and item.type or 1))
		doRemoveItem(item.uid)
		doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "You take an item.")
		return true
	elseif param:lower() == "items" then
		local list = "Here is the list of items:\n"
		for i = 1, carryItems do
			if getCreatureStorage(cid, petItems[i]) < 1 then
				list = list .. "\n" .. i .. ". Empty."
			else
				local thing = getCreatureStorage(cid, petItems[i])
				local item = math.floor(thing / 1000)
				local count = thing - item * 1000
				list = list .. "\n" .. i .. ". x" .. count .. " " .. getItemNameById(item) .. "."
			end
		end

		return doShowTextDialog(cid, 1948, list)
	else
		param = string.explode(param, ":")
		if param[1]:lower() == "attack" then
			local pet = getPlayerPet(cid)
			if not pet then
				return doPlayerSendCancel(cid, "Please call your pet first.")
			end
			if not param[2] or not isNumber(param[2]) or tonumber(param[2]) < 1 then
				return doPlayerSendCancel(cid, "No attack index specified.")
			end

			if exhaustion.get(cid, petStorages.exhaust) then
				return doPlayerSendCancel(cid, 'You are exhausted.') and doSendMagicEffect(getCreaturePosition(getCreatureSummons(cid)[1]), CONST_ME_POFF)
			end

			doAttack(pet, getCreatureTarget(cid), tonumber(param[2]))
			exhaustion.set(cid, petStorages.exhaust, petExhaust)
		elseif param[1]:lower() == "give" then
			local pet = getPlayerPet(cid)
			if not pet then
				return doPlayerSendCancel(cid, "Please call your pet first.")
			end
			if not param[2] or not isNumber(param[2]) or tonumber(param[2]) < 1 then
				return doPlayerSendCancel(cid, "No slot specified.")
			end

			if getCreatureStorage(cid, petItems[tonumber(param[2])]) > 0 then
				local thing = getCreatureStorage(cid, petItems[tonumber(param[2])])
				local item = math.floor(thing/1000)
				local count = thing-item*1000
				doPlayerAddItem(cid, item, count)
				doCreatureSetStorage(cid, petItems[tonumber(param[2])], 0)
				doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "You get an item.")
			else
				return doPlayerSendCancel(cid, "This slot is empty.")
			end
		elseif param[1]:lower() == "say" then
			local pet = getPlayerPet(cid)
			if not pet then
				return doPlayerSendCancel(cid, "Please call your pet first.")
			end
			if not param[2] or param[2] == "" then
				return doPlayerSendCancel(cid, "Command param required.")
			end

			if exhaustion.get(cid, petStorages.sayExhaust) then
				return doPlayerSendCancel(cid, "You are exhausted.") and doSendMagicEffect(getCreaturePosition(getCreatureSummons(cid)[1]), CONST_ME_POFF)
			end

			doCreatureSay(pet, param[2], TALKTYPE_MONSTER)
			exhaustion.set(cid, petStorages.sayExhaust, petExhaust)
		elseif param[1]:lower() == "move" then
			local pet = getPlayerPet(cid)
			if not pet then
				return doPlayerSendCancel(cid, "Please call your pet first.")
			end
			if not param[2] or param[2] == "" then
				return doPlayerSendCancel(cid, "Command param required.")
			end

			if exhaustion.get(cid, petStorages.moveExhaust) then
				return doPlayerSendCancel(cid, "You are exhausted.") and doSendMagicEffect(getCreaturePosition(getCreatureSummons(cid)[1]), CONST_ME_POFF)
			end

			local dir
			local t =
			{
				["north"] = NORTH,
				["east"] = EAST,
				["south"] = SOUTH,
				["west"] = WEST,
				["southwest"] = SOUTHWEST,
				["southeast"] = SOUTHEAST,
				["northwest"] = NORTHWEST,
				["northeast"] = NORTHEAST
			}
			if t[param[2]:lower()] then
				dir = t[param[2]:lower()]
				local toPos = getPosByDir(getCreaturePosition(pet), dir, 1)
				local ret = queryTileAddThing(pet, toPos)
				if ret == RETURNVALUE_NOERROR then
					doMoveCreature(pet, t[param[2]:lower()])
					exhaustion.set(cid, petStorages.moveExhaust, petExhaust)
				else
					return doPlayerSendCancel(cid, "There is not enough room.") and doSendMagicEffect(getCreaturePosition(pet), CONST_ME_POFF)
				end
			else
				return doPlayerSendCancel(cid, "No direction specified.")
			end
		end
	end
	return true
end

Paste this at talkactions.xml:
XML:
	<talkaction words="!pet" event="script" value="pet.lua"/>

Create a fil called petKill.lua at data/creaturescripts/scripts and paste:
Lua:
function onKill(cid, target, lastHit)

	if not isMonster(target) then return true end
	if getConfigValue("rateExperience") < 0.1 then return true end
	if getMonsterInfo(getCreatureName(target)) and getMonsterInfo(getCreatureName(target)).experience < 1 then return true end

	local pet = getPlayerPet(cid)
	if pet then
		doPetAddExperience(pet, getMonsterInfo(getCreatureName(target)).experience * petExpRate)
		if getLevelByExp(getPetExperience(pet)) > getPetLevel(pet) then
			doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE, "Your pet advanced from level " .. getPetLevel(pet) .. " to level " .. getLevelByExp(getPetExperience(pet)) .. ".")
			doPetSetLevel(pet, getLevelByExp(getPetExperience(pet)))
		end
	end
	return true
end

Create a fil called petDeath.lua at data/creaturescripts/scripts and paste:
Lua:
function onDeath(cid, corpse, deathList)

	if not isMonster(cid) then return true end
	if not isPet(cid) then return true end

	doCreatureSetStorage(getCreatureMaster(cid), petStorages.isDead, 1)
	doPlayerSendTextMessage(getCreatureMaster(cid), MESSAGE_STATUS_CONSOLE_BLUE, "Your pet is dead.")
	return true
end

Now paste this at creaturescripts.xml:
XML:
	<event type="kill" name="petKill" event="script" value="petkill.lua"/>
	<event type="death" name="petDeath" event="script" value="petdeath.lua"/>

This is the script to revive the pet via npc:
Lua:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)
local talkState = {}
 
function onCreatureAppear(cid)                          npcHandler:onCreatureAppear(cid) end
function onCreatureDisappear(cid)                       npcHandler:onCreatureDisappear(cid) end
function onCreatureSay(cid, type, msg)                  npcHandler:onCreatureSay(cid, type, msg) end
function onThink()                                      npcHandler:onThink() end

function creatureSayCallback(cid, type, msg)

	if(not npcHandler:isFocused(cid)) then
		return false
	end
	local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_PRIVATE and 0 or cid
	if msgcontains(msg, 'revive') or msgcontains(msg, 'pet') then
		if getCreatureStorage(cid, petStorages.isDead) > 0 then
			npcHandler:say('Do you want to revive your pet for ' .. reviveCost .. ' gold coins?.', cid)
			talkState[talkUser] = 1
		else
			npcHandler:say('Your pet is not dead.', cid)
			talkState[talkUser] = 0
		end
	elseif msgcontains(msg, 'yes') and talkState[talkUser] == 1 then
		if doPlayerRemoveMoney(cid, reviveCost) then
			doCreatureSetStorage(cid, petStorages.isDead, 0)
			npcHandler:say('Your pet has been revived.', cid)
			talkState[talkUser] = 0
		else
			npcHandler:say('You do not have enough money.', cid)
			talkState[talkUser] = 0
		end
	end
	return true
end
 
npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())

This is how you configure a new pet (Water Elemental used as example, only one attack used as example):
Lua:
[4] = --Id, should be in order
	{
		monster = "water elemental", --The monster name.
		vocations = {2}, --Vocations that can use this pet.
		level = 95, --Level required to use this pet.
		attacks = 
		{
			[1] = 
			{
				name = "Ice Bomb", --Attack name, pet will say the name when you use the attack.
				level = 100, --Level that player needs to use the attack.
				petLevel = 100, --Level that pets needs to use the attack.
				type = TYPE_DISTANCE, --(TYPE_NEAR/TYPE_DISTANCE) _NEAR = Used for melee attacks. _NEAR = Used for distance attacks. (Declare this correct or you'll get some bugs.)
				range = 3, --Attack range, used only for distance attacks.
				combat = COMBAT_ICEDAMAGE, --Damage type.
				effect = CONST_ME_ICEAREA, --Floor effect.
				distEffect = CONST_ANI_ICE, --Distance effect.
				damageMin = 1.5, --Damage min.
				damageMax = 2.0, --Damage max.
				area = AREA_CROSS1X1 --Area for the attack, if you want an attack without area, remove this param.
			}
		}
	},

The pet system commands are:
  • !pet call // Call pet.
  • !pet info // It shows your pet info (Name, health, mana, level, experience and attacks)
  • !pet take // It takes the item above the pet.
  • !pet items // It shows the items you carry.
  • !pet attack:index // Attack with your pet. Ex: !pet attack:2
  • !pet give:index // Get a carried item. Ex: !pet give:4
  • !pet say:text // Makes your pet says something. Ex: !pet say:Hello World
  • !pet move:direction //Makes your moves. Ex: !pet move:northeast


NOTE: I used tibia creatures, remember that these creatures already has attacks, you can create new creatures to use it as pet.

Any bug you find, post here.
Enjoy.

Perfect <3.
 
I think this line is kinda unnecessary as nobody uses per-person NPC convo nowdays:
Code:
local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_PRIVATE and 0 or cid

You should either set talkState to nil or use table.remove instead of setting it to 0 and for spell areas you could've used dofile().

This one can be done within one statement:
Code:
if not isMonster(cid) then return true end
if not isPet(cid) then return true end

Also, why you once 'try' to shorten code with returning function instead of just value and once you return just value?

@Edit:
I wouldn't assign any value to variable here, I'd just define slot and then check it with if not slot.
Code:
local slot = 0

This:
Code:
local dir
local t =

Could be replaced with:
Code:
local dir, t = {...}

Also, as it's constant table, I would put it outside the function's scope.

@Edit2:
This probably could be optimized even more, but whatever. Good work though, it's way more clear code than the one we had here before.
 
The system it's self is awesome, but i don't like how it got like.. 10 commands, a player wud hotkey them and just forget about his spells :( You shud add a pet channel or something, still pet channel wud be lame, i dunno what suits pets better than !talkactions..

EDIT: It's still awesome tho, works for 0.3.6?

ANOTHER EDIT: Npc.xml plz, a bug, i say !pet call, tells me you do not have enough level to call a pet, i maxed my self to 717k, still tells me the same, plus how do i buy a pet lol?

Quoting so thread poster sees it...
 
-.- Stop that fuck. Darkhaos will see you post -.-

Nice to see another pet system.
Stop stalking me then...

Hey, you shud add an npc to buy pets, and when i use !pet call, nothing happens, You shud also add that it checks tile the player standing on and the tile the pet will be summoned on, if both are pz then doPlayerSendCancel(cid, "You cannot call your pet here"), Thanks in advance, Ranyo13
 
Last edited by a moderator:
It's not working for me, Well 0.3.6 is same as NaxedOT 8 (8.6) i think..
 
This system have that console errors in timer event when try to add mana or hp when the pet has logged out like josejunior and delyria system ?
 
This system have that console errors in timer event when try to add mana or hp when the pet has logged out like josejunior and delyria system ?

No, of course i made the system to check if pet is in game or not
 
I didn't get any help nor answer..
 
Last edited:
Back
Top