Ir para conteúdo

Oneshot Spell Lib


Oneshot

Posts Recomendados

Oneshot Spell Lib

Boa tarde, meus queridos.

 

Como eu disse no último post do tópico do Spell Forge, sim, ele estava ficando funcional o bastante, mas uma coisa não me agradava, o nível de dificuldade de configuração do sistema estava aumentando, e uma hora, não teria como OT-admins usarem meu sistema, pois não saberiam configurar.

 

Então resolvi parar o desenvolvimento dele por enquanto, mas segue minha biblioteca que estava usando para desenvolvimento do sistema.

 

Uma biblioteca completa para desenvolvimento de magias, orientada a objetos, torna a coisa bem mais interessante.

 

Para utilizar minha biblioteca, basta criar arquivo com qualquer nome na pasta data/lib do seu servidor e colar o seguinte conteúdo abaixo:

-- This library is part of Oneshot Spell System
-- Copyright (C) 2013 Oneshot
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.

-- constant
CONST_SPELL_AREA = 0 -- area spells, like exevo gran mas vis
CONST_SPELL_DIRECTION = 1 -- wave spells, like exevo flam hur
CONST_SPELL_TARGETORDIRECTION = 2 -- mix between area and wave spells

WEAPON_SKILLS = {
	[WEAPON_SWORD] = SKILL_SWORD,
	[WEAPON_CLUB] = SKILL_CLUB,
	[WEAPON_AXE] = SKILL_AXE,
}

-- class for combats (spell instances)
Combat = {
	type = 0,
	me = 0,
	ani = 0,
	formula = {
		type = 0,
		values = {},
	},
	condition = nil,
	delay = 0,
	id = 0,
}

function Combat:New(_type, me, ani, delay, id)
	local new_spellinstance = {
		type = _type or COMBAT_NONE,
		me = me or CONST_ME_NONE,
		ani = ani or CONST_ANI_NONE,
		formula = {
			type = COMBAT_FORMULA_UNDEFINED,
			values = {0, 0, 0, 0, 0, 0, 0, 0},
		},
		condition = nil,
		delay = delay or -1,
		id = id or 1,
	}
	return setmetatable(new_spellinstance, {__index = self})
end

function Combat:SetType(_type)
	self.type = (tonumber(_type) and _type or COMBAT_NONE)
end

function Combat:SetEffect(me)
	self.me = (tonumber(me) and me or CONST_ME_NONE)
end

function Combat:SetDistanceEffect(ani)
	self.ani = (tonumber(ani) and ani or CONST_ANI_NONE)
end

function Combat:SetFormula(_type, ...)
	local args = select("#", ...)
	self.formula.type = (tonumber(_type and _type or COMBAT_FORMULA_UNDEFINED))
	local minc, maxc
	if args > 8 then
		minc, maxc = select(9, ...)
	end

	local minm, maxm = getConfigValue("formulaMagic") or 1
	maxm = minm
	local minl, maxl = getConfigValue("formulaLevel") or 5
	maxl = minl

	if args > 6 then
		minm, maxm = select(7, ...)
	end

	if args > 4 then
		minl, maxl = select(5, ...)
	end

	local mina, minb, maxa, maxb = select(1, ...)
	self.formula.values = {mina, minb, maxa, maxb, minl, maxl, minm, maxm, minc, maxc}
end

function Combat:SetCondition(condition)
	-- condition needs to be a createConditionObject(), e.g
	-- local condition = createConditionObject(CONDITION_FIRE)
	-- setConditionParam(condition, CONDITION_PARAM_TICKS, 1 * 1000)
	self.condition = condition
end

function Combat:GetDelay()
	return self.delay
end

function Combat:SetDelay(delay)
	self.delay = (tonumber(delay) and delay or -1)
end

function Combat:GetId()
	return self.id
end

function Combat:SetId(id)
	self.id = (tonumber(id) and id or 1)
end

function Combat:getMinMaxValues(cid, ex)
	local min, max = 0, 0
	local n = self.formula.values
	
	if not isCreature(cid) then
		return false
	end
	
	if not isPlayer(cid) then
		self.formula.type = COMBAT_FORMULA_DAMAGE
	end
	
	if self.formula.type == COMBAT_FORMULA_LEVELMAGIC then
		min = (getPlayerLevel(cid) / n[5] + getPlayerMagLevel(cid) * n[7]) * n[1] + n[2]
		max = (getPlayerLevel(cid) / n[6] + getPlayerMagLevel(cid) * n[8]) * n[3] + n[4]
		
		if n[9] then
			min = math.max(n[9], min)
		end
		
		if n[10] then
			max = math.max(n[10], max)
		end
	elseif self.formula.type == COMBAT_FORMULA_SKILL then
		local weapon = getPlayerWeapon(cid)
		if weapon.uid > 0 then
			max = getPlayerWeaponDamage(cid, weapon) * n[3] + n[4]
		else
			max = n[4]
		end
		
		if n[10] then
			max = math.max(n[10], max)
		end
	elseif self.formula.type == COMBAT_FORMULA_DAMAGE then
		min = n[2]
		max = n[4]
	end
	return min, max
