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!
Pompidom
Posts: 497
Joined: Sun May 06, 2018 9:42 pm

Re: Ask a simple question, get a simple answer

Post by Pompidom »

I figured it out, I just added the onDie in the monster class of the base trooper class so all different variations of the skeleton trooper model now drop a skeleton corpse on the spot where they perish :)

onDie = function(self)
spawn("dm_floordeco_corpse", self.go.level, self.go.x, self.go.y, self.go.facing, self.go.elevation)
end,

Code: Select all

monsters["skeleton_trooper"] = {
weapons = {
	skullcleave={model="assets/models/items/skeleton_knight_axe.fbx",wtype="axe",offset=vec(0.03,0.17,0.04),rotation=vec(90,0,270),attackPower=15,cooldown=4.5},
	ancient_claymore={model="assets/models/items/claymore.fbx",wtype="sword",offset=vec(0,0.45,-0.03),rotation=vec(270,0,270),attackPower=45,cooldown=5},
	backbiter={model="assets/models/items/backbiter.fbx",wtype="sword",offset=vec(0,0.1,0.03),rotation=vec(90,0,270),attackPower=15,cooldown=3,accuracy=5},
	baton={model="assets/models/items/baton.fbx",wtype="mace",offset=vec(0,0.2,0.06),rotation=vec(90,0,270),attackPower=10,cooldown=4.5},
	bone_blade={model="assets/models/items/bone_blade.fbx",wtype="sword",offset=vec(-0.02,0.1,0.05),rotation=vec(90,0,270),attackPower=25,accuracy=10,cooldown=3.4},
	bone_club={model="assets/models/items/bone_club.fbx",wtype="mace",offset=vec(-0.06,0.1,0.05),rotation=vec(50,-20,270),attackPower=4,cooldown=3},
	cudgel={model="assets/models/items/cudgel.fbx",wtype="mace",offset=vec(-0.08,0.14,0),rotation=vec(180,0,270),attackPower=12,cooldown=5},
	cutlass={model="assets/models/items/cutlass.fbx",wtype="sword",offset=vec(-0.01,0.32,0.03),rotation=vec(90,0,270),attackPower=24,cooldown=3.3},
	dagger={model="assets/models/items/dagger.fbx",wtype="sword",offset=vec(-0.01,0.08,0.03),rotation=vec(90,0,270),attackPower=7,accuracy=5,cooldown=2.5},
	g1_dismantler={model="assets/models/items/dismantler.fbx",wtype="sword",offset=vec(0,0.4,-0.03),rotation=vec(270,0,270),attackPower = 41,cooldown = 4,
		extraComponents = G1_ENHANCED_FX and {
			{
				class = "Light",
				parentNode = "weapon_right",
				offset = vec(0.2, 0.05, 0)+vec(0,0.4,-0.03),
				range = 2,
				color = vec(0.25,0.5,1),
				brightness = 30,
				castShadow = false,
				fillLight = true,
				onUpdate = function(self)
					self:setBrightness((math.noise(Time.currentTime()*13+123)*15+27))
				end,
			},
			{
				class = "Particle",
				parentNode = "weapon_right",
				particleSystem = "g1_dismantler",
				offset = vec(0,0.4,-0.03),
				rotation=vec(270,0,270),
				emitterMesh = "mod_assets/g1/items/Standard/dismantler_emitter.fbx",
			},
		} or nil,
		onAttackHit = function(self, champion)
			if math.random() < 0.25 then 
				local d = spawn("shockburst",party.level,party.x,party.y,party.facing,party.elevation).tiledamager
				d:setAttackPower(30)
			end
		end,
	},
	g1_gladius=G1_MINE_WEAPONS and {model="mod_assets/g1/items/Mine/Gladius.fbx",wtype="sword",offset=vec(0,0.3,-0.03),rotation=vec(270,0,270),attackPower=13,accuracy=5,cooldown=3.6} or nil,
	hand_axe={model="assets/models/items/hand_axe.fbx",wtype="axe",offset=vec(0.02,0.1,0.05),rotation=vec(90,0,270),attackPower=10,cooldown=4.5},
	long_sword={model="assets/models/items/long_sword.fbx",wtype="sword",offset=vec(0,0.4,-0.03),rotation=vec(270,0,270),attackPower=16,cooldown=3.7},
	machete={model="assets/models/items/machete.fbx",wtype="sword",offset=vec(0,0.32,0.03),rotation=vec(90,0,270),attackPower=12,cooldown=4.5},
	-- pierce compensation
	morning_star={model="assets/models/items/morning_star.fbx",wtype="mace",offset=vec(-0.01,0.25,-0.04),rotation=vec(270,0,270),attackPower=25,cooldown=4.5},
	throwing_axe={model="assets/models/items/throwing_axe.fbx",wtype="axe",offset=vec(0.08,0.25,0.05),rotation=vec(90,0,270),attackPower=5,cooldown=3.5},
	torch={model="assets/models/items/torch.fbx",wtype="mace",offset=vec(0,0.25,-0.03),rotation=vec(270,0,270),attackPower=4,cooldown=3,
		extraComponents = {
			{
				class = "Light",
				parentNode = "weapon_right",
				range = 9,
				offset = vec(0,0.65,0.03),
				brightness = 10,
				castShadow = true,
				onUpdate = function(self)
					local noise = math.noise(Time.currentTime()*3 + 123) * 0.5 + 0.9
					self:setBrightness(noise * 10)
				end,
			},
			{
				class = "Particle",
				parentNode = "weapon_right",
				particleSystem = "torch_dynamic",
				offset = vec(0,0.65,0.03),
				rotation = vec(0,0,-50),
			},
		},
	},
	warhammer={model="assets/models/items/warhammer.fbx",wtype="mace",offset=vec(-0.02,0.25,0.05),rotation=vec(90,0,90),attackPower=20,cooldown=4.5},
},

-- XXX: The axe animation has a little clipping with some of these because e.g. the heavy shield is so much larger than the kite shield the standard skeleton trooper has...
shields = {
	skeleton_knight_shield={model = "assets/models/items/skeleton_knight_shield.fbx",offset = vec(0,-0.15,0.07),rotation = vec(90,0,90),evasion=5},

	legionary_shield=G1_STANDARD_ITEMS and {model = "assets/models/items/skeleton_shield.fbx",offset = vec(0,-0.35,0.06),rotation = vec(100,0,270),evasion=5} or nil,
	heavy_shield={model = "mod_assets/g1/monsters/weapon_monsters/heavy_shield_strapless.fbx",offset = vec(0,-0.35,0.06),rotation = vec(100,0,270),evasion=6},
	g1_shield_courage=G1_SHIELDS and {model = "mod_assets/g1/monsters/weapon_monsters/heavy_shield_strapless.fbx",material="heavy_shield_lion",offset = vec(0,-0.35,0.06),rotation = vec(100,0,270),evasion=8} or nil,
	g1_shield_falcon=G1_SHIELDS and {model = "mod_assets/g1/monsters/weapon_monsters/heavy_shield_strapless.fbx",material="heavy_shield_falcon",offset = vec(0,-0.35,0.06),rotation = vec(100,0,270),evasion=12,cooldownMod=1.2} or nil,

	crystal_shield={model = "assets/models/items/crystal_shield.fbx",offset = vec(0.1,-0.2,0.1),rotation = vec(100,0,290),evasion=10},

	round_shield={model = "assets/models/items/small_shield.fbx",offset = vec(0,-0.08,-0.05),rotation = vec(270,0,270),evasion=4},
	g1_shield_winter=G1_SHIELDS and {model = "assets/models/items/small_shield.fbx",material="small_shield_blue",offset = vec(0,-0.08,-0.05),rotation = vec(270,0,270),evasion=6} or nil,
	g1_shield_dragon=G1_SHIELDS and {model = "assets/models/items/small_shield.fbx",material="small_shield_red",offset = vec(0,-0.08,-0.05),rotation = vec(270,0,270),evasion=6,hpMod=30} or nil,

	shield_elements={model = "assets/models/items/shield_elements.fbx",offset = vec(0.04,-0.3,0.1),rotation = vec(90,0,275),evasion=7},
	meteor_shield={model = "assets/models/items/meteor_shield.fbx",offset = vec(0.04,-0.1,0.1),rotation = vec(90,0,275),evasion=7},
	g1_shield_valor={model = "assets/models/items/shield_valor.fbx",offset = vec(0.04,-0.1,0.1),rotation = vec(90,0,275),evasion=10},
}
}

