Having some trouble with shootProjectile

Ask for help about creating mods and scripts for Grimrock 2 or share your tips, scripts, tools and assets with other modders here. Warning: forum contains spoilers!
MrChoke
Posts: 324
Joined: Sat Oct 25, 2014 7:20 pm

Re: Having some trouble with shootProjectile

Post by MrChoke »

AndakRainor wrote:So, here my function is local :

Code: Select all

local function rock_fall()
   spawn("rock_fall_spell",party.level,x-0.5+math.random(),y-0.5+math.random(),direction,elevation)
end
What would be the correct values for the receiver and msg parameters in this situation ?
Should I avoid using a local function ?
Where should I put the code of that function instead, and what would then be the correct values for the delayCall parameters ?
The function cannot be local. Place it in file-scope in whatever script entity you like. The 1st parameter to delayedCall() is the ID of the script entity.

For example, I have a script entity with an ID of "lib_scr". Let's say I have a non-local function defined in it called "delayedHudPrint".

From any other script, even in defineObject{} definitions, I can call this function with delayedCall by something like this:

Code: Select all

delayedCall("lib_scr", 1.5, "delayedHudPrint" , "This is a delayed message")
The function can be

Code: Select all

function delayedHudPrint(msg)
    hudPrint(msg)
end
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Having some trouble with shootProjectile

Post by AndakRainor »

Thank you :)
Just to be sure you noticed, I have posted the next step of my work on this spell (end of page 2) with still more questions ;p
Grimfan
Posts: 369
Joined: Wed Jan 02, 2013 7:48 am

Re: Having some trouble with shootProjectile

Post by Grimfan »

While I'm not a great scripter myself I have noted that all impact spells have the TileDamager component. Take these lines from the fireburst spell for example:

Code: Select all

{
			class = "TileDamager",
			attackPower = 20,
			damageType = "fire",
			sound = "fireburst",
			screenEffect = "fireball_screen",			
			--cameraShake = true,
		},


I think that the TileDamager component determines whether the spell grants exp or not (it also looks like it would simplify your script). In fact the only spells which deal damage and grant exp that do not have this component have the CloudSpell component instead (like poison_cloud).
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Having some trouble with shootProjectile

Post by AndakRainor »

Well I tried this, with or without delay, but so far no custom spell seems to grant XP, only the original game spells do;

Code: Select all

onCast = "fireball"
works for example. But the current asset pack does not include the original spells code, nowhere have I found the function "fireball" or "meteorStorm". Note that those seem to be function names, they do not include "_" and don't match the spells "defineObject" sections, for example there is no "fireball" object but "fireball_small", "fireball_medium" and "fireball_large". So unless someone has a clue, the original spells code would be needed to resolve the xp problem...
minmay
Posts: 2790
Joined: Mon Sep 23, 2013 2:24 am

Re: Having some trouble with shootProjectile

Post by minmay »

Look at the scripting reference. You need to use TileDamagerComponent:setCastByChampion().
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Having some trouble with shootProjectile

Post by AndakRainor »

Thank you !

This way it works :

Code: Select all

defineObject{
   name = "rock_fall_spell",
   baseObject = "base_spell",
   components = {
     {
       class = "Model",
       model = "assets/models/items/rock.fbx",
       rotation = vec(0, 0, 0),
     },
     {
       class = "Projectile",
       spawnOffsetY = 3,
       velocity = 0,
       gravity = 10,
       radius = 0.1,
       hitEffect = "rock_blast",
     },
   },
}

defineObject{
  name = "rock_blast",
  baseObject = "base_spell",
  components = {
    {
      class = "TileDamager",
      attackPower = 5,
      castByChampion = 1,
      damageType = "physical",
      damageFlags = DamageFlags.NoLingeringEffects,
      woundChance = 10,
    },
  },
}
I just put castByChampion = 1 since I don't have access to the correct ordinal number at his point in the code. It is not a problem as the xp is shared equally among champoins in this game, but may be I should get it right just in case. I will search a way to pass the correct value... (this TileDamagerComponent is only referenced by the projectileComponent through a string, so I should perhaps find another way)
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Having some trouble with shootProjectile

Post by AndakRainor »

Hello, I just noticed a strange behavior with the spawn function :

Code: Select all

spawn("custom_spell", party.level, x, y, direction, elevation)
this works well with any floating values for x, y and elevation, so it's a good way to copy the meteor spell.

But, for x and y, it only works with spells defined with a model and projectile component so far, just like in the code modders suggested here.
If you try to use a spell from the original game instead, or a custom one with components similar to the original spells (particles...), only integer numbers are allowed, or you get an error.

Has anyone tried this ? Any idea why it is happening ? some components only accepting integers ?
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Having some trouble with shootProjectile

Post by AndakRainor »

Hello !

Here is a little update on my custom spells, with one example spell named Fire & Ice. It is supposed to launch several fireballs and frostbolts. To cast it, use the same gesture as the meteor spell, but replace the air rune with the water rune.

in spells.lua :

Code: Select all

defineSpell{
   name = "fire_and_ice",
   uiName = "Fire & Ice",
   skill = "fire_magic",
   requirements = {"fire_magic", 4, "water_magic", 4},
   gesture = 14569,
   manaCost = 80,
   icon = 72,
   spellIcon = 13,
   description = "Unleashes a devastating storm of fire and ice on your foes.",
   onCast = function(champion, x, y, direction, elevation, skillLevel)
      delayedCall("script_center", 0, "multi", champion:getCurrentStat("willpower")*skillLevel/10, {"fireball", "frostbolt"}, 2)
   end
}
in scripts.lua (the source file for the script entity "script_center") :

