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!
minmay
Posts: 2689
Joined: Mon Sep 23, 2013 2:24 am

Re: Ask a simple question, get a simple answer

Post by minmay » Tue Sep 10, 2019 8:37 pm

It should work fine to use a skill/trait onRecomputeStats hook to subtract the appropriate amount for each resistance. Resistances only get clamped to 0-100 at the very end of recomputation, fortunately.

However, calculating the amount to subtract will be a bit annoying: you will absolutely get wrong results if you poll stats from inside an onRecomputeStats hook. The order in which onRecomputeStats hooks are called in each set is undefined. The only way to get correct results with the naive approach of calling getCurrentStat() inside an onRecomputeStats hook is if the onRecomputeStats hook is on a trait and it is your only onRecomputeStats hook on a trait; trait onRecomputeStats hooks are called after item/condition/skill ones.
So your options are:
1. manually account for the effects of all your other onRecomputeStats hooks in your elemental resistance-calculating onRecomputeStats hook.
2. make your elemental resistance-calculating hook your only trait onRecomputeStats hook, moving all the other trait onRecomputeStats hooks' effects into it.

Option 2 is less error-prone, though it's still pretty ugly.
You also need to manually account for leadership (+1 to all stats per hasTrait("leadership") party member that is alive) and nightstalker (+5 vitality when time of day >= 1, -5 vitality otherwise) since those are only applied after all onRecomputeStats hooks are called.

So your trait would look something like this (untested, as usual):

Code: Select all

defineTrait{
	name = "resistance_hack",
	uiName = "Players shouldn't see this uiName",
	icon = 0,
	description = "Players shouldn't see this either",
	hidden = true,
	onRecomputeStats = function(champion, level)
		level = champion:getLevel()
		if champion:hasTrait("fighter") then
			champion:addStatModifier("max_health", 60 + (level-1) * 7)
			champion:addStatModifier("max_energy", 30 + (level-1) * 3)
		end
		if champion:hasTrait("barbarian") then
			champion:addStatModifier("strength", level)
			champion:addStatModifier("max_health", 80 + (level-1) * 10)
			champion:addStatModifier("max_energy", 30 + (level-1) * 3)
		end

		[...and so on for all the other traits]



		local leadershipBonus = 0
		for c=1,4 do
			local champ = party.party:getChampion(c)
			if champ:isAlive() and champ:hasTrait("leadership") then
				leadershipBonus = leadershipBonus+1
			end
		end

		local trueStrength = champion:getCurrentStat("strength")+leadershipBonus
		local trueDexterity = champion:getCurrentStat("dexterity")+leadershipBonus
		local trueVitality = champion:getCurrentStat("vitality")+leadershipBonus
		local trueWillpower = champion:getCurrentStat("willpower")+leadershipBonus
		if champion:hasTrait("nightstalker") then
			trueVitality = trueVitality+(GameMode.getTimeOfDay() >= 1 and 5 or -5)
		end

		-- Resistances get stat*2-20 added to them, so subtract stat*2-20 here. 
		champion:addStatModifier("resist_fire", -trueStrength*2+20)
		champion:addStatModifier("resist_shock", -trueDexterity*2+20)
		champion:addStatModifier("resist_poison", -trueVitality*2+20)
		champion:addStatModifier("resist_cold", -trueWillpower*2+20)
	end,
}
And you'd make sure to start every champion with this trait and not have any other onRecomputeStats hooks on traits.
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.

User avatar
Lorial
Posts: 74
Joined: Sat Dec 29, 2018 12:27 pm

Re: Ask a simple question, get a simple answer

Post by Lorial » Thu Sep 12, 2019 9:08 am

Isaac wrote:
Sat Sep 07, 2019 2:33 am
Works like a charm, thanks.


Is there a proper way to add a delay or timer to a onDie/spawnOnDeath event for a breakable item to make it "reappear"?

I am thinking about a "regrowing" function for breakable items like thorns or cattail. I might simply use timers to respawn them or let them spawn after certain events of progression in my mod (e.g. boss fights, entrace to another area etc.), however, this would seem like a smoother solution.

Zo Kath Ra
Posts: 863
Joined: Sat Apr 21, 2012 9:57 am
Location: Germany

Re: Ask a simple question, get a simple answer

Post by Zo Kath Ra » Thu Sep 12, 2019 4:40 pm

Lorial wrote:
Thu Sep 12, 2019 9:08 am
Is there a proper way to add a delay or timer to a onDie/spawnOnDeath event for a breakable item to make it "reappear"?

I am thinking about a "regrowing" function for breakable items like thorns or cattail. I might simply use timers to respawn them or let them spawn after certain events of progression in my mod (e.g. boss fights, entrace to another area etc.), however, this would seem like a smoother solution.
delayedCall(receiver, delay, msg)
You can pass additional parameters to delayedCall.