monsters.skeleton_trooper.create = function(weapon,shield,wname,sname,preferredName)

local comps ={
		{
			class = "Model",
			name = "weaponModel",
			parentNode = "weapon_right",
			model = weapon.model,
			offset = weapon.offset,
			rotation = weapon.rotation,
			material = weapon.material,
		},
		{
			class = "Model",
			name = "shieldModel",
			parentNode = "weapon_left",
			model = shield.model,
			offset = shield.offset,
			rotation = shield.rotation,
			material = shield.material,
		},
		{
			class = "Monster",
			meshName = "skeleton_knight_trooper_mesh",
			footstepSound = "skeleton_footstep",
			hitSound = "skeleton_hit",
			dieSound = "skeleton_die",
			hitEffect = "hit_dust",
			capsuleHeight = 0.7,
			capsuleRadius = 0.25,
			collisionRadius = 0.6,
			health = 150+(shield.hpMod or 0),
			immunities = { "poisoned", "sleep", "blinded", "frozen" },
			resistances = {
				["poison"] = "immune",
				["shock"] = "weak",
			},
			traits = { "undead" },
			evasion = SKELETON_TROOPER_BASE_EVASION+shield.evasion,
			protection = 5,
			lootDrop = { 0, wname, 0, sname },
		},
		{
			class = "MonsterAttack",
			name = "basicAttack",
			accuracy = weapon.accuracy,
			attackPower = SKELETON_TROOPER_BASE_ATTACK+weapon.attackPower,
			cooldown = (SKELETON_TROOPER_BASE_COOLDOWN+weapon.cooldown)*(shield.cooldownMod or 1),
			sound = "skeleton_trooper_attack",
			onBeginAction = onBeginActions[weapon.wtype],
			onAttackHit = weapon.onAttackHit,
		},
		{
			class = "MonsterAttack",
			name = "turnAttack",
			accuracy = weapon.accuracy,
			attackPower = SKELETON_TROOPER_BASE_ATTACK+weapon.attackPower,
			cooldown = (SKELETON_TROOPER_BASE_COOLDOWN+weapon.cooldown)*(shield.cooldownMod or 1),
			sound = "skeleton_trooper_attack",
			turnToAttackDirection = true,
			onAttackHit = weapon.onAttackHit,
		},
	}
