[SCRIPT] The Arrow solution
Posted: Sat Dec 01, 2012 10:19 am
Hey there,
This one was a bit tricky, but here it goes: fix for custom projectiles & ammo problems!
Latest Version: 1.02 (Implemented grimQ, fixed a rare bug, cleaned up code)
Using essentially the same scripting model as my goromorg shield recreation, this script:
- registers custom arrows/quarrels on impact in a table
- respawns them on monster death, cleaning up wrongly reverted ones
- prevents you from accidentaly/on purpose enchanting custom projectiles
So now you can create custom arrows, that have "arrow" ammoType, are compatible with normal bows, and don't revert.
Note that JKos's LoG framework is required for this, as it involves an extended timer and adding hooks to all monsters at once. Version 1.02 also implements Xanathar's grimQ library to optimize query functions and code.
Here's the script that has to go in a script entity named arrowTracker:
Then you must define the following hooks:
If you have custom monsters, don't forget to update fw monster list.
There's comments in the code indicating what's going on, the only thing you must watch out for is that custom arrows/quarrels must have "arrow" or "quarrel" in their definition name. So if you want, let's say, a "slayer" like in DM, use name = "arrow_slayer" and uiName = "slayer".
Enjoy, feedback welcome.
This one was a bit tricky, but here it goes: fix for custom projectiles & ammo problems!
Latest Version: 1.02 (Implemented grimQ, fixed a rare bug, cleaned up code)
Using essentially the same scripting model as my goromorg shield recreation, this script:
- registers custom arrows/quarrels on impact in a table
- respawns them on monster death, cleaning up wrongly reverted ones
- prevents you from accidentaly/on purpose enchanting custom projectiles
So now you can create custom arrows, that have "arrow" ammoType, are compatible with normal bows, and don't revert.
Note that JKos's LoG framework is required for this, as it involves an extended timer and adding hooks to all monsters at once. Version 1.02 also implements Xanathar's grimQ library to optimize query functions and code.
Here's the script that has to go in a script entity named arrowTracker:
SpoilerShow
Code: Select all
-- Diarmuid's arrowTracker script, Version 1.02
-- JKos's Log Framework and Xanathar's grimQ modules required
--
-- Changelog
-- 1.02 Implemented grimQ methods, fixed a rare bug, cleaned up code
-- 1.01 Fixed save game serialization crash
-- 1.0 Initial release
arrowsTable = {}
aClear = {}
standardAssets = {"arrow","cold_arrow","fire_arrow","poison_arrow",
"shock_arrow", "quarrel", "cold_quarrel", "fire_quarrel",
"poison_quarrel","shock_quarrel"}
function register(monster, projectile)
-- Check if projectile is an arrow/quarrel. Custom arrows/quarrels must
-- have "arrow" or "quarrel" in the item name (uiName can be anything
-- of course). If not a missile weapon, let it through:
if string.find(projectile.name,"arrow") == nil and string.find(projectile.name,"quarrel") == nil then
return true
end
-- Check if it's a standard asset arrow/quarrel and let it through
-- so that it can revert:
if grimq.fromArray(standardAssets)
:any(function(v) return v==projectile.name; end) == true then
return true
end
-- Now that we know that the projectile is a custom projectile,
-- store it in the table and destroy it:
if arrowsTable[monster.id] == nil then
arrowsTable[monster.id] = {}
end
table.insert(arrowsTable[monster.id],projectile.name)
return true
end
function clear(monster)
if aClear[monster.id] == nil then
aClear[monster.id] = {}
end
table.insert(aClear[monster.id],monster.level)
table.insert(aClear[monster.id],monster.x)
table.insert(aClear[monster.id],monster.y)
table.insert(aClear[monster.id],monster.facing)
-- Set respawn of stored projectiles 0.1s after monster death:
local clearTimer = timers:create(monster.id..'_clearTimer')
clearTimer:setTimerInterval(0.1)
clearTimer:addCallback(
function(self,monsterId)
if arrowTracker.arrowsTable[monsterId] ~= nil then
-- get stored monsters
local monsterLevel = arrowTracker.aClear[monsterId][1]
local monsterX = arrowTracker.aClear[monsterId][2]
local monsterY = arrowTracker.aClear[monsterId][3]
local monsterFacing = arrowTracker.aClear[monsterId][4]
-- fix any custom reverted projectiles to normal models:
local droppedItems = grimq.fromEntitiesAround(monsterLevel,monsterX,monsterY, 2, true):toArray()
local arrowDestroyed
for i,v in ipairs(droppedItems) do
arrowDestroyed = false
if v.name == "arrow" then
spawn("arrow",monsterLevel,monsterX,monsterY,monsterFacing)
arrowDestroyed = true
v:destroy()
end
if arrowDestroyed == false and v.name == "quarrel" then
spawn("quarrel",monsterLevel,monsterX,monsterY,monsterFacing)
v:destroy()
end
end
-- spawn stored custom projectiles and destroys equivalent number of reverted ones:
for k,v in ipairs(arrowTracker.arrowsTable[monsterId]) do
spawn(v,monsterLevel,monsterX,monsterY,monsterFacing)
arrowDestroyed = false
droppedItems = grimq.fromEntitiesAround(monsterLevel,monsterX,monsterY, 2, true)
:where(grimq.isItem)
:toIterator()
if string.find(v,"arrow") ~= nil then
for i in droppedItems do
if i.name == "arrow" and arrowDestroyed == false then
i:destroy()
arrowDestroyed = true
end
end
end
if string.find(v,"quarrel") ~= nil then
for i in droppedItems do
if i.name == "quarrel" and arrowDestroyed == false then
i:destroy()
arrowDestroyed = true
end
end
end
end
-- clear tables:
for k,v in ipairs(arrowTracker.arrowsTable[monsterId]) do
table.remove(arrowTracker.arrowsTable[monsterId],k)
end
for k,v in ipairs(arrowTracker.aClear[monsterId]) do
table.remove(arrowTracker.aClear[monsterId],k)
end
end
end,
{monster.id}
)
clearTimer:setTickLimit(1)
clearTimer.destroy = true
clearTimer:activate()
end
function enchantArrows(caster,spell)
local arrowCast = true
-- Check if caster has a normal asset arrow or quarrel for enchanting,
-- if not, prevent casting
if spell == "enchant_fire_arrow" or spell == "enchant_frost_arrow"
or spell == "enchant_shock_arrow" or spell == "enchant_poison_arrow" then
local currentItem
arrowCast = false
currentItem = caster:getItem(7)
if currentItem ~= nil and grimq.fromArray(standardAssets)
:any(function(v) return v==currentItem.name; end) then
arrowCast = true
end
currentItem = caster:getItem(8)
if currentItem ~= nil and grimq.fromArray(standardAssets)
:any(function(v) return v==currentItem.name; end) then
arrowCast = true
end
if arrowCast == false then
hudPrint("Wrong ammo type!")
end
end
return arrowCast
end
SpoilerShow
Code: Select all
fw.addHooks('monsters','arrows',{
onProjectileHit = function(self,projectile,damage,type)
return arrowTracker.register(self,projectile)
end,
onDie = function(self)
return arrowTracker.clear(self)
end,
}
)
fw.addHooks('party','arrows',{
onCastSpell = function(caster, spell)
return arrowTracker.enchantArrows(caster,spell)
end,
}
)
There's comments in the code indicating what's going on, the only thing you must watch out for is that custom arrows/quarrels must have "arrow" or "quarrel" in their definition name. So if you want, let's say, a "slayer" like in DM, use name = "arrow_slayer" and uiName = "slayer".
Enjoy, feedback welcome.