Custom spells + monster attacking = Crash!! HELP PLS!!

Talk about creating Grimrock 1 levels and mods here. Warning: forum contains spoilers!
Post Reply
User avatar
akroma222
Posts: 1029
Joined: Thu Oct 04, 2012 10:08 am

Custom spells + monster attacking = Crash!! HELP PLS!!

Post by akroma222 »

Hello there!!

I have a situation here - if my champ casts a custom projectile spell AND there is a monster in front of the party attacking at the same time - the game crashes!! :(
I noticed this while play testing Labyrinth of Lies... and have tried to replicate the crash in the editor - which was successful.
However, only some monsters (when attacking) would cause the editor to crash out of the preview...
I tried (using Magic Arrow and Blades) against crabs, spiders, wyverns, ogres, tentacles, scavengers, Leki's Ghoul and crowerns.
Only Tentacles, Spiders and Crowerns would cause the crash... and I may be way off on this, but they are monsters that seem to move themselves into the party's square when melee attacking....???

This is the error message I get...
SpoilerShow
#fxScripts:86: attempt to index field 'node' (a nil value)
stack traceback:
[C]: in function 'error'
[string "ScriptInterface.lua"]: in function 'setParticleSystem'
#fxScripts:86: in function <#fxScripts:78>
[string "ScriptEntity.lua"]: in function 'onMessageReceived'
[string "MessageSystem.lua"]: in function 'sendMessageToEntity'
[string "MessageSystem.lua"]: in function 'broadcastMessage'
[string "TriggerEvents.lua"]: in function 'fire'
[string "Timer.lua"]: in function 'update'
[string "Map.lua"]: in function 'updateEntities'
[string "Dungeon.lua"]: in function 'updateLevels'
[string "GameMode.lua"]: in function 'update'
[string "Grimrock.lua"]: in function 'display'
[string "Grimrock.lua"]: in main chunk
I am assuming it is talking about the 'node' of the monster that is attacking???

I will post some of the coding that is involved, hopefully someone can see what I can't :oops:

The spells... Magic Arrow
SpoilerShow
spellCaster = 0
function castMagicArrow(caster, x, y, direction, skill)

local dx,dy = getForward(party.facing)
local e = caster:getStat("energy")
local c = findEntity("learnPrismicMissileCounter")

if skill >= 26 then
shootProjectile("prismic_arrow", party.level, party.x,party.y, direction, 22, 0, 0, 0, 0, 0, 3*skill, party, true, originator)
playSound("magic_arrow")
if c and c:getValue() == 0 then
hudPrint("Magic Arrow has upgraded (skill 26) to a new spell: Prismic Missile!")
playSound("upgrade_spell")
c:destroy()
end
else
if (caster:getItem(7) ~= nil and caster:getItem(7).name == "magic_sphere") or (caster:getItem(8) ~= nil and caster:getItem(8).name == "magic_sphere") then
playSound("magic_arrow")
shootProjectile("prismic_arrow", party.level, party.x,party.y, direction, 22, 0, 0, 0, 0, 0, 3*skill, party, true, originator)
else
playSound("magic_arrow")
shootProjectile("magic_arrow", party.level, party.x,party.y, direction, 16, 0, 0, 0, 0, 0, math.random(20,30), party, true, originator)
end
end
for ent in entitiesAt(party.level, party.x, party.y) do
if ent.name == "magic_arrow" then
fxScripts.addProjectileEffect(ent, "magic_arrow", 20, "magic_arrow_hit")
end
if ent.name == "prismic_arrow" then
fxScripts.addProjectileEffect(ent, "prismic_arrow", 20, "prismic_arrow_hit")
end
end
spellCaster = caster:getOrdinal()
return true
end
and Blades....
SpoilerShow
function castBlades(caster,x,y,direction,skill)

local dx,dy = getForward(party.facing)
local x,y = party.x + dx, party.y + dy
local originator = caster:getOrdinal()
local e = caster:getSkillLevel("earth_magic")
local c = findEntity("learnShrapnelBlastCounter")

playSound("blades_gear")
if e >= 40 then
if party.facing == 0 or party.facing == 2 then
for modif = -0.5,0.5,0.1 do
shootProjectile("blade_gear", party.level, x+modif, y, party.facing, 14+(modif*2), 0, 1, 0, 0, 0, 35, party, true, originator)
end
else
for modif = -0.5,0.5,0.1 do
shootProjectile("blade_gear", party.level, x, y+modif, party.facing, 14+(modif*2), 0, 1, 0, 0, 0, 35, party, true, originator)
end
end
for ent in entitiesAt(party.level, party.x, party.y) do
if ent.name == "blade_gear" then
fxScripts.addProjectileEffect(ent, "blades_knife", 9, "hit_flame")
end
end
if c and c:getValue() == 0 then
hudPrint("Blades has upgraded (Earth 40) to a new spell: Shapnel Blast!")
playSound("upgrade_spell")
c:destroy()
end
else
if party.facing == 0 or party.facing == 2 then
for modif = -0.5,0.5,0.1 do
shootProjectile("blade_knife", party.level, x+modif, y, party.facing, 14+(modif*2), 0, 1, 0, 0, 0, 15, party, true, originator)
end
else
for modif = -0.5,0.5,0.1 do
shootProjectile("blade_knife", party.level, x, y+modif, party.facing, 14+(modif*2), 0, 1, 0, 0, 0, 15, party, true, originator)
end
end
for ent in entitiesAt(party.level, party.x, party.y) do
if ent.name == "blade_knife" then
fxScripts.addProjectileEffect(ent, "blades_knife", 9, "hit_flame")
end
end
end
spellCaster = caster:getOrdinal()
return true
end
In monsters.lua I have an onProjectileHitHook....
SpoilerShow
for i=1,# allMonstersList do
cloneObject{
name = allMonstersList,
baseObject = allMonstersList,
onProjectileHit = function(monster, projectile, damage, damageType)
return mymod_spells.customSpellOnProjectileHitHook(monster,projectile,damage, damageType)
end,
}
end


that calls a function in the same script as the custom spells....
SpoilerShow
projectileItems = {'blade_knife' , 'blade_gear' , 'light_missile' , 'arcane_missile' , 'venomstone_exploder' , 'venomstone_shot' ,
'dark_missile' , 'dread_missile' , 'ghost_flame' , 'cleansing_flame' , 'whipping_rock' , 'blazing_rock' ,
'iceflame_bolt' , 'iceflame_torrent' , 'iceflame_torrent_dire' , 'wizards_fire_ball' , 'magic_arrow' ,
'prismic_arrow' ,'boulder_rolling' , 'returning_throwing_axe' , 'recurring_nightmare' , 'crystal_shard' ,
'soul_orb_bomb' , 'cool_stone' , 'pyre_stone' , 'mind_stone' , 'tooth_lizard' , 'boom_rock' , 'death_star'}


function isInTable(table, element)
for _,value in pairs(table) do
if value == element then
return true
end
end
return false
end


function customSpellOnProjectileHitHook(monster,projectile,damage,damageType)
local dx,dy = getForward(monster.facing)
if isInTable(projectileItems, projectile.name) then

if projectile.name == 'magic_arrow' then
local originator = 2 ^ (spellCaster+1) -- calculate the originator of the spell
local damage = math.random(15,30)--
damageTile(monster.level,monster.x,monster.y,(monster.facing + 2)%4,originator+1, 'physical',damage)
playSound("prismic_missile_hit")
return false

elseif projectile.name == 'blade_knife' then
local originator = 2 ^ (spellCaster+1) -- calculate the originator of the spell
local damage = math.random(15,20) --
damageTile(monster.level,monster.x,monster.y,(monster.facing + 2)%4,originator+1, 'physical',damage)
playSoundAt("impact_blade",monster.level,monster.x,monster.y)
return false
end
end


So that is all pretty simple... what I think is causing the problem is the script that adds the effects to the spells. This script is not my work (it was written by ....... for the spells Wizards Fire and Wizards Life Fire) and admittedly is way beyond me in terms of complexity :oops: :cry: This is it....
SpoilerShow
projectiles = {}
function addProjectileEffect(projectile,effectName,speed,onHitEffectName)
local timer = spawn('timer',projectile.level,projectile.x,projectile.y,projectile.facing)
local effect = spawn('fx',projectile.level,projectile.x,projectile.y,projectile.facing)
projectiles[timer.id] = {}
projectiles[timer.id].projectile_id = projectile.id
projectiles[timer.id].effect_id = effect.id
projectiles[timer.id].level = projectile.level
projectiles[timer.id].x = 0
projectiles[timer.id].y = 0
projectiles[timer.id].facing = projectile.facing
projectiles[timer.id].onHitEffect= onHitEffectName

local dx,dy = getForward(projectile.facing)
dy = -1 * dy

projectiles[timer.id].x = dx * 0.5
projectiles[timer.id].y = dy * 0.5

timer:addConnector('activate','fxScripts','move_effect')
timer:setTimerInterval(0.5/speed)
local light = getLightPreset(effectName)
if light then
effect:setLight(unpack(light))
end

effect:translate(0.5*dx,1,0.5*dy)
effect:setParticleSystem(effectName)
timer:activate()
return effect
end

function addProjectileManualHit(caster, projectile, damageAmount, damageType, radius)

for key,value in pairs(projectiles) do
if value.projectile_id == projectile.id then
if caster then
projectiles[key].caster = caster:getOrdinal()
end
projectiles[key].damage = damageAmount
projectiles[key].damageType = damageType
projectiles[key].radius = radius

end
end

end

function getLightPreset(preset)
--FX:setLight(red, green, blue, brightness, range, time, castShadow)
local s = {}
s['fireball'] = {1, 0.5, 0.25,15,7,200,true}
s['fireball_hit'] = {1, 0.5, 0.25,40,7,1,true}
s['magic_missile'] = {1, 1, 1,15,7,200,true}
s['poison_bolt'] = {0.25, 0.6, 0.2,4,4,200,true}
s['lightning_bolt'] = {0.25, 0.5, 1,30,5,1,true}
s['lightning_bolt_hit'] = {0.25, 0.5, 1,100,5,0.5,true}
s['magic_missile_hit'] = {1, 1, 1,40,10,1,true}
s['wizards_fire'] = {1, 0.9, 0.5,40,10,1,true}
s['wizards_life_fire'] = {1, 1, 1,120,10,1,true}
s['wizards_fire_hit'] = {1, 1, 0.95, 150,10,0.6,true}

local l = s[preset]

if not l then
l = {0, 0, 0, 0, 0, 0, false}
end

return l

end




function move_effect(timer)
local e = projectiles[timer.id]
local p = findEntity(e.projectile_id)
local fx = findEntity(e.effect_id)

if not p then
if e.onHitEffect then
local ohfx = spawn('fx',e.level,e.px,e.py,e.facing)
ohfx:setParticleSystem(e.onHitEffect)
ohfx:translate(0, 1, 0)
local light = getLightPreset(e.onHitEffect)
if light then
ohfx:setLight(unpack(light))
end
end

projectiles[timer.id] = nil
timer:deactivate()
timer:destroy()
fx:destroy()
return
end
e.px = p.x
e.py = p.y
fx:translate(e.x,0,e.y)
end


Unfortunately I do not get any help from the editor when it crashes back to preview.
Any help at all would be appreciated!! :D :D

Akroma
User avatar
Leki
Posts: 550
Joined: Wed Sep 12, 2012 3:49 pm

Re: Custom spells + monster attacking = Crash!! HELP PLS!!

Post by Leki »

There is not a monster moving into party cell - it's only animation - monster stays still on the same cell. Imho, there is a problem in collision of monster capsule and projectile. This projectiles are timer based and I believe that if OnHit hook happens and timer is still there, projectile model is destroyed and 0,25 sec later there is not a projectile node to spawn light etc. So in OnHit Hook you shall to destroy all connected timers as well or smth like that. But to be honest, I don't like this (and any) timer based method...
I'm the Gate I'm the Key.
Dawn of Lore
User avatar
akroma222
Posts: 1029
Joined: Thu Oct 04, 2012 10:08 am

Re: Custom spells + monster attacking = Crash!! HELP PLS!!

Post by akroma222 »

Ahhh thanks Leki :)
That does make some sense, even for me hehehe. I will see what I can do regarding the onHit hook and destroying timers. - Thank you!!

I understand that the timer based method is not the best, but unfortunately I have run out of time for working on my mod
- restructuring and testing all the spells in Labyrinth of Lies, using a different system, will take too much time :cry:
It will be something I look at for my next mod for sure though ;)

Akroma
Post Reply