end

function Combat:Callback(position, cid, ex)
	if not isCreature(cid) then
		return false
	end
	
	local min, max = self:getMinMaxValues(cid, ex)

	doCombatAreaHealth(cid, self.type, position, 0, min, max, self.me)
	if self.condition then
		doCombatAreaCondition(cid, position, 0, self.condition, CONST_ME_NONE)
	end
	return true
end

-- class for spells
Spell = {
	type = 0,
	level = 0,
	maglevel = 0,
	mana = 0,
	needtarget = false,
	target_or_direction = false,
	range = 0,
	needweapon = false,
	selftarget = false,
	vocations = {},
	combats = {},
}

function Spell:New(_type, level, maglevel, mana, needtarget, range, needweapon, selftarget, ...)
	local new_spell = {
		type = _type or CONST_SPELL_AREA,
		level = level or 1,
		maglevel = maglevel or 0,
		mana = mana or 0,
		needtarget = needtarget or false,
		range = range or 1,
		needweapon = needweapon or false,
		selftarget = selftarget or false,
		vocations = {...},
		
		combat = {},
		area = {{3}},
	}
	return setmetatable(new_spell, {__index = self})
end

function Spell:SetType(_type)
	self.type = (tonumber(_type) and _type or CONST_SPELLarea)
end

function Spell:SetLevel(level)
	self.level = (tonumber(level) and level or 1)
end

function Spell:SetMagLevel(maglevel)
	self.maglevel = (tonumber(maglevel) and maglevel or 0)
end

function Spell:SetMana(mana)
	self.mana = (tonumber(mana) and mana or 0)
end

function Spell:SetNeedTarget(needtarget)
	self.needtarget = (type(needtarget) == "boolean" and needtarget or false)
end

function Spell:SetRange(range)
	self.range = (tonumber(range) and range or 1)
end

function Spell:SetNeedWeapon(needweapon)
	self.needweapon = (type(needweapon) == "boolean" and needweapon or false)
end

function Spell:SetSelfTarget(selftarget)
	self.selftarget = (type(selftarget) == "boolean" and selftarget or false)
end

function Spell:SetVocations(...)
	self.vocations = {...}
end

function Spell:Append(...)
	local t = {...}
	for i = 1, #t do
		self.combat[t[i]:GetId()] = t[i]
	end
end

function Spell:SetArea(area)
	self.area = area
end

