Page 123 of 396

Re: Ask a simple question, get a simple answer

Posted: Wed Jun 08, 2016 3:57 am
by zimberzimber
After defining a few new spells I've noticed that the party isn't credited for the kill, as if the monster was killed by 'natural' means.
How do I make custom spells credit the kill to the party?

Another issue I have with spells is that when I tell them to spawn an entity, they do even if its inside/behind a wall. How do I make them check if the tile they are meant to spawn in is free, and if not them spawn it on the parties location? (Like Ice Shards do)

Here's an example of one of the spells:

Code: Select all

-- Firewall --
defineSpell{
	name = "fireWall",
	uiName = "Firewall",
	gesture = 14,
	manaCost = 25,
	skill = "fire_magic",
--	requirements = { "fire_magic", 1},
	icon = 60,
	spellIcon = 1,
	description = "Summons forth a fiery wall to incinerate your foes",
	
	onCast = function(champ, x, y, direction, skill)
		local dx,dy = getForward(party.facing)
		for i = 0, 10 do
			if findEntity("firewall_spell"..i) == nil then
				spawn("wall_fire", party.level, party.x+dx, party.y+dy, direction, party.elevation, "firewall_spell"..i)
				return
			end
		end
	end,
}

Re: Ask a simple question, get a simple answer

Posted: Wed Jun 08, 2016 5:06 pm
by Zo Kath Ra
You can use
http://www.grimrock.net/modding/scripting-reference/
Map:isWall(x, y)

Or, if the spell should only work on empty tiles with no monsters or crates:
Map:isBlocked(x, y, elevation)
Map:isObstacle(x, y, elevation)

Difference between isBlocked() and isObstacle()?
viewtopic.php?f=22&t=8130&p=82252&hilit ... ked#p82252

Code: Select all

    -- Firewall --
    defineSpell{
       name = "fireWall",
       uiName = "Firewall",
       gesture = 14,
       manaCost = 25,
       skill = "fire_magic",
    --   requirements = { "fire_magic", 1},
       icon = 60,
       spellIcon = 1,
       description = "Summons forth a fiery wall to incinerate your foes",
       
       onCast = function(champ, x, y, direction, skill)
          local dx,dy = getForward(party.facing)
          local x,y
                    
          if party.map:isWall(party.x + dx, party.y + dy) then
              x = party.x
              y = party.y
          else
              x = party.x + dx
              y = party.y + dy
          end
          
          for i = 0, 10 do
              if findEntity("firewall_spell"..i) == nil then
                  spawn("wall_fire", party.level, x, y, direction, party.elevation, "firewall_spell"..i)
                  return
              end
          end
       end,
    }

Re: Ask a simple question, get a simple answer

Posted: Wed Jun 08, 2016 7:05 pm
by zimberzimber
Problem with those is that they don't check for wall objects such as secret doors, which could lead to some nasty exploits.

Any idea on the spells crediting the player? I assume this has to do with damage flags, but I'm having issues understanding on how to implement them in custom spells.

Re: Ask a simple question, get a simple answer

Posted: Wed Jun 08, 2016 8:07 pm
by minmay
I hate to toot my own horn, but the MinAssets spells do everything you are asking about. Here is an example of how to implement a burst spell similar to Fireburst/Shockburst, which seems to be what you want:

Code: Select all

-- Disrupt: Burst spell. Severely damages undead, elementals, and constructs. Water 4.
-- Also destroys goromorg shields. 
defineSpell{
	name = "disrupt",
	uiName = "Disrupt",
	gesture = 4569,
	manaCost = 50,
	skill = "water_magic",
	requirements = { "water_magic", 4 },
	iconAtlas = "mod_assets/MinAssets/spells/spells_large.tga",
	icon = 0,
	spellIconAtlas = "mod_assets/MinAssets/spells/spells_small.tga",
	spellIcon = 0,
	description = "Disrupts magical fields directly in front of you. This shatters magical shields and severely damages undead, elementals, and constructs, but has no effect on living flesh.",
	onCast = function(champion,x,y,direction,elevation,skill)
		local nx = x
		local ny = y
		local obs = party.map:checkObstacle(party,direction)
		if (not obs) or obs == "dynamic_obstacle" or obs == "obstacle" then
			local dx,dy = getForward(direction)
			nx = x+dx
			ny = y+dy
		end
		local burst = spawn("disrupt_burst",party.level,nx,ny,direction,elevation)
		burst.tiledamager:setAttackPower(80*(1+skill*0.2)) -- meteor storm is 150 total attack power
		burst.tiledamager:setCastByChampion(champion:getOrdinal())
	end,
}
defineObject{
	name = "disrupt_burst",
	baseObject = "base_spell",
	components = {
		{
			class = "Particle",
			particleSystem = "disrupt",
			offset = vec(0, 1.2, 0),
			destroyObject = true,
		},
		{
			class = "Light",
			color = vec(0.8, 0.2, 1),
			brightness = 40,
			range = 4,
			offset = vec(0, 1.2, 0),
			fadeOut = 1.5,
			disableSelf = true,
		},
		{
			class = "TileDamager",
			attackPower = 80,
			damageType = "disruption", -- custom damage type that wont be affected by resistances
			sound = "dispel_hit",
			onHitMonster = function(self, monster)
				if monster:isAlive() then
					-- kill shields
					for _,c in monster.go:componentIterator() do
						if c:getClass() == "GoromorgShieldComponent" and c:getEnergy() > 0 then
							c:setEnergy(1)
							return true
						end
					end
					-- only damage undead, elementals, constructs
					return monster:hasTrait("undead") or monster:hasTrait("construct") or monster:hasTrait("elemental") or false
				end
			end,
			onHitChampion = function(self, champion)
				-- champions aren't undead, elementals, or constructs
				return false
			end,
			onHitObstacle = function(self, obstacle)
				-- obstacles aren't undead, elementals, or constructs either
				-- (well, I guess you could say barrels are constructs...not magical ones though)
				return false
			end,
		},
		{
			class = "Sound",
			sound = "light",
			pitch = 1.6,
		},
	},
}
I am not sure why you are spawning 11 wall_fire objects on the same square, however. Surely it would be better to just spawn 1 with more attack power.

