Ask a simple question, get a simple answer

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!
Whisper
Posts: 184
Joined: Fri Apr 13, 2012 5:01 pm

Re: Ask a simple question, get a simple answer

Post by Whisper »

Ok, figured out!

print(party.party:getChampionByOrdinal(1):getCurrentStat("cooldown_rate"))
bongobeat
Posts: 1076
Joined: Thu May 16, 2013 5:58 pm
Location: France

Re: Ask a simple question, get a simple answer

Post by bongobeat »

Hey, does the following spell spawn an object that stay in the dungeon forever and is saved also?


Base spell:
SpoilerShow

Code: Select all

defineSpell{--used by minor wizard virge
	name = "minorvirgespell",
	uiName = "Light Bolt",
	gesture = 0,
	manaCost = 30,
	skill = "light_magic",
	requirements = {"light_magic", 3},
	icon = 58,
	spellIcon = 18,
	description = "Cast a bolt of pure light on your foes.",
	hidden = true,
	onCast = function(champion,x,y,direction,elevation,skill)

		local bolt = spawn("minorvirgespell_bolt",party.level,x,y,direction,elevation)

		bolt.projectile:setIgnoreEntity(party)
		local ord = champion:getOrdinal()
		local left = nil
		for i = 1,4 do
			if party.party:getChampion(i):getOrdinal() == ord then
				left = i == 1 or i == 3
				break
			end
		end
		local wpos = party:getWorldPosition()
		local dx = nil
		local dz = nil
		if party.facing == 0 then
			dx = left and -0.1 or 0.1
			dz = -1
		elseif party.facing == 1 then
			dz = left and 0.1 or -0.1
			dx = -1
		elseif party.facing == 2 then
			dx = left and 0.1 or -0.1
			dz = 1
		else -- party.facing == 3
			dz = left and -0.1 or 0.1
			dx = 1
		end
		bolt:setWorldPosition(vec(wpos[1]+dx,wpos[2]+1.35,wpos[3]+dz))
	end,
}
Object and blast:
SpoilerShow

Code: Select all

defineObject{
	name = "minorvirgespell_bolt",
	baseObject = "base_spell",
	components = {
		{
			class = "Particle",
			particleSystem = "arcane_bolt",
		},
		{
			class = "Light",
			color = vec(1, 0.6, 1),
			brightness = 7,
			range = 5,
		},
		{
			class = "Sound",
			sound = "blob",
			pitch = 0.5,
		},
		{
			class = "Sound",
			name = "launchSound",
			sound = "arcane_launch",
			onInit = function(self) self:setPitch(1.2+math.random()*0.6) end,
		},
		{
			class = "Projectile",
			spawnOffsetY = 1.15,
			velocity = 35,
			radius = 0.1,
			hitEffect = "minorvirgespell_blast",
		},
	},
}

defineObject{
	name = "minorvirgespell_blast",
	baseObject = "base_spell",
	components = {
		{
			class = "Particle",
			particleSystem = "arcane_blast",
		},
		{
			class = "Light",
			color = vec(1, 0.6, 1),
			brightness = 10,
			range = 7,
			fadeOut = 0.5,
			disableSelf = true,
		},
		{
			class = "Sound",
			sound = "blob_hit",
			onInit = function(self) self:setPitch(1.2+math.random()*0.6) end,
		},
		{
			class = "TileDamager",
			attackPower = 1100,
			damageType = "pure",
			sound = "blob_hit",
			onInit = function(self)
			-- Award experience.
			self:setDamageFlags(DamageFlags.Champion1)
			end,
		},
	},
}
particle system:
SpoilerShow

Code: Select all

defineParticleSystem{
  name = "arcane_bolt",
  emitters = {
    {
      emissionRate = 256,
      emissionTime = 0,
      maxParticles = 32,
      sprayAngle = {0,360},
      velocity = {0,1},
      objectSpace = false,
      texture = "assets/textures/particles/glitter_silver.tga",
      lifetime = {0.5,1},
      color0 = {3,1,1},
      opacity = 1,
      fadeIn = 0.1,
      fadeOut = 0.1,
      size = {0.1, 1.0},
      gravity = {0,0,0},
      airResistance = 5,
      rotationSpeed = 5,
      blendMode = "Additive",
    },
    {
      emissionRate = 256,
      emissionTime = 0,
      maxParticles = 32,
      sprayAngle = {0,360},
      velocity = {0,1},
      objectSpace = false,
      texture = "assets/textures/particles/glitter_silver.tga",
      lifetime = {0.5,1},
      color0 = {1,1,3},
      opacity = 1,
      fadeIn = 0.1,
      fadeOut = 0.1,
      size = {0.1, 1.0},
      gravity = {0,0,0},
      airResistance = 5,
      rotationSpeed = 5,
      blendMode = "Additive",
    },
    {
      spawnBurst = true,
      maxParticles = 1,
      sprayAngle = {0,0},
      velocity = {0,0},
      objectSpace = true,
      texture = "assets/textures/particles/glow.tga",
      lifetime = {1000000,1000000},
      color0 = {0.25,0.2,0.25},
      opacity = 1,
      fadeIn = 0.001,
      fadeOut = 0.001,
      size = {1.0, 1.0},
      gravity = {0,0,0},
      airResistance = 1,
      rotationSpeed = 0,
      blendMode = "Additive",
    },
  }
}
Blast particle:
SpoilerShow