if weapon.extraComponents then
	for _,c in pairs(weapon.extraComponents) do
		table.insert(comps,c)
	end
end
if shield.extraComponents then
	for _,c in pairs(shield.extraComponents) do
		table.insert(comps,c)
	end
end
defineObject{
	name = preferredName or string.format("skeleton_trooper_%s_%s",wname,sname),
	baseObject = "base_skeleton_trooper_custom_"..weapon.wtype,
	components = comps,
}

end


-- global
function g1_createWeaponMonster(montype,wname,sname,preferredName)
	if not (montype and wname and sname) then
		Console.warn("g1_createWeaponMonster called with invalid arguments (expected string,string,string), aborting")
		return false
	end
	local mon = monsters[montype]
	if not mon then
		Console.warn("g1_createWeaponMonster called with invalid monster type "..mon..", aborting")
		return false
	end
	local weapon = mon.weapons[wname]
	if not weapon then
		Console.warn("g1_createWeaponMonster called with invalid weapon "..wname..", aborting")
	end
	local shield = mon.shields[sname]
	if not shield then
		Console.warn("g1_createWeaponMonster called with invalid shield "..sname..", aborting")
	end
	if not (mon and weapon and shield) then
		return false
	end
	mon.create(weapon,shield,wname,sname,preferredName)
end