Re: Ask a simple question, get a simple answer

Posted: Wed Jun 08, 2016 8:31 pm
by Isaac
So what are some of the valid monsterFlags?
(I haven't found any information on them or their uses.)

* .setMonsterFlag(string, boolean) .getMonsterFlag(string)

Re: Ask a simple question, get a simple answer

Posted: Wed Jun 08, 2016 8:39 pm
by zimberzimber
minmay wrote:I hate to toot my own horn, but the MinAssets spells do everything you are asking about. Here is an example of how to implement a burst spell similar to Fireburst/Shockburst, which seems to be what you want:
Looks good, will try it out once I have the chance to. So correct me if I'm wrong, but all I have to do is override some properties of the spawned object (based on what kind of object it is) through the defined spells onCast function?
minmay wrote:I am not sure why you are spawning 11 wall_fire objects on the same square, however. Surely it would be better to just spawn 1 with more attack power.
Not spawning 11 wall_fires, but preventing the game from creating two objects with the same ID. I'm pretty sure there's a better way to do that, but thats what I managed to come up with. I think you missed the 'return' after the spawn command.
Chose 11 for no particular reason, I just doubt someone would be able to create 11 wall_fires with a spell in 6 seconds.

Re: Ask a simple question, get a simple answer

Posted: Wed Jun 08, 2016 8:51 pm
by Isaac
zimberzimber wrote: Not spawning 11 wall_fires, but preventing the game from creating two objects with the same ID.
I don't think this is needed, just omitting the object name will have the game generate a safe one.

Code: Select all

-- Firewall --
defineSpell{
   name = "fireWall",
   uiName = "Firewall",
   gesture = 14,
   manaCost = 25,
   skill = "fire_magic",
--   requirements = { "fire_magic", 1},
   icon = 60,
   spellIcon = 1,
   description = "Summons forth a fiery wall to incinerate your foes",
   
   onCast = function(champ, x, y, direction, skill)
      local dx,dy = getForward(party.facing)
      
            spawn("wall_fire", party.level, party.x+dx, party.y+dy, direction, party.elevation)
         
   end,
}
With this version defined, I had three spell casters cast as many as they could at once, with no problems.

** Ah, or does this involve PC XP credit for kills (as per mentioned above)?

Re: Ask a simple question, get a simple answer

Posted: Wed Jun 08, 2016 9:08 pm
by minmay
Isaac wrote:So what are some of the valid monsterFlags?
(I haven't found any information on them or their uses.)

* .setMonsterFlag(string, boolean) .getMonsterFlag(string)
The following monster flags exist:
NonMaterial - Monster becomes intangible to melee attacks (even the "Miss" damageText is not produced) and TileDamagerComponents. The monster can still be hit by projectiles and telefragged/crushed, and still functions as an obstacle.
CantDie - Monster cannot die by any means, even telefrags and MonsterComponent:die(). The monster still takes damage.
Invulnerable - Monster cannot take damage. It can still be hit, but all hits will do 0 damage. The monster cannot be telefragged/crushed, but can still be killed by MonsterComponent:die().
Collides - Unlike the rest of the flags, true by default. If set to false, the monster's will no longer collide with projectiles. It still functions as an obstacle, gets hit by melee attacks, etc.
DamagedByChampion1, DamagedByChampion2, DamagedByChampion3, DamagedByChampion4 - Not used AFAIK. Presumably a holdover from Grimrock 1 where champions that didn't damage a monster only got half experience.

Re: Ask a simple question, get a simple answer

Posted: Wed Jun 08, 2016 9:16 pm
by zimberzimber
Isaac wrote:I don't think this is needed, just omitting the object name will have the game generate a safe one.
huh, good to know. So far any spell definition I've seen involved an object name at the end and I was sure its a must have. (hence the 'for' and the checking) Sadly spells (and brains) don't have their full definition in the asset packs.
Isaac wrote:** Ah, or does this involve PC XP credit for kills (as per mentioned above)?
The kill credit, yes. Because its a spawned tileDamager that kills the monster that has no link to the hero, the kill is counted as 'natural' (like a monster:die() command for example), therefore it doesn't give experience.
I've had a similar issue on an item that killed a monster if their health was below a certain threshold, and I didn't want extra numbers spamming the screen. With an item it was a lot easier for me to fix because hitting the monster is directly linked to the weapon, so I could just and run a champion:gainExp(monster:getExp())

Re: Ask a simple question, get a simple answer

Posted: Wed Jun 08, 2016 9:36 pm
by Isaac
You can tag the tile damager as coming from a specific PC like this:

Code: Select all

onCast = function(champ, x, y, direction, skill)
      local dx,dy = getForward(party.facing)
        
         spawn("wall_fire", party.level, party.x+dx, party.y+dy, direction, party.elevation).tiledamager:setCastByChampion(champ:getOrdinal())
            
   end,
________________

@minmay : Thank you for the concise information; it will be neat to try those out.