[SCRIPT] Goromorg Shield (v1.1 update)

Talk about creating Grimrock 1 levels and mods here. Warning: forum contains spoilers!
User avatar
Diarmuid
Posts: 807
Joined: Thu Nov 22, 2012 6:59 am
Location: Montreal, Canada
Contact:

[SCRIPT] Goromorg Shield (v1.1 update)

Post by Diarmuid »

Hi everyone!

I've managed to recreate the goromorg shield functionality, it is updated now in the first post with visual effects and sound!)

Version 1.1 Update with moving shield!

- The shield effect now follows the monster if the monster is moving while hit.
- Sounds plays correctly at monster location
- Now works with monsters with no immunites (you just don't get the "0" text, just the shield visual)
- Script includes readme to help setup
- Optimized code

Here's the updated damageTracker script entity:
SpoilerShow

Code: Select all

--[[
Diarmuid's Goromorg Shield script, Version 1.1

Changelog:
1.1 Shield visual effect now moves along if monster moves
	Shield sound effect is now played at monster's location
	Simplified monster properties table
1.0 Initial release

Readme:
Add the following hooks to your monster(s):
	onDamage = function(self,damage,type)
		return damageTracker.shield(self,damage)
	end,
	
	onMove = function(self, direction)
		return damageTracker.createMoveTimer(self, direction)
	end,
	
	onDie = function(self)
		return damageTracker.clear(self)
	end,
	
Then define the monster(s) in monstersTable below using the following properties:

shield: sets how much damage the shield can take before breaking
immunity: "fire", "frost", "poison", "shock" or "none"
	(If elemental, you will get a "0" over the monster, if "none", just the shield visual)
shieldVAlign: adjusts the shield effect vertical alignment. Positive values go upwards.
shieldSize: adjusts the size of the shield effect. Values below 1 = smaller, above 1 = larger.
moveSpeed: adjusts the speed at which the shield moves with monster. Values below 1 = slower, above 1 = faster.
	
]]

damageTable = {}

timersTable = {}

monstersTable = {
	white_goromorg = {
		shield = 300,
		immunity = "cold",
		shieldVAlign = 0,
		shieldSize = 1,
		moveSpeed = 1,
	},
}


function shield(monster, damage)
	if damage > 0 then
		local shieldActive = false
		if damageTable[monster.id] == nil then
			damageTable[monster.id] = damage
			shieldActive = true
		elseif damageTable[monster.id] < monstersTable[monster.name].shield then
			damageTable[monster.id] = damageTable[monster.id] + damage
			shieldActive = true
		end
		if shieldActive == true then
			
			if monstersTable[monster.name].immunity ~= 'none' then
				damageTile(monster.level, monster.x, monster.y, monster.facing, 0, monstersTable[monster.name].immunity,0)
			end
			
			local fxId = monster.id..".shield"
			if findEntity(fxId) ~= nil then
				findEntity(fxId):destroy()
			end
			spawn("fx", monster.level, monster.x, monster.y, monster.facing,fxId)
			findEntity(fxId):setParticleSystem("shield_glow")
			local dx,dy = getForward(party.facing)
			local shieldSize = monstersTable[monster.name].shieldSize
			local shieldVAlign = monstersTable[monster.name].shieldVAlign
			findEntity(fxId):translate(-dx*0.6*shieldSize,shieldVAlign,dy*0.6*shieldSize)
			
			local timerId = monster.id..'.moveTimer'
			if findEntity(timerId) ~= nil then
				local mx, my = getForward(timersTable[timerId].direction)
				local moveSpeed = monstersTable[monster.name].moveSpeed
				local x, y = unpack(timersTable[timerId].position)
				findEntity(fxId):translate(mx*0.2*timersTable[timerId].tick*moveSpeed,0,-my*0.2*timersTable[timerId].tick*moveSpeed)
				if x ~= monster.x or y ~= monster.y then
					print("Translated")
					findEntity(fxId):translate(-mx*3,0,my*3)
				end
			end
			if damageTable[monster.id] < monstersTable[monster.name].shield then
				playSoundAt("goromorg_shield_hit", monster.level, monster.x, monster.y)
			else
				playSoundAt("goromorg_shield_break", monster.level, monster.x, monster.y)
			end
			
			return false
		end
		return damage
	end
end

function clear(monster)
	damageTable[monster.id] = 0
	local fxId = monster.id..".shield"
	if findEntity(fxId) ~= nil then
		findEntity(fxId):destroy()
	end
end

function createMoveTimer(monster, direction)
	local level, x, y = monster.level, monster.x, monster.y
	local timerId = monster.id..'.moveTimer'
	if findEntity(timerId) ~= nil then
		findEntity(timerId):destroy()
	end
	local moveTimer = spawn('timer', level, x, y, 0, timerId)
	moveTimer:addConnector('activate', 'damageTracker', 'moveTimer')
	moveTimer:setTimerInterval(0.05)
	moveTimer:activate()
	timersTable[timerId] = {}
	timersTable[timerId].id = monster.id
	timersTable[timerId].direction = direction
	timersTable[timerId].position = {x, y}
end

function moveTimer(self)
	timerId = self.id
	if timersTable[timerId].tick == nil then
		timersTable[timerId].tick = 0
	end
	timersTable[timerId].tick = timersTable[timerId].tick + 1
	fxId = timersTable[timerId].id..'.shield'
	if findEntity(fxId) ~= nil then
		local dx, dy = getForward(timersTable[timerId].direction)
		local monster = findEntity(timersTable[timerId].id)
		local moveSpeed = monstersTable[monster.name].moveSpeed
		findEntity(fxId):translate(dx*0.2*moveSpeed,0,-dy*0.2*moveSpeed)
	end
	if timersTable[timerId].tick ==  15 then
		timersTable[timerId].tick = 0 
		self:destroy()
	end
end
Usage:
  • shieldTable sets how much damage the shield can take before breaking
    damageType sets the type of elemental damage the monster is immune to (monster must have an immunity)
    shieldVAlign adjusts the shield effect vertical alignment. Positive values go upwards
    shieldSize adjusts the size of the shield effect. Values below 1 = smaller, above 1 = larger.
moveSpeed adjusts the speed at which the shield moves with monster. Values below 1 = slower, above 1 = faster.[/list]

VAlign, Size and moveSpeed have been tailored for a Goromorg, but can be adjusted for something else.

Setup:
add the following hooks to your monsters:
SpoilerShow

Code: Select all

	onDamage = function(self,damage,type)
		return damageTracker.shield(self,damage)
	end,
	
	onMove = function(self, direction)
		return damageTracker.createMoveTimer(self, direction)
	end,
	
	onDie = function(self)
		return damageTracker.clear(self)
	end,
add this to materials.lua or anywhere else you store your particles:
SpoilerShow

Code: Select all

defineParticleSystem{
	name = "shield_glow",
	emitters = {

		-- outer glow
		{
			spawnBurst = true,
			emissionRate = 1,
			emissionTime = 0,
			maxParticles = 1,
			boxMin = {0,1.1,0},
			boxMax = {0,1.1,0},
			sprayAngle = {0,0},
			velocity = {0,0},
			texture = "mod_assets/textures/particles/goromorg_shield_particle.tga",
			lifetime = {0.7, 0.7},
			colorAnimation = false,
			color0 = {2, 2, 2},
			opacity = 0.5,
			fadeIn = 0.1,
			fadeOut = 0.1,
			size = {2.6, 2.6},
			gravity = {0,0,0},
			airResistance = 1,
			rotationSpeed = 0,
			blendMode = "Additive",
			depthBias = -0.0005,
			objectSpace = true,
		}
	}
}
And here's the texture file (properly trimmed + added alpha) that should go in mod_assets\textures\particles\:
https://docs.google.com/open?id=0B-rVma ... Vc2MTVpYTA
Last edited by Diarmuid on Sun Dec 16, 2012 5:36 am, edited 13 times in total.
flatline

Re: Goromorg Shield

Post by flatline »

First thing I noticed is that you have "if damage > 1", shouldn't that be "if damage > 0" or possible "if damage >= 1"?

Regarding the shield, it should work with a white glow effect, onHit, centered on the central goromorg node. Sounds like the easiest part of it all, something like this:

if ("shield is still up=true") then
spawn("FX", monster.level, monster.x, monster.y, monster.facing)
:setParticleSystem("shieldglow")

and make shieldglow spawn a shortlived single glow particle, white, the size of the goromorg, opacity 0.1 or something like that.
User avatar
JKos
Posts: 464
Joined: Wed Sep 12, 2012 10:03 pm
Location: Finland
Contact:

Re: Goromorg Shield

Post by JKos »

Yeah, damage tile always does at least 1 damage, believe me I have tested all possible tricks, there is no way to make it do 0 damage (besides the immunity), I have also tried shootProjectile with 0 damage, it doesn't work either.

But I think you could make your monsters immune to cold and do the cold damage manually in onDamage hook using damageTile and poison type damage which does not have any visual effects. Or is the onDamage called if monster is immune to that damage type? Not sure about that.

ps. Nice to see that you use the framework :) dynamic hooks are pretty useful.
- LoG Framework 2http://sites.google.com/site/jkoslog2 Define hooks in runtime by entity.name or entity.id + multiple hooks support.
- cloneObject viewtopic.php?f=22&t=8450
User avatar
Diarmuid
Posts: 807
Joined: Thu Nov 22, 2012 6:59 am
Location: Montreal, Canada
Contact:

Re: Goromorg Shield

Post by Diarmuid »

Thanks for the cues.

Sorry about the > 1, indeed, it should be > 0, a typo.

I didn't look into FX/particles much yet (hey, I had never heard of lua and hadn't coded anything for 5 years a month ago, much ground to catch up), so that's why I askd if it was doable. I'll go make some tests.

JKos, unfortunately the onDamage gets called, but it is passed a 0 if the monster is immune. :( It was a good idea though, sadly.

PS: Your framework is awesome. As I said, I learned scripting as I was going along, and in the beginning my dungeon was poorly coded, with scripts all over the place... this week I took on cleaning and consolidating the mess, installed your framework am updating all my code in a more intelligent way. Thanks a lot!
flatline

Re: Goromorg Shield

Post by flatline »

Sorry if I made it sound trivial with the shield FX. I thought the part you did was the hard part (I don't know half of the framework thing yet, and I've never coded anything outside of modding). Setting up FX are easy, I can try out some settings for a Goromorg shield and report them here.
User avatar
Diarmuid
Posts: 807
Joined: Thu Nov 22, 2012 6:59 am
Location: Montreal, Canada
Contact:

Re: Goromorg Shield

Post by Diarmuid »

Ok, I managed to get it quite right I think! EDIT:now with sound ;) Updated in the first post.
Last edited by Diarmuid on Sat Dec 01, 2012 5:52 pm, edited 2 times in total.
flatline