Code: Select all

function multi(power, tab, length)
  power = quantum(power)
  local start = math.random(length)
  for i = 0, power do delayedCall("script_center", i/power, tab[1 + (start + i)%length]) end
end

function quantum(float)
  local i = math.floor(float)
  if math.random() < float-i then return i+1 else return i end
end

function checkEmpty()
   local x = party.x
   local y = party.y
   local direction = party.facing
   local elevation = party.elevation
   local myx = x
   local myy = y
   if direction == 0 then myy = y-1 elseif direction == 1 then myx = x+1 elseif direction == 2 then myy = y+1 else myx = x-1 end
   if party.map:checkLineOfFire(x,y,myx,myy,elevation) then return true else return false end
end

function fireball()
  local x = party.x
  local y = party.y
  local direction = party.facing
  local elevation = party.elevation - 0.25 + 0.5*math.random()
  if checkEmpty() then
    if direction == 0 then y = y-1 elseif direction == 1 then x = x+1 elseif direction == 2 then y = y+1 else x = x-1 end
  end
  local ball = spawn("fireball_medium_custom", party.level, x, y, direction, elevation)
  ball:setSubtileOffset(math.random() - 0.5, math.random() - 0.5)
end

function frostbolt()
  local x = party.x
  local y = party.y
  local direction = party.facing
  local elevation = party.elevation - 0.25 + 0.5*math.random()
  if checkEmpty() then
    if direction == 0 then y = y-1 elseif direction == 1 then x = x+1 elseif direction == 2 then y = y+1 else x = x-1 end
  end
  local bolt = spawn("frostbolt_custom", party.level, x, y, direction, elevation)
  bolt:setSubtileOffset(math.random() - 0.5, math.random() - 0.5)
end
finally, in objects.lua :

Code: Select all

defineObject{
	name = "fireball_medium_custom",
	baseObject = "base_spell",
	components = {
		{
			class = "Particle",
			particleSystem = "fireball_medium",
		},
		{
			class = "Light",
			color = vec(1, 0.5, 0.25),
			brightness = 15,
			range = 7,
			castShadow = true,
		},
		{
			class = "Projectile",
			spawnOffsetY = 1.35,
			velocity = 10,
			radius = 0.1,
			hitEffect = "fireball_blast_medium_custom",
		},
		{
			class = "Sound",
			sound = "fireball",
		},
		{
			class = "Sound",
			name = "launchSound",
			sound = "fireball_launch",
		},
	},
}

defineObject{
	name = "fireball_blast_medium_custom",
	baseObject = "base_spell",
	components = {
		{
			class = "Particle",
			particleSystem = "fireball_blast_medium",
			destroyObject = true,
		},
		{
			class = "Light",
			color = vec(1, 0.5, 0.25),
			brightness = 40,
			range = 10,
			fadeOut = 0.5,
			disableSelf = true,
		},
		{
			class = "TileDamager",
			attackPower = 50,
      castByChampion = 1,
			damageType = "fire",
			sound = "fireball_hit",
			screenEffect = "fireball_screen",
			woundChance = 20,
		},
	},
}

defineObject{
	name = "frostbolt_custom",
	baseObject = "base_spell",
	components = {
		{
			class = "Particle",
			particleSystem = "frostbolt",
		},
		{
			class = "Light",
			color = vec(0.25, 0.5, 1),
			brightness = 15,
			range = 7,
		},
		{
			class = "Projectile",
			spawnOffsetY = 1.35,
			velocity = 10,
			radius = 0.1,
			hitEffect = "frostbolt_blast_custom",
		},
		{
			class = "Sound",
			sound = "frostbolt",
		},
		{
			class = "Sound",
			name = "launchSound",
			sound = "frostbolt_launch",
		},
	},
}

defineObject{
	name = "frostbolt_blast_custom",
	baseObject = "base_spell",
	components = {
		{
			class = "Particle",
			particleSystem = "frostbolt_hit",
			destroyObject = true,
		},
		{
			class = "Light",
			color = vec(0.25, 0.5, 1),
			brightness = 40,
			range = 10,
			fadeOut = 0.5,
			disableSelf = true,
		},
		{
			class = "TileDamager",
			attackPower = 10,
      castByChampion = 1,
			damageType = "cold",
			sound = "frostbolt_hit",
			onHitMonster = function(self, monster)
				if math.random() < 0.25 then
					monster:setCondition("frozen", 4)
				end
			end,
		},
	},
}
As you can see, I don't use a fixed number of bolts as the meteor spell does, but a formula with skillLevel and the caster's willpower (the spell is probably not balanced with this exact formula but this is another debate).

Also, I still have to find better icons, and a way to set the correct value for the castByChampion variable of the TileDamager component. In this example if I could do that from the fireball function in scripts.lua I could also remove completely the object definitions that are copy/paste of the original game with just custom names and "castByChampion = 1".

Finally, in this example, the line "skill = "fire_magic"," seems a little wrong, what about multiple / new magic schools or damage types in mods ?

What do you think about this spell ? Any idea about the syntax used to access this castByChampion variable ?
User avatar
Thorham
Posts: 80
Joined: Sat May 04, 2013 5:12 pm

Re: Having some trouble with shootProjectile

Post by Thorham »

AndakRainor wrote:Any idea about the syntax used to access this castByChampion variable ?
I've made something similar, namely meteor spells for each element, and I simply use script components to set castByChampion. Seems to be the only way because of how the cast system works.
Post Reply