onDie:
delayedCall("script_plant_growth", 20, "newPlant", "orcweed", level, x, y, facing, elevation)

Code: Select all

function newPlant(object, level, x, y, facing, elevation)
	spawn(object, level, x, y, facing, elevation)
end

Pompidom
Posts: 476
Joined: Sun May 06, 2018 9:42 pm

Re: Ask a simple question, get a simple answer

Post by Pompidom » Thu Sep 12, 2019 9:24 pm

Using the default asset object code from the
- repeater, revolver
- fire_bomb,
- pellet_box maybe if needed

Can anyone help me with:

1. A "defineobject code" that makes a repeater firing pellets but doesn't need pellets as ammo in the offhand.

2. A grenade launcher using the default repeater as a base that uses custom firebombs as ammo? (I can implement a model later)

3. The default 6 cylinder revolver that fires 2 pellets by leftclicking instead of 1 pellet as preparation for a double shotgun.

4. The secondary "repeatfire mode" can it be implemented to the base attack (leftclick) of the repeater? In that case I don't think question 3 is necessary.

5. is it possible to make a "pellet spitting" turret? Instead of fireball activated traps, a hail of bullets.

minmay
Posts: 2689
Joined: Mon Sep 23, 2013 2:24 am

Re: Ask a simple question, get a simple answer

Post by minmay » Thu Sep 12, 2019 11:55 pm

Pompidom wrote:
Thu Sep 12, 2019 9:24 pm
1. A "defineobject code" that makes a repeater firing pellets but doesn't need pellets as ammo in the offhand.
Set clipSize and loadedCount to math.huge in the FIrearmAttackComponent definition.
Pompidom wrote:
Thu Sep 12, 2019 9:24 pm
3. The default 6 cylinder revolver that fires 2 pellets by leftclicking instead of 1 pellet as preparation for a double shotgun.

4. The secondary "repeatfire mode" can it be implemented to the base attack (leftclick) of the repeater? In that case I don't think question 3 is necessary.
repeatCount and repeatDelay in the FirearmAttackComponent definition. Look at the repeater and shuriken definitions for examples.
Pompidom wrote:
Thu Sep 12, 2019 9:24 pm
5. is it possible to make a "pellet spitting" turret? Instead of fireball activated traps, a hail of bullets.
attackType = "firearm" in the MonsterAttackComponent definition. Look at ratling_boss.lua for examples.
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: 476
Joined: Sun May 06, 2018 9:42 pm

Re: Ask a simple question, get a simple answer

Post by Pompidom » Fri Sep 13, 2019 4:12 am

Works great, I have functional shotguns, double shotguns etc... now it even gave me answers to some follow up questions I wanted to ask by simply experimenting with the defineobject codes.

However I still haven't grasped doing the same for my magic wands. Simply rightclicking them for instant casts. They don't need an option to go to the rune panel. It doesn't need to be a secondary function. Simply fire off their spell instantly without a buildup. basically like in your mod Lost Halls of the Drinn.

Zo kath ra told me to:
If you want truly immediate secondary actions, maybe try PartyComponent.onClickItemSlot(self, champion, container, slot, button)

But I have no idea how to proceed. I have taken a look at lost halls of the drinn wands notepad file, but it seems it's vastly different from log2 defineobject code.

Example wand taken from your dm pack and altered into a custom wand in my mod:

Code: Select all

defineObject{
	name = "dm_staff_of_manar5",
	baseObject = "base_dm_item",
	components = {
		{
			class = "Model",
			model = "mod_assets/dmcsb_pack/models/items/dm_staff_of_manar.fbx",
		},
		{
			class = "Item",
			uiName = "Staff of Lightning bolt +5",
			description = "An ancient magical staff that shoots lightning bolts",
			gfxAtlas = "mod_assets/dmcsb_pack/textures/gui/dm_icoatlas.tga",
			gfxIndex = 71,
			gfxIndexPowerAttack = 150,
			impactSound = "impact_blunt",
			weight = 2.9,
			secondaryAction = "Lightning",
		},
		{
			class = "EquipmentItem",
			slot = "Weapon",
			willpower = 5,
		},
		{
			class = "CastSpell",
			name = "Lightning",
			uiName = "Lightning Bolt",
			gameEffect = "Conjures a Lightning Bolt",
			cooldown = 5,
			spell = "lightning_bolt",
			energyCost = 50,
			power = 10,
			buildup = 0.1,
			requirements = { "concentration", 1 },
		},
	},
}

minmay
Posts: 2689
Joined: Mon Sep 23, 2013 2:24 am