Code: Select all

defineParticleSystem{
  name = "arcane_blast",
  emitters = {
    {
      spawnBurst = true,
      maxParticles = 128,
      sprayAngle = {0,360},
      velocity = {2,5},
      objectSpace = false,
      texture = "assets/textures/particles/glitter_silver.tga",
      lifetime = {0.6,1},
      color0 = {3.0,3.0,3.0},
      opacity = 1,
      fadeIn = 0.1,
      fadeOut = 0.5,
      size = {0.16, 0.8},
      gravity = {0,0,0},
      airResistance = 4,
      rotationSpeed = 2,
      blendMode = "Additive",
    },
    {
      spawnBurst = true,
      maxParticles = 128,
      sprayAngle = {0,360},
      velocity = {2,5},
      objectSpace = false,
      texture = "assets/textures/particles/glitter_silver.tga",
      lifetime = {0.6,1},
      color0 = {3.0,1.8,3.0},
      opacity = 1,
      fadeIn = 0.1,
      fadeOut = 0.5,
      size = {0.16, 0.8},
      gravity = {0,0,0},
      airResistance = 4,
      rotationSpeed = -2,
      blendMode = "Additive",
    },
    {
      spawnBurst = true,
      maxParticles = 1,
      sprayAngle = {0,0},
      velocity = {0,0},
      objectSpace = false,
      texture = "assets/textures/particles/glow.tga",
      lifetime = {1,1},
      color0 = {1.0,0.6,1.0},
      opacity = 1,
      fadeIn = 0.1,
      fadeOut = 0.9,
      size = {7.2, 7.2},
      gravity = {0,0,0},
      airResistance = 1,
      rotationSpeed = 0,
      blendMode = "Additive",
    },
  }
}
I'm askink because someone reported to me that an object is spawned and stay after blast, while using a script in the console at the impact point. So maybe I did something wrong while creating that spell.
(The script he used)

Code: Select all

for i in party.map:entitiesAt(party.x,party.y) do print(i.name,i.id) end
Also, does it create a conflict if you use the same name for a particle and an object?
My asset pack: viewtopic.php?f=22&t=9320

Log1 mod : Toorum Manor: viewtopic.php?f=14&t=5505
minmay
Posts: 2768
Joined: Mon Sep 23, 2013 2:24 am

Re: Ask a simple question, get a simple answer

Post by minmay »

minorvirgespell_blast will stay in the dungeon forever, yes. You should add "destroyObject = true" to the ParticleComponent.
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.
bongobeat
Posts: 1076
Joined: Thu May 16, 2013 5:58 pm
Location: France

Re: Ask a simple question, get a simple answer

Post by bongobeat »

ok, thank you!

Then, would it be ok to add the destroyObject for the light or particle too?
like in this one?

destroyObject is disabled there, this is a custom spell that use a copy of the wallfire in the asset pack.
SpoilerShow

Code: Select all

defineObject{
	name = "wallOffire",
	baseObject = "base_spell",
	components = {
		{
			class = "Model",
			model = "assets/models/effects/wall_fire.fbx",
			sortOffset = 1,
		},
		{
			class = "Animation",
			animations = {
				idle = "assets/animations/effects/wall_fire.fbx",
			},
			playOnInit = "idle",
			onAnimationEvent = function(self, event)
				if event == "end" then
					self.go.particle:stop()
					self.go.particle2:stop()
					self.go.particle:fadeOut(1)
					self.go.particle2:fadeOut(1)
					self.go.light:fadeOut(1)
				end
			end,
		},
		{
			class = "Particle",
			particleSystem = "wall_fire_smoke",
			offset = vec(0, 0, 0),
			sortOffset = 3,
			--destroyObject = true,
		},
		{
			class = "Particle",
			name = "particle2",
			particleSystem = "wall_fire",
			offset = vec(0, 0, 0),
			--destroyObject = true,
		},
		{
			class = "Particle",
			name = "impact",
			particleSystem = "wall_fire_impact",
			offset = vec(0, 0, 0),
			sortOffset = 3,
			--destroyObject = true,
		},
		{
			class = "Light",
			color = vec(0.75, 0.4, 0.25),
			brightness = 40,
			range = 6,
			offset = vec(0, 1.2, 0),
			--fadeOut = 0.75,
			disableSelf = true,
		},
		{
			class = "TileDamager",
			attackPower = 27,
			damageType = "fire",
			repeatCount = 5,
			repeatDelay = 4.3/5,
			onInit = function(self)
			-- Award experience.
			self:setDamageFlags(DamageFlags.Champion1)
			end
		},
	},
}
Again, the same person reported, while checking fot that minor wizard virge blast:
SpoilerShow