function Spell:Cast(cid)
	if not isCreature(cid) then
		return false
	end
	
	if #self.combat == 0 then
		doSendMagicEffect(getCreaturePosition(cid), CONST_ME_POFF)
		return false
	end
	
	if isPlayer(cid) then
		if not getPlayerFlagValue(cid, PLAYERFLAG_IGNORESPELLCHECK) then
			if getPlayerLevel(cid) < self.level then
				doSendMagicEffect(getCreaturePosition(cid), CONST_ME_POFF)
				doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTENOUGHLEVEL)
				return false
			end
			
			if getCreatureMana(cid) < self.mana then
				doSendMagicEffect(getCreaturePosition(cid), CONST_ME_POFF)
				doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTENOUGHMANA)
				return false
			end
			
			if getPlayerMagLevel(cid) < self.maglevel then
				doSendMagicEffect(getCreaturePosition(cid), CONST_ME_POFF)
				doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTENOUGHMAGICLEVEL)
				return false
			end
			
			if self.needweapon and (getPlayerWeapon(cid).uid == 0) then
				doSendMagicEffect(getCreaturePosition(cid), CONST_ME_POFF)
				doPlayerSendDefaultCancel(cid, RETURNVALUE_YOUNEEDAWEAPONTOUSETHISSPELL)
				return false
			end
			
			local vocation = getPlayerVocation(cid)
			if #self.vocations > 0 and not (table.find(self.vocations, vocation) or table.find(self.vocations, getVocationInfo(vocation).fromVocation)) then
				doSendMagicEffect(getCreaturePosition(cid), CONST_ME_POFF)
				doPlayerSendDefaultCancel(cid, RETURNVALUE_YOURVOCATIONCANNOTUSETHISSPELL)
				return false
			end
		end
	end
	
	local target = getCreatureTarget(cid)
	if self.needtarget == true then
		if self.type == CONST_SPELL_DIRECTION then
			self.type = CONST_SPELL_TARGETORDIRECTION
		elseif self.type == CONST_SPELL_AREA and not isCreature(target) then
			doSendMagicEffect(getCreaturePosition(cid), CONST_ME_POFF)
			doPlayerSendDefaultCancel(cid, RETURNVALUE_YOUCANONLYUSEITONCREATURES)
			return false
		end
	end
	
	if self.range and isCreature(target) then
		if getDistanceBetween(getCreaturePosition(cid), getCreaturePosition(target)) > self.range then
			doSendMagicEffect(getCreaturePosition(cid), CONST_ME_POFF)
			doPlayerSendDefaultCancel(cid, RETURNVALUE_TOOFARAWAY)
			return false
		end
	end
	
	local area = self.area
	if self.type == CONST_SPELL_DIRECTION or (self.type == CONST_SPELL_TARGETORDIRECTION and not isCreature(target)) then
		area = getAreaByDir(area, getCreatureLookDirection(cid))
	end
	
	local centre = getCreaturePosition(cid)
	
	local target = getCreatureTarget(cid)
	if self.type == CONST_SPELL_DIRECTION then
		centre = getPosByDir(getCreaturePosition(cid), getCreatureLookDirection(cid), 1)
	elseif self.type == CONST_SPELL_TARGETORDIRECTION then
		centre = (isCreature(target) and getCreaturePosition(target) or getPosByDir(getCreaturePosition(cid), getCreatureLookDirection(cid), 1))
	elseif self.type == CONST_SPELL_AREA then
		if self.needtarget and isCreature(target) then
			centre = getCreaturePosition(target)
		end
	end

	local positions = getAreaPositions(area, centre)
	for i = 1, #area do
		for j = 1, #area[i] do
			local tmp = area[i][j]
			if tmp == 3 then
				for k = 1, #self.combat do
					local combat = self.combat[k]
					
					if combat then
						addEvent(function()
							if self.selftarget then
								combat:Callback(positions[i][j], 0)
							else
								combat:Callback(positions[i][j], cid)
							end
							doSendDistanceShoot(getCreaturePosition(cid), centre, combat.ani)
						end, combat:GetDelay())
					end
				end
			elseif type(tmp) == "number" and self.combat[tmp] then
				local combat = self.combat[tmp]
				
				if combat then
					addEvent(function()
						if self.selftarget then
							combat:Callback(positions[i][j], 0)
						else
							combat:Callback(positions[i][j], cid)
						end
						doSendDistanceShoot(getCreaturePosition(cid), centre, combat.ani)
					end, combat:GetDelay())
				end
			elseif type(tmp) == "table" then
				for k = 1, #tmp do
					local tile = tmp[k]
					local combat = self.combat[tile]
					
					if combat then
						addEvent(function()
							if self.selftarget then
								combat:Callback(positions[i][j], 0)
							else
								combat:Callback(positions[i][j], cid)
							end
							doSendDistanceShoot(getCreaturePosition(cid), centre, combat.ani)
						end, combat:GetDelay())
					end
				end
			end
		end
	end

	if self.mana > 0 then
		doCreatureAddMana(cid, -self.mana, 0)
	end
	return true
end