end
--------------------------------------------------------------------------------
defineParticleSystem{
	name = "torch_dynamic",
	emitters = {
		-- smoke
		{
			emissionRate = 5,
			emissionTime = 0,
			maxParticles = 50,
			boxMin = {-0.03, -0.03, -0.03},
			boxMax = { 0.03, 0.03,  0.03},
			sprayAngle = {0,30},
			velocity = {0.1,0.5},
			objectSpace = false,
			texture = "assets/textures/particles/smoke_01.tga",
			lifetime = {1,1.75},
			color0 = {0.15, 0.15, 0.15},
			opacity = 1,
			fadeIn = 0.5,
			fadeOut = 0.5,
			size = {0.3, 0.6},
			gravity = {0,0,0},
			airResistance = 0.1,
			rotationSpeed = 0.6,
			blendMode = "Translucent",
		},

		-- flames
		{
			emissionRate = 256,
			emissionTime = 0,
			maxParticles = 512,
			boxMin = {-0.03, -0.03, -0.03},
			boxMax = { 0.03, 0.03,  0.03},
			sprayAngle = {0,10},
			velocity = {0.2, 1},
			objectSpace = false,
			texture = "assets/textures/particles/torch_flame.tga",
			frameRate = 35,
			frameSize = 64,
			frameCount = 16,
			lifetime = {0.25, 0.85},
			colorAnimation = true,
			color0 = {2, 2, 2},
			color1 = {1.0, 1.0, 1.0},
			color2 = {1.0, 0.5, 0.25},
			color3 = {1.0, 0.3, 0.1},
			opacity = 1,
			fadeIn = 0.15,
			fadeOut = 0.3,
			size = {0.01, 0.08},
			gravity = {0,0,0},
			airResistance = 1.0,
			rotationSpeed = 1,
			blendMode = "Additive",
			depthBias = 0.1,
		},
		{
			emissionRate = 32,
			emissionTime = 0,
			maxParticles = 512,
			boxMin = {-0.05, -0.05, -0.05},
			boxMax = { 0.05, 0.05,  0.05},
			sprayAngle = {0,10},
			velocity = {0, 0},
			objectSpace = true,
			texture = "assets/textures/particles/torch_flame.tga",
			frameRate = 35,
			frameSize = 64,
			frameCount = 16,
			lifetime = {0.25, 0.85},
			colorAnimation = true,
			color0 = {2, 2, 2},
			color1 = {1.0, 1.0, 1.0},
			color2 = {1.0, 0.5, 0.25},
			color3 = {1.0, 0.3, 0.1},
			opacity = 1,
			fadeIn = 0.15,
			fadeOut = 0.3,
			size = {0.08, 0.15},
			gravity = {0,0,0},
			airResistance = 1.0,
			rotationSpeed = 1,
			blendMode = "Additive",
			depthBias = 0.3,
		},


		-- glow
		{
			spawnBurst = true,
			emissionRate = 1,
			emissionTime = 0,
			maxParticles = 1,
			boxMin = {0,0,0},
			boxMax = {0,0,0},
			sprayAngle = {0,30},
			velocity = {0,0},
			objectSpace = true,
			texture = "assets/textures/particles/glow.tga",
			lifetime = {1000000, 1000000},
			colorAnimation = false,
			color0 = {0.23, 0.11, 0.08},
			opacity = 1,
			fadeIn = 0.1,
			fadeOut = 0.1,
			size = {2, 2},
			gravity = {0,0,0},
			airResistance = 1,
			rotationSpeed = 0,
			blendMode = "Additive",
			depthBias = 0.5,
		}
	}
}
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 »

hello,

is that possible to make a weapon that can gains xp by killing monsters, and then increase its attackdamage (permanently) on a certain amount of xp gained?
My asset pack: viewtopic.php?f=22&t=9320

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

Re: Ask a simple question, get a simple answer

Post by minmay »

bongobeat wrote: Wed May 01, 2019 10:21 am hello,

is that possible to make a weapon that can gains xp by killing monsters, and then increase its attackdamage (permanently) on a certain amount of xp gained?
yes
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.
Pompidom
Posts: 497
Joined: Sun May 06, 2018 9:42 pm

Re: Ask a simple question, get a simple answer

Post by Pompidom »

An easy method would by an onHit counter where the weapon transitions to a +1/+2/+3/+... version after x amount of successful hits, no idea how to do it properly where the weapon gains experience like a real champion though. I'd love to implement this in my mod as well.
User avatar
Zo Kath Ra
Posts: 937
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: Wed May 01, 2019 10:21 am hello,

is that possible to make a weapon that can gains xp by killing monsters, and then increase its attackdamage (permanently) on a certain amount of xp gained?
MeleeAttackComponent.onPostAttack(self, champion, slot)
only called when you attack an empty tile (?)

ItemActionComponent.onAttack(self, champion, slot, chainIndex)
MeleeAttackComponent.onHitMonster(self, monster, tside, damage, champion)