Code: Select all

fireball_blast_large
poison_bolt_blast
Those comes from goromorg, I've checked in my definitions, they use the standard spells in the asset pack, except for a variation of a monster using a custom spell, which does not happen in that check.

I've lookd in the asset pack, but both fireball large and poison bolt blasts got the destroyObject to true.
Don't know how to fix that, as nothing seems wrong in the definition.

That's the goromorg ranged attack:
SpoilerShow

Code: Select all

		{
			class = "MonsterAttack",
			name = "rangedAttack",
			attackPower = 100,
			cooldown = 2,
			animation = "attack",
			sound = "goromorg_attack",
			onAttack = function(self)
				local h = 1.4
				
				local r = math.random()				
				if r < 0.45 then
					-- cast fireball
					self.go.monster:shootProjectile("fireball_large", h, 100)
				elseif r < 0.7 then
					-- cast lightning bolt
					self.go.monster:shootProjectile("lightning_bolt_greater", h, 100)
				elseif r < 0.8 then
					-- cast poison bolt
					self.go.monster:shootProjectile("poison_bolt", h, 100)
				else
					-- cast ice shards
					local dx,dy = getForward(self.go.facing)
					local spell = spawn("ice_shards", self.go.level, self.go.x + dx, self.go.y + dy, self.go.facing, self.go.elevation)
					spell.tiledamager:setAttackPower(100)
				end
			end,
		},
My asset pack: viewtopic.php?f=22&t=9320

Log1 mod : Toorum Manor: viewtopic.php?f=14&t=5505
minmay
Posts: 2768
Joined: Mon Sep 23, 2013 2:24 am

Re: Ask a simple question, get a simple answer

Post by minmay »

bongobeat wrote: Sun Mar 26, 2023 12:15 pmThen, would it be ok to add the destroyObject for the light or particle too?
Yes, LightComponent and ParticleComponent both have a destroyObject property, and it's fine to use it on multiple components on the same object (although it rarely makes any sense to put it on more than one of an object's components).

On ParticleComponent it destroys the parent object if, when it updates, it is enabled and either its particle system has finished or it has been fully faded out (by the fadeOut property or setFadeOut() method).
On LightComponent it destroys the parent object if, when it updates, it is enabled and either its brightness is 0 or it has been fully faded out.

Of course, both of these will still result in an object that stays around forever if the particle system never finishes (due to any of its emitters having an emissionTime or lifetime of 0 or very long), or the LightComponent never goes to 0 brightness or fully fades out, respectively.



Related enough to mention: ParticleComponent, but not LightComponent, has a destroySelf property, which is like destroyObject except it only destroys the ParticleComponent itself and doesn't affect the rest of the parent object. This is rarely useful, but it's there.
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.
bongobeat
Posts: 1076
Joined: Thu May 16, 2013 5:58 pm
Location: France

Re: Ask a simple question, get a simple answer

Post by bongobeat »

minmay wrote: Sun Mar 26, 2023 9:24 pm
bongobeat wrote: Sun Mar 26, 2023 12:15 pmThen, would it be ok to add the destroyObject for the light or particle too?
Yes, LightComponent and ParticleComponent both have a destroyObject property, and it's fine to use it on multiple components on the same object (although it rarely makes any sense to put it on more than one of an object's components).

On ParticleComponent it destroys the parent object if, when it updates, it is enabled and either its particle system has finished or it has been fully faded out (by the fadeOut property or setFadeOut() method).
On LightComponent it destroys the parent object if, when it updates, it is enabled and either its brightness is 0 or it has been fully faded out.

Of course, both of these will still result in an object that stays around forever if the particle system never finishes (due to any of its emitters having an emissionTime or lifetime of 0 or very long), or the LightComponent never goes to 0 brightness or fully fades out, respectively.



Related enough to mention: ParticleComponent, but not LightComponent, has a destroySelf property, which is like destroyObject except it only destroys the ParticleComponent itself and doesn't affect the rest of the parent object. This is rarely useful, but it's there.
Sorry, but you're confusing me. From what I understand, you said it is ok to add the destroyObject to particle or light, but it is not necessary, if one of the component already got it, assuming that particle or light has no unlimited living time.