function rotate(area)
	local ret = {}
	for i = 1, #area do
		for j = 1, #area[i] do
			if not ret[#area[i]-j+1] then
				ret[#area[i]-j+1] = {}
			end
			ret[#area[i]-j+1][i] = area[i][j]
		end
	end
	return ret
end

function getAreaByDir(area, direction)
	local ret = area

	
	if direction > NORTH then
		local n = (4 - direction)
		repeat
			ret = rotate(ret)
			n = n - 1
		until n == 0
	end
	return ret
end

function getAreaCentre(area)
	local x, y = 0, 0
	for i = 1, #area do
		for j = 1, #area[i] do	
			if area[i][j] == 3 then
				x = j
				y = i
				break
			end
		end
	end
	return x, y
end

function getAreaPositions(area, centre)
	local ret = {}
	local x, y = getAreaCentre(area)

	for i = 1, #area do
		for j = 1, #area[i] do
			if not ret[i] then
				ret[i] = {}
			end
			ret[i][j] = {x = centre.x + (j - x), y = centre.y + (i - y), z = centre.z}
		end
	end
	return ret
end

function getPlayerMeleeDamage(cid, item)
	local skill, attack
	if item.uid > 0 then
		local info = getItemInfo(item.itemid)
		
		skill = getPlayerSkillLevel(cid, WEAPON_SKILLS[getItemWeaponType(item.uid)])
		attack = ((getItemAttribute(item.uid, "attack") or info.attack) + (getItemAttribute(item.uid, "extraAttack") or info.extraAttack) - info.abilities.elementDamage)
	else
		skill = getPlayerSkillLevel(cid, SKILL_FIST)
		attack = 0
	end
	
	local damage = math.ceil((2 * (attack * (skill + 5.8) / 25 + (getPlayerLevel(cid) - 1) / 10.)) / getPlayerAttackFactor(cid))
	return -math.random(0, damage)
end

function getPlayerAttackFactor(cid)
	local switch = {
		1.0,
		1.2,
		2.0,
	}
	return switch[(getPlayerModes(cid).fight + 1)]
end

dofile(getDataDir() .."/spells/lib/spells.lua")

É uma biblioteca orientada a objetos que facilita o desenvolvimento de magias. Comparando a forma dos scripts de magias, com a minha biblioteca e sem, podemos ver a diferença.

 

Sem a biblioteca:

local combat = createCombatObject()
setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_ENERGYDAMAGE)
setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_ENERGYAREA)
setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ENERGY)
setAttackFormula(combat, COMBAT_FORMULA_LEVELMAGIC, 5, 5, 4.5, 9)

local area = createCombatArea(AREA_SQUAREWAVE5, AREADIAGONAL_SQUAREWAVE5)
setCombatArea(combat, area)

function onCastSpell(cid, var)
	return doCombat(cid, combat, var)
end

Com a biblioteca:

local combat = Combat:New(COMBAT_ENERGYDAMAGE, CONST_ME_ENERGYAREA, CONST_ANI_ENERGY)
combat:SetFormula(COMBAT_FORMULA_LEVELMAGIC, -1, 0, -1, 0, 5, 5, 4.5, 9)

local spell = Spell:New(CONST_SPELL_DIRECTION)
spell:Append(combat)

spell:SetArea(AREA_WAVE4)

function onCastSpell(cid, var)
	return spell:Cast(cid)
end

Mas o melhor mesmo é notado quando você quer desenvolver as magias com mais de uma variável combat. Aquelas magias de múltiplos hits e efeitos.

 

Vou pegar uma magia de 3 efeitos e danos diferentes.

local combat1 = createCombatObject()
setCombatParam(combat1, COMBAT_PARAM_TYPE, COMBAT_ENERGYDAMAGE)
setCombatParam(combat1, COMBAT_PARAM_EFFECT, CONST_ME_ENERGYAREA)
setCombatParam(combat1, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ENERGY)
setAttackFormula(combat1, COMBAT_FORMULA_LEVELMAGIC, 5, 5, 4.5, 9)

local combat2 = createCombatObject()
setCombatParam(combat2, COMBAT_PARAM_TYPE, COMBAT_ICEDAMAGE)
setCombatParam(combat2, COMBAT_PARAM_EFFECT, CONST_ME_ICEAREA)
setCombatParam(combat2, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ICE)
setAttackFormula(combat2, COMBAT_FORMULA_LEVELMAGIC, 5, 5, 1, 2)

local combat3 = createCombatObject()
setCombatParam(combat3, COMBAT_PARAM_TYPE, COMBAT_EARTHDAMAGE)
setCombatParam(combat3, COMBAT_PARAM_EFFECT, CONST_ME_SMALLPLANTS)
setCombatParam(combat3, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_EARTH)
setAttackFormula(combat3, COMBAT_FORMULA_LEVELMAGIC, 5, 5, 3.5, 7)

local area = createCombatArea(AREA_SQUAREWAVE5, AREADIAGONAL_SQUAREWAVE5)

setCombatArea(combat1, area)
setCombatArea(combat2, area)
setCombatArea(combat3, area)


function onCastSpell1(cid, var)
	doCombat(cid, combat1, var)
end

function onCastSpell2(cid, var)
	doCombat(cid, combat2, var)
end

function onCastSpell3(cid, var)
	doCombat(cid, combat3, var)
end

function onCastSpell(cid, var)
	onCastSpell1(cid, var)
	addEvent(onCastSpell2, 300, cid, var)
	addEvent(onCastSpell3, 600, cid, var)
	return true
end

Com o uso da minha biblioteca, podemos notar a redução de linhas, e a limpeza do código:

local combat1 = Combat:New(COMBAT_ENERGYDAMAGE, CONST_ME_ENERGYAREA, CONST_ANI_ENERGY)
combat1:SetFormula(COMBAT_FORMULA_LEVELMAGIC, -1, 0, -1, 0, 5, 5, 4.5, 9)