add two counter components to the weapon:
a) for storing the weapon slot during onAttack()
b) for storing the weapon's xp

How much exp should the weapon gain?
- same amount of xp the champion gains (only when monster dies)
- amount that depends on the damage done, and the monster's exp (every time monster takes damage, even if it doesn't die)
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 »

Thank you for your answers

I omit something: is this possible as well for any kind of item, like an armor, etc... ?

I have in mind the legendary item (particularly armor) that are in might and magic X, and gain xp (well I don't remember if it was by killing directly monsters or anything else), and can be upgraded a few times.
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: 937
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: Wed May 01, 2019 4:09 pm Thank you for your answers

I omit something: is this possible as well for any kind of item, like an armor, etc... ?

I have in mind the legendary item (particularly armor) that are in might and magic X, and gain xp (well I don't remember if it was by killing directly monsters or anything else), and can be upgraded a few times.
Depends on what exactly you want to do.

Armor gains xp every time the champion gains xp?
Then I'd attach a timer to the party.
The timer runs once every frame.
It checks if any champions have gained xp.
If so, the timer distributes the xp gain among the weapons / armor the champion is wearing / wielding.
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 »

yes, stats of the weapon or armor have to changes higher when gaining xp from any monster
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: 937
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 May 02, 2019 10:34 am yes, stats of the weapon or armor have to changes higher when gaining xp from any monster
Only xp gains from killing monsters?
(but not from finding secrets or treasures)
User avatar
vanblam
Posts: 243
Joined: Sun Nov 09, 2014 12:15 am

Re: Ask a simple question, get a simple answer

Post by vanblam »

In an objects definition how would I go about making it a one time animation and give it a delay before it does its activation on its target?

Here is my objects definition, I just have it set as a lever for now. I need it to animate just once and then activate its selected target (after a delay of about 3 seconds).

Code: Select all

defineObject{
	name = "rc_unique_lever",
	baseObject = "base_wall_decoration",
	components = {
		{
			class = "Model",
			model = "mod_assets/stotp_models/red_cave/env/rc_unique_lever.fbx",
			staticShadow = true,
		},
		{
			class = "Animation",
			animations = {
				activate = "mod_assets/stotp_animations/red_cave/rc_unique_lever_activate.fbx",
				deactivate = "mod_assets/stotp_animations/red_cave/rc_unique_lever_rest.fbx",
			},
			onAnimationEvent = function(self, event)
			if event == "activate" then
			self.go.clickable:disable()
				end
			end,
		},
		{
			class = "Clickable",
			offset = vec(0,1.50,0.5),
			size = vec(0.15, 0.9, 0.4),
			maxDistance = 1,
			debugDraw = true,
		},
		{
			class = "Lever",
			sound = "rc_unique_lever",
		},
		{
			class = "Null",
			onInit = function(self)
				local x = self.go.x
				local y = self.go.y
				local facing = self.go.facing
				local level = self.go.level
				
				local fx,fy = getForward(facing)
				local rx,ry = getForward((facing+1)%4)

				do				
					local x = self.go.x + fx
					local y = self.go.y + fy
					if x >= 0 and y >= 0 and x < 32 and y < 32 then
						self.go.map:setAutomapTile(x, y, 4)	-- rocky wall
					end
				end

				-- destroy all pillars ,slopes and supports
				for yy=0,2 do
					for xx=-1,1 do
						local x = x + rx * xx + fx * yy
						local y = y + ry * xx + fy * yy
						if x >= 0 and y >= 0 and x < 32 and y < 32 then
							for e in self.go.map:entitiesAt(x, y) do
								if e.name == "rc_edge_support_01" 
								or e.name == "rc_edge_support_02" 
								or e.name == "rc_edge_support_03"
								or e.name == "rc_edge_support_04"
								or e.name == "rc_edge_support_01b"
								or e.name == "rc_edge_support_02b"
								or e.name == "rc_edge_support_03b"
								or e.name == "rc_edge_support_04b"
								or e.name == "rc_pillar_01a" 
								or e.name == "rc_pillar_01b" 
								or e.name == "rc_pillar_02a"
								or e.name == "rc_wall_slope"								
								then e:destroy()
								end
							end
						end
					end
				end
			end,
		},
	},
	editorIcon = 92,
	tags = { "shadowgate", "red cave", "vanblam" },
}
Post Reply