Do I understand it right?
My asset pack: viewtopic.php?f=22&t=9320

Log1 mod : Toorum Manor: viewtopic.php?f=14&t=5505
bongobeat
Posts: 1076
Joined: Thu May 16, 2013 5:58 pm
Location: France

Re: Ask a simple question, get a simple answer

Post by bongobeat »

Hey there, again one silly question:

is that script ok?
SpoilerShow

Code: Select all

    combination = 11222121211121
    comb = {}
    count = 0
    solved = false

    function buttonCombination(self)
       if solved == false then
          count = count + 1
          for i = 1,2 do
             if string.match(self.go.id, i) then
                comb[count] = i
                local order = table.concat(comb)
                local s, b = string.find(combination, order)
                if s == 1 and b == count and count < string.len(combination) then
                elseif s == 1 and b == string.len(combination) then
                   force_field_94.controller:deactivate()
					playSound("force_field_cast")
                   solved = true
                else
                   combinationReset()
                end
             end
          end
       end
    end

    function combinationReset()
--       playSound("lock_incorrect")
		playSound("no_sound")
       for i = 1,string.len(combination) do
          comb[i] = nil
       end
       count = 0
    end
it is in a single script file, without anything else, and linked to 2 buttons named mkbforge1 and mkbforge2

I'm asking because some players seems to have trouble with that, if the right combination is not found at first.
Maybe I have done something wrong here. This is just copy paste and modify from another script that someone made for me.
My asset pack: viewtopic.php?f=22&t=9320

Log1 mod : Toorum Manor: viewtopic.php?f=14&t=5505
User avatar
Zo Kath Ra
Posts: 931
Joined: Sat Apr 21, 2012 9:57 am
Location: Germany

Re: Ask a simple question, get a simple answer

Post by Zo Kath Ra »

bongobeat wrote: Thu Apr 06, 2023 10:33 am it is in a single script file, without anything else, and linked to 2 buttons named mkbforge1 and mkbforge2

I'm asking because some players seems to have trouble with that, if the right combination is not found at first.
Maybe I have done something wrong here. This is just copy paste and modify from another script that someone made for me.
How does the puzzle work?

And how does the script work?
(if you can't answer this question, then that's the problem)

And what "trouble" are the players having?
It helps to be precise when reporting bugs...
kelly1111
Posts: 349
Joined: Sun Jan 20, 2013 6:28 pm

Re: Ask a simple question, get a simple answer

Post by kelly1111 »

Just testing something in chatgtp ...

Code: Select all

local combination = "11222121211121"
local count = 0
local solved = false
local buttons = {"1", "2"}

function buttonCombination(self)
   if solved then return end
   for i = 1, #buttons do
      if self.go.id:find(buttons[i]) then
         count = count + 1
         local order = self.go.id:gsub(buttons[i], count)
         if combination:find(order, 1, true) == 1 and #order < #combination then
            return
         elseif combination == order then
            force_field_94.controller:deactivate()
            playSound("force_field_cast")
            solved = true
            return
         end
         combinationReset()
      end
   end
end

function combinationReset()
--   playSound("lock_incorrect")
    playSound("no_sound")
   count = 0
end
It answered:

First, the variable combination is assigned a string of numbers which represents a combination to be solved. Then, there are three global variables: count to keep track of the number of buttons pressed, solved to check if the combination has been solved, and buttons to store the possible values of the buttons.

The function buttonCombination is called when a button is pressed. It first checks if the combination has already been solved, and if so, returns immediately. It then loops through the buttons table to find which button was pressed. If the button was pressed, count is incremented, and the order variable is created by replacing the button value in the button's id with the current count.

If order is a substring of combination and is not equal to combination, the function returns immediately without doing anything else. If order is equal to combination, it deactivates a controller and plays a sound, sets solved to true, and returns. Otherwise, it calls combinationReset function to reset count.

The combinationReset function resets the count variable to 0.

Overall, this code checks if the button presses match a specific combination and activates the corresponding action if successful.

And on the question: what happens when the player does not find the right combination right away in this script? it answered

If the player does not find the right combination right away, the combinationReset() function is called. This function resets the count variable to 0, effectively starting over the combination guessing process. Additionally, it plays a sound (either "lock_incorrect" or "no_sound" depending on which one is commented out), indicating to the player that they have made a mistake. The player can then try again to find the correct combination. This process continues until the player enters the correct combination or chooses to give up.
kelly1111
Posts: 349
Joined: Sun Jan 20, 2013 6:28 pm

Re: Ask a simple question, get a simple answer

Post by kelly1111 »

Offtopic: I wonder what would happen if you paste a complete dungeon.lua into chatgpt and ask it to tell a story from it ?
Post Reply