Re: Ask a simple question, get a simple answer

Post by minmay » Fri Sep 13, 2019 7:35 am

CastSpellComponent is what you would use for that; look at the fire blade, lightning rod, stormseed orb, etc. in the standard assets for examples. Just set it as the primary action instead of the secondary action like it is for those items.
Be aware however that the onComputeCooldown hook doesn't work for CastSpellComponent, or RunePanelComponent for that matter, so plan accordingly (probably by replacing all your onComputeCooldown hooks with multipliers to the champion's "cooldown_rate" stat).
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.

User avatar
Lorial
Posts: 74
Joined: Sat Dec 29, 2018 12:27 pm

Re: Ask a simple question, get a simple answer

Post by Lorial » Fri Sep 13, 2019 8:38 am

Zo Kath Ra wrote:
Thu Sep 12, 2019 4:40 pm
delayedCall(receiver, delay, msg)
You can pass additional parameters to delayedCall.

onDie:
delayedCall("script_plant_growth", 20, "newPlant", "orcweed", level, x, y, facing, elevation)

Code: Select all

function newPlant(object, level, x, y, facing, elevation)
	spawn(object, level, x, y, facing, elevation)
end
I did all the above and the plant does reappear as it should, but it respawns right in front of the party after the timer, regardless whether I traverse to another level/area.

What do I have to change to make the object appear right where the destroyed object triggered the script? I tried the self.go:spawn instead and add selfs to the functions which leads to crashes only.

User avatar
maneus
Posts: 233
Joined: Mon Jun 17, 2013 10:42 pm
Location: Switzerland

Re: Ask a simple question, get a simple answer

Post by maneus » Fri Sep 13, 2019 4:39 pm

No idea if it worked but how if your dying "plant" would spawn a timer which is connected to a script in your mod?
Spawn the timer on the place your dying plant is, like:

Code: Select all

onDie:  spawn("timer", self.level, self.x, self.y, self.facing, self.elevation)
	:setTimerInterval(your time till respawn)
	:setDisableSelf(true)
	:onActivate:(your plant spawning script here)
	:start
end
Make a script in your dungeon like:

Code: Select all

function newPlant:
	spawn(your object, self.level, self.x, self.y, self.facing, self.elevation)
end

Zo Kath Ra
Posts: 863
Joined: Sat Apr 21, 2012 9:57 am
Location: Germany

Re: Ask a simple question, get a simple answer

Post by Zo Kath Ra » Fri Sep 13, 2019 6:32 pm

Lorial wrote:
Fri Sep 13, 2019 8:38 am
I did all the above and the plant does reappear as it should, but it respawns right in front of the party after the timer, regardless whether I traverse to another level/area.

What do I have to change to make the object appear right where the destroyed object triggered the script? I tried the self.go:spawn instead and add selfs to the functions which leads to crashes only.
Sample dungeon:
https://drive.google.com/file/d/1eQn-SI ... sp=sharing

mod_assets/scripts/objects.lua contains a redefined "beach_cattail_damaged"
script_entity_1 contains the function newPlant()

1) Attack the "beach_cattail_blocker" => it becomes a "beach_cattail_damaged"
2) Attack the "beach_cattail_damaged" => it becomes a "beach_cattail_broken"
3) After 5 seconds, the "beach_cattail_broken" becomes a "beach_cattail_damaged"
4) After another 5 seconds, the "beach_cattail_damaged" becomes a "beach_cattail_blocker"

script_entity_1.newPlant spawns a new plant and removes the old plant at the same location.
So it assumes that there's only one plant of a certain type at a particular (level, x, y, elevation)

edit:
If you destroy the plant while it's regrowing, it will regrow anyway.

You can make it regrow in a single step:
1) mod_assets/scripts/objects.lua
Replace

Code: Select all

delayedCall("script_entity_1", 5, "newPlant", "beach_cattail_broken", "beach_cattail_damaged", self.go.level, self.go.x, self.go.y, self.go.facing, self.go.elevation)
with

Code: Select all

delayedCall("script_entity_1", 5, "newPlant", "beach_cattail_broken", "beach_cattail_blocker", self.go.level, self.go.x, self.go.y, self.go.facing, self.go.elevation)
2) script_entity_1.newPlant
You can remove

Code: Select all

	-- If we've just spawned a "beach_cattail_damaged"
	-- Then replace it with a "beach_cattail_blocker" after 5 seconds
	if (object_old == "beach_cattail_broken") and (object_new == "beach_cattail_damaged") then
		delayedCall(self.go.id, 5, "newPlant", "beach_cattail_damaged", "beach_cattail_blocker", level, x, y, facing, elevation)
	end

Post Reply