Re: Goromorg Shield

Post by flatline »

Wow, great job! Impressive!
User avatar
Diarmuid
Posts: 807
Joined: Thu Nov 22, 2012 6:59 am
Location: Montreal, Canada
Contact:

Re: Goromorg Shield

Post by Diarmuid »

Oh, I just realized I forgot sound for shield_hit and shield_break, but it's past 3am so I'll update this tomorrow.

EDIT: It's updated now, it has sound effects and I fixed a little logic problem I overlooked in the if statements.
Last edited by Diarmuid on Fri Nov 30, 2012 4:19 pm, edited 1 time in total.
User avatar
Komag
Posts: 3658
Joined: Sat Jul 28, 2012 4:55 pm
Location: Boston, USA

Re: Goromorg Shield

Post by Komag »

I quite impressed with this as well, nice job! This was one of those things on the "hardcoded and impossible" list 8-) :lol:
Finished Dungeons - complete mods to play
User avatar
Diarmuid
Posts: 807
Joined: Thu Nov 22, 2012 6:59 am
Location: Montreal, Canada
Contact:

Re: Goromorg Shield

Post by Diarmuid »

Thanks guys, glad to help! It just didn't make sense in my dungeon that half the "goromorgs" would have a shield and the others not... so I set on finding a solution. ;)

Next, I'm taking care of the arrows problem using the same hooks/table indexed by monster.id scripting model.
Post Reply