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!
User avatar
Isaac
Posts: 3172
Joined: Fri Mar 02, 2012 10:02 pm

Re: Ask a simple question, get a simple answer

Post by Isaac »

Here is an alternate holy_water_flask. It is not a tile-damager, and affects single monsters, one at a time; even in groups. It is possible to miss with these. It could certainly use a custom model and icon; it presently uses default assets.

Code: Select all

defineObject{
	name = "holy_water_flask",
	baseObject = "flask",
	components = {
		{
			class = "Item",
			uiName = "Holy Water Flask",
			gameEffect = "Burns the undead",
			description = "A flask of blessed water.",
			gfxIndex = 150,
			impactSound = "impact_blunt",
			stackable = true,
			projectileRotationSpeed = 15,
			projectileRotationZ = -50,
			projectileRotationY = 40,
			weight = 0.5,
			stackable = true,
			traits = { "throwing_weapon" },
			ConvertToItemOnImpact = "holy_water_spray",
			onThrowAttackHitMonster = function(self, monster)
										monster.go:playSound("water_hit_small")
										if  monster.go.monster:hasTrait('undead') and not monster.go.burningmonster then
											monster.go:createComponent('BurningMonster')
											monster.go.poisonedParticle:setParticleSystem("devine_flames")
											monster.go:playSound("ice_hit")
											monster.go.burningmonster:setCausedByChampion(math.random(4))
											--[[ So I had an elaborate system in place to detect which champion
												 tossed the holy water flask; for the XP award after the monster dies.

												 But then I discover that the game apparently doesn't care (!?); it splits
												 the XP between the party members regardless of who caused the damage.
												So what is the purpose of :setCausedByChampion? (Because it's not Boolean)
											--]]
										end
									end	
		},
		{
			class = "ThrowAttack",
			attackPower = 2,
			cooldown = 4,
		},
	},
	tags = { "weapon", "weapon_throwing" },
}

defineObject{
	name = "holy_water_spray",
	baseObject = "base_spell",
	components = {
		{
			class = "Particle",
			particleSystem = "hit_ice",
			offset = vec(0, 1.5, 0),
		},	
	},
}

defineParticleSystem{
	name = "devine_flames",
	emitters = {
		{
			emitterShape = "MeshShape",
			emissionRate = 50,
			emissionTime = 0,
			maxParticles = 50,
			sprayAngle = {0,360},
			velocity = {0.1*2,0.5*2},
			texture = "assets/textures/particles/smoke_01.tga",
			lifetime = {0.5,0.7},
			color = {0.15, 0.15, 0.15},
			opacity = 1,
			fadeIn = 0.1,
			fadeOut = 0.5,
			size = {0.5, 1.3},
			gravity = {0,2,0},
			airResistance = 0.05,
			rotationSpeed = 0.6,
			blendMode = "Translucent",
			depthBias = 0.1,
		},
		{
			-- flames
			emitterShape = "MeshShape",
			maxParticles = 400,
			emissionRate = 600,
			emissionTime = 0,
			sprayAngle = {0,30},
			velocity = {0.0, 0.0},
			texture = "assets/textures/particles/flame.tga",
			frameRate = 40,
			frameSize = 32,
			frameCount = 40,
			lifetime = {0.4, 0.7},
			colorAnimation = true,
			color0 = {.2, .2, 2.0},
			color1 = {0.2, 0.2, 2.0},
			color2 = {0.3, 0.5, 1.25},
			color3 = {0.1, 0.1, 2},
			fadeIn = 0.01,
			fadeOut = 0.1,
			size = {0.021*2, 0.12*3},
			gravity = {0,3,0},
			airResistance = 1,
			rotationSpeed = 0,
			blendMode = "Additive",
			objectSpace = true,
		},
		{
			-- small flames
			emitterShape = "MeshShape",
			maxParticles = 400,
			emissionRate = 500,
			emissionTime = 0,
			sprayAngle = {0,30},
			velocity = {0.0, 0.0},
			texture = "assets/textures/particles/flame.tga",
			frameRate = 40,
			frameSize = 32,
			frameCount = 40,
			lifetime = {0.2, 0.4},
			colorAnimation = true,
			color0 = {.2, .2, 2.0},
			color1 = {0.2, 0.2, 2.0},
			color2 = {0.3, 0.5, 1.25},
			color3 = {0.1, 0.1, 2},
			fadeIn = 0.01,
			fadeOut = 0.1,
			size = {0.021*2*0.5, 0.12*3*0.7},
			gravity = {0,4,0},
			airResistance = 1,
			rotationSpeed = 0,
			blendMode = "Additive",
		},
	}
}
User avatar
zimberzimber
Posts: 432
Joined: Fri Feb 08, 2013 8:06 pm

Re: Ask a simple question, get a simple answer

Post by zimberzimber »

bongobeat wrote:Hey there,
I'm not sure that I understand this corectly

You mean that if I throw one bomb (with the code of Akroma) on a monster groupe, it will spawn a fireburst for every monster that is part of the monstergroup?
Is that not the same thing when you throw a fire bomb or any other bomb on a monster group?
Normal bombs just spawn a tile damager where they land.
Akromas code spawns a tile damage for every monster on the tile.

But you don't have to use my method, you can just have onExplode spawn the tile damaging object on its location as it happens when the bomb would explode. (I forgot you can do that)
But don't forget setting the bomb type to nil so you don't spawn an extra effect on top of that.
It will print an error message to console every time though, but you can disable console error prints when you export the dungeon.
Isaac wrote:So I had an elaborate system in place to detect which champion
tossed the holy water flask; for the XP award after the monster dies.
But then I discover that the game apparently doesn't care (!?); it splits
the XP between the party members regardless of who caused the damage.
So what is the purpose of :setCausedByChampion? (Because it's not Boolean)
IIRC its a remnant from LoG1 where it did matter who delivers the killing blow.
My asset pack [v1.10]
Features a bit of everything! :D
User avatar
akroma222
Posts: 1029
Joined: Thu Oct 04, 2012 10:08 am

Re: Ask a simple question, get a simple answer

Post by akroma222 »

zimberzimber wrote:Akromas code spawns a tile damage for every monster on the tile.
Bad Akroma! :cry:

Place a script entity in your dungeon called 'combatTempScript' with (Zimber's) damageMonster code:
SpoilerShow

Code: Select all

function damageMonster(monster, baseDamage, playerSource, type)
   if not monster:isAlive() then return end
   
   local h = monster:getHealth()
   local mh = monster:getMaxHealth()
   local resist = monster:getResistance(type)
   local damage = baseDamage
   local color = "ffffff"
   
   -- Check for resistances and apply the right damage value/text color.
   if resist == "absorb" then
      healMonster(monster, baseDamage, false)
      return
   elseif resist == "immune" then
      damage = 0
   elseif resist == "resist" then
      damage = baseDamage * 0.5
   elseif resist == "weak" then
      damage = baseDamage * 1.5
      damageColor = "ff0000"
   elseif resist == "vulnerable" then
      damage = baseDamage * 2
      damageColor = "ff0000"
   end
   
   -- Take protection into account if damage is physical
   -- Not really sure if this comes before or after resistance...
   if type == "physical" then
      local protection = monster:getProtection()
      damage = math.max(1, damage - math.floor((math.random() + 0.5) * protection ))
   end
   
   -- Turn the number whole
   damage = roundNumber(damage)
   
   -- No reason to continue the function if damage is 0 or negative
   if damage <= 0 then monster:showDamageText("0", color) return end
   
   -- Apply the damage/heal, print damage text, kill monster if its health will drop below 0, and grant xp if monster died and playerSource was true
  
   if h - damage > 0 then
      monster:showDamageText(""..damage, color)
      monster:setHealth(h - damage)
      monster.go:playSound(monster:getHitSound())
   else
      if playerSource then
        monster:showDamageText("+"..monster:getExp().." xp", "ffeb3f")
         
         for i = 1,4 do
            local champion = party.party:getChampion(i)
            if champion and champion:isAlive() then
              champion:gainExp(monster:getExp())
               
			end 
		end
	 end
      monster:showDamageText(""..h, color)
      monster:die()
   end
end










--------------------------------roundNumber(number)
--Returns a rounded version of the recieved number (1.3 -> 1 | 2.6 -> 3)

function roundNumber(number)
	local rounded = number
	local max = math.ceil(number)
	local min = math.floor(number)
	if math.abs(number - max) < math.abs(number - min) then
		rounded = math.ceil(number)
	else
		rounded = math.floor(number)
	end
	return rounded
end
And try this definition for the HolyWater - its a combined job (Isaac, Zimber, Bongo, Akroma)
SpoilerShow

Code: Select all

defineObject{
   name = "akr_combined_holywater",
   baseObject = "fire_bomb",
   components = {
      {
         class = "Item",
         uiName = "Holy Water",
         gfxIndex = 136,
         impactSound = "impact_blunt",
         stackable = true,
         projectileRotationSpeed = 10,
         projectileRotationZ = -30,
         weight = 1,
         traits = { "throwing_weapon", "bomb" },
      },
      {
         class = "ThrowAttack",
         gameEffect = "............",
         cooldown = 4,
      },
      {
         class = "BombItem",
         onExplode = function(self, level, x, y, facing, elevation)
            
            --print(self:getName(), level, x, y, facing, elevation)
           local undeadMon = false
			
            for entity in Dungeon.getMap(level):entitiesAt(x, y) do       
               
				if entity and entity.monster then
                  
					local entMon = entity.monster
				  
					if entMon:hasTrait("undead") then
                  
						if not undeadMon then
							entMon.go:playSound("water_hit_small")
							undeadMon = true
						end
						print(entity.name)
						 
						entMon.go:createComponent('BurningMonster')
						entMon.go.poisonedParticle:setParticleSystem("devine_flames")
						entMon.go:playSound("frostbolt_hit")
						entMon.go.burningmonster:setCausedByChampion(math.random(4))
						entMon:showDamageText("Burn!", "FF0000")
						 
						local partSys = spawn("particle_system", level, x, y, facing, elevation)
						partSys.particle:setParticleSystem("fireburst")
						partSys.particle:setOffset(vec(0,0,0))
					  
						local power = 35 * self.go.item:getStackSize()
						combatTempScript.script.damageMonster(entMon, rollDamage(power), true, "pure")
					end
               end
            end
				
        end,
         
      },
      
   },
}



defineObject{
   name = "holy_water_spray",
   baseObject = "base_spell",
   components = {
      {
         class = "Particle",
         particleSystem = "hit_ice",
         offset = vec(0, 1.5, 0),
      },   
   },
}

defineParticleSystem{
   name = "devine_flames",
   emitters = {
      {
         emitterShape = "MeshShape",
         emissionRate = 50,
         emissionTime = 0,
         maxParticles = 50,
         sprayAngle = {0,360},
         velocity = {0.1*2,0.5*2},
         texture = "assets/textures/particles/smoke_01.tga",
         lifetime = {0.5,0.7},
         color = {0.15, 0.15, 0.15},
         opacity = 1,
         fadeIn = 0.1,
         fadeOut = 0.5,
         size = {0.5, 1.3},
         gravity = {0,2,0},
         airResistance = 0.05,
         rotationSpeed = 0.6,
         blendMode = "Translucent",
         depthBias = 0.1,
      },
      {
         -- flames
         emitterShape = "MeshShape",
         maxParticles = 400,
         emissionRate = 600,
         emissionTime = 0,
         sprayAngle = {0,30},
         velocity = {0.0, 0.0},
         texture = "assets/textures/particles/flame.tga",
         frameRate = 40,
         frameSize = 32,
         frameCount = 40,
         lifetime = {0.4, 0.7},
         colorAnimation = true,
         color0 = {.2, .2, 2.0},
         color1 = {0.2, 0.2, 2.0},
         color2 = {0.3, 0.5, 1.25},
         color3 = {0.1, 0.1, 2},
         fadeIn = 0.01,
         fadeOut = 0.1,
         size = {0.021*2, 0.12*3},
         gravity = {0,3,0},
         airResistance = 1,
         rotationSpeed = 0,
         blendMode = "Additive",
         objectSpace = true,
      },
      {
         -- small flames
         emitterShape = "MeshShape",
         maxParticles = 400,
         emissionRate = 500,
         emissionTime = 0,
         sprayAngle = {0,30},
         velocity = {0.0, 0.0},
         texture = "assets/textures/particles/flame.tga",
         frameRate = 40,
         frameSize = 32,
         frameCount = 40,
         lifetime = {0.2, 0.4},
         colorAnimation = true,
         color0 = {.2, .2, 2.0},
         color1 = {0.2, 0.2, 2.0},
         color2 = {0.3, 0.5, 1.25},
         color3 = {0.1, 0.1, 2},
         fadeIn = 0.01,
         fadeOut = 0.1,
         size = {0.021*2*0.5, 0.12*3*0.7},
         gravity = {0,4,0},
         airResistance = 1,
         rotationSpeed = 0,
         blendMode = "Additive",
      },
   }
}
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 »

I just had a look this afternoon at both Isaac and Akroma's new versions. Thanks for that! Sorry, I didn't notice that you posted messages here! I thought that this was resolved!

About the fireburst, well ok, I understand now.

Well I'd rather prefer to divise the damage by 2 in my actual definition to limit this "bug" effect, instead of adding a new kind of script, if my version didn't make any crash or error, that's ok for me. This "bomb" is not really "useful". I mean I've only made it to illustrate a part of the story in my mod.
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 »

I got a very simple question :

can I add minimalSaveState = true, to any object that has a ladder component?
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 »

Yes, LadderComponent is fine to use on minimalSaveState objects.
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!
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 »

Another "silly" question :mrgreen:

is that possible to define a special wall texts with the minimalSaveState = true?

I mean, to add some text directly in the definition of the objet, and add the minimalthings that it will not be counted in the savegame

e.g: I have a wall text that say "danger" (or anything else), then define an object who got always the "danger" text.
My asset pack: viewtopic.php?f=22&t=9320

Log1 mod : Toorum Manor: viewtopic.php?f=14&t=5505
User avatar
zimberzimber
Posts: 432
Joined: Fri Feb 08, 2013 8:06 pm

Re: Ask a simple question, get a simple answer

Post by zimberzimber »

You can by having the text components init function set its text to it
My asset pack [v1.10]
Features a bit of everything! :D
minmay
Posts: 2768
Joined: Mon Sep 23, 2013 2:24 am

Re: Ask a simple question, get a simple answer

Post by minmay »

Code: Select all

defineObject{
  name = "danger_sign",
  baseObject = "base_wall_text",
  components = {
    {
      class = "Model",
      model = "mod_assets/models/danger_sign.fbx",
      staticShadow = true,
    },
    {
      class = "WallText",
      wallText = "Danger!",
    },
  }
  minimalSaveState = true,
}
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.
Post Reply