local combat2 = Combat:New(COMBAT_ICEDAMAGE, CONST_ME_ICEAREA, CONST_ANI_ICE)
combat2:SetFormula(COMBAT_FORMULA_LEVELMAGIC, -1, 0, -1, 0, 5, 5, 1, 2)
combat2:SetId(2)
combat2:SetDelay(300)

local combat3 = Combat:New(COMBAT_EARTHDAMAGE, CONST_ME_SMALLPLANTS, CONST_ANI_EARTH)
combat3:SetFormula(COMBAT_FORMULA_LEVELMAGIC, -1, 0, -1, 0, 5, 5, 3.5, 7)
combat3:SetId(4)
combat3:SetDelay(600)

local T = {1, 2, 4}

local area = {
	{T, T, T},
	{T, T, T},
	{T, T, T},
	{0, T, 0},
	{0, 3, 0},
}

local spell = Spell:New(CONST_SPELL_DIRECTION)
spell:SetArea(area)

spell:Append(combat1, combat2, combat3)

function onCastSpell(cid, var)
	return spell:Cast(cid)
end

Qualquer dúvida quanto ao uso da biblioteca no desenvolvimento de magias, basta postar neste tópico que estarei esclarecendo.

 

 

Grande Abraço,

Oneshot.

Editado por Oneshot
Link para o comentário
Compartilhar em outros sites

O Spell Forge não é uma magia, mas sim uma talkaction que usava as funções da minha biblioteca.

 

Por enquanto ele está incompleto, estou reescrevendo ele, e tentando encontrar uma maneira de ele ficar o mais simples possível para o uso do sistema por qualquer pessoa que saiba o mínimo de Lua.

 

Grande abraço.

Link para o comentário
Compartilhar em outros sites

Quais as vantagens e desvantagens(se tiver alguma) de usar a sua lib para o desenvolvimento de novas spells, alem de reduzir o tamanho do codigo?

E tambem como cria uma spell com essa lib e que nao entendi essa parte:

combat3:SetId(4)

combat3:SetDelay(600).

Link para o comentário
Compartilhar em outros sites

Nenhuma vantagem e desvantagem, colega. Eu só senti vontade de desenvolver uma biblioteca orientada a objetos para desenvolvimento de magias. Não preciso convencer com quaisquer argumentos o uso dela.

 

Quanto as duas funções:

 

combat:SetID(2)

 

Isso vai dar o tileid igual a 2 para um combat, aí você vai poder chamar ela na área da sua magia com isso

 

local area = {
   {2},
   {3},
}

 

E a função:

 

combat:SetDelay(200)

 

É o delay, intervalo de tempo para o combat ser chamado em um sqm.

 

Abraços.

Editado por Oneshot
Link para o comentário
Compartilhar em outros sites

Ai que delícia de Lib, cara.

 

E EU FALEI QUE VOCÊ TAVA FAZENDO COM OOP, SEU CORNO

 

Hihi, falando sério agora, maneira a lib, só acho que você não deveria se preocupar com dificuldade em configurar, pois você não iria liberar o sistema anyway.

Link para o comentário
Compartilhar em outros sites

muito show =)

pretendo usar
+rep

 

 

Ai que delícia de Lib, cara.

 

E EU FALEI QUE VOCÊ TAVA FAZENDO COM OOP, SEU CORNO

 

Hihi, falando sério agora, maneira a lib, só acho que você não deveria se preocupar com dificuldade em configurar, pois você não iria liberar o sistema anyway.

 

eu acho q se ele ta se preocupando com a dificuldade é pq vai liberar né
'-'

Link para o comentário
Compartilhar em outros sites

muito show =)

pretendo usar

+rep

 

 

Ai que delícia de Lib, cara.

 

E EU FALEI QUE VOCÊ TAVA FAZENDO COM OOP, SEU CORNO

 

Hihi, falando sério agora, maneira a lib, só acho que você não deveria se preocupar com dificuldade em configurar, pois você não iria liberar o sistema anyway.

 

eu acho q se ele ta se preocupando com a dificuldade é pq vai liberar né

'-'

 

Ele não ia divulgar, tava conversando com ele quando ele começou a fazer o sistema, e ele falou que não pretendia liberar pra galera.

 

Isso é meio que coisa de programador, eu mesmo tenho vários sistemas aqui que não vou liberar, porém sempre procuro deixar as configurações bem simples.

Editado por LuckOake
Link para o comentário
Compartilhar em outros sites

  • 2 weeks later...
  • 3 weeks later...
×
×
  • Criar Novo...