Wizards warping off of ground?

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!
Post Reply
DaggorathMaster
Posts: 18
Joined: Thu Sep 08, 2022 7:29 pm

Wizards warping off of ground?

Post by DaggorathMaster » Sat Oct 01, 2022 5:01 am

When a wizard or other monster based on it does a "warp" action outside, it apparently ignores the heightmap. Can anything be done about that other than making new animations or keeping all wizard-battlefields flat?

I thought it was something I did with my own wizard models, but playing the vanilla game and spawning a wizard, it does that, and so do the clones (which I tend to disable).

User avatar
Isaac
Posts: 3123
Joined: Fri Mar 02, 2012 10:02 pm

Re: Wizards warping off of ground?

Post by Isaac » Sat Oct 01, 2022 6:17 am

It's probably just that they use the wizard in that specific map, where his AI behavior works well (and predictably) with the layout.

The engine does allow custom hooking of the monster AI for implementing user-designed AI reactions. I have not tried... but I do expect it's possible to correct the behavior on levels with heightmaps —provided the warp effect can have the destination height adjusted; else that too [the warp effect] would have to be implemented in script.

DaggorathMaster
Posts: 18
Joined: Thu Sep 08, 2022 7:29 pm

Re: Wizards warping off of ground?

Post by DaggorathMaster » Sat Oct 01, 2022 5:16 pm

I'm doing multiple wizard bosses, each with different powers.
SpoilerShow
e.g., one that warps and a mummy or skeleton appears behind it.
So I'd rather not limit the maps too harshly, though I suppose I can have them stay in their castles/temples etc., or have flattened areas outside.

I'll also try implementing "move" animations, and (*tests something*) setSubtileOffset() does correctly adjust, if I want to rescript "warp".

So I have some options.

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

Re: Wizards warping off of ground?

Post by minmay » Sun Oct 02, 2022 2:33 am

I think you should be able to use setWorldPositionY() on the monster object to adjust its Y position to the heightmap (put some other object on the ground at the same x/z world position and copy its y world position), without disrupting anything else.

setWorldPosition() and setSubtileOffset() (which just calls setWorldPosition() internally) can have side effects that you probably don't want.
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.

DaggorathMaster
Posts: 18
Joined: Thu Sep 08, 2022 7:29 pm

Re: Wizards warping off of ground?

Post by DaggorathMaster » Sun Oct 02, 2022 7:20 am

OK an item with setPosition() copying the wizard, and then the wizard copying the item's y position, works great.

Code: Select all

-- Make wizards doing "warp" action stay on ground when there's a heightmap.
-- Looks ridiculous without this.

-- Uses an RC lowpass filter to smooth out height changes:
--- (new_y * j + old_y * k, j + k = 1).

-- I can't find a good code sample for an RC filter, so I'm not sure what to call this.
-- Low values are more gooey, high values are more jagged.
_rc_coef = 0.5

-- Keep track of who is warping, their tracker item, and last y position.
-- Key = wizard.id, val = { tracker_id, last_y }
_warpers = {}

-- Create a timer, attached to party so it's full-speed.
-- One timer to track them all.
--- Will check if party/monster leaves level.
party:createComponent ("Timer", "warpGroundTimer")
party.warpGroundTimer:setTimerInterval (0) -- Per frame.
party.warpGroundTimer:addConnector ("onActivate", "warpOnGround", "warp")

-- Call this when warp starts, from onBeginAction().
function startWarp(component)
	local ent = component.go
	
	-- Checks my level_info script to see if level has a heightmap.
	if not level_info.script.LevelInfo[ent.level].has_heightmap then
		-- Nothing to do if level has no heightmap.
		return
	end

	local tracker = _get_tracker (ent.id)
	_warpers[ent.id] = { tracker_id = tracker.id, last_y = ent:getWorldPositionY() }
	party.warpGroundTimer:start()
end

-- Call this when warp stops, from onEndAction(),
-- or when monster dies or party leaves level (detected in this script, no hook needed).
--- When called from a hook, argument is automatically the action component.
--- When not called from a hook, any component (of the right game object) works.
function endWarp(component)
	local id = component.go.id
	
	-- Delete id from the table.
	_warpers[id] = nil

	-- If the table is empty, stop the per-frame timer.
	if #_warpers == 0 then
		party.warpGroundTimer:stop()
	end
end

-- Called per-frame during warp to keep wizard on the ground following heightmap.
-- There's no simple direct way to get it like getHeightmapHeight (x, y, subtile_x, subtile_y).
function warp(timer)
	for id, info in pairs (_warpers) do
		local ent = findEntity (id)
		if not ent or ent.level ~= party.level or not ent.monster:isAlive() then
			endWarp (ent.monster)
		else
			local tracker = findEntity (info.tracker_id)
			
			-- Copy tracker item to wizard's position.
			-- Does not include distance off of ground, which is set automatically.
			tracker:setPosition (ent.x, ent.y, ent.facing, ent.elevation, ent.level)
			tracker:setSubtileOffset (ent:getSubtileOffset())
			
			-- Calculate new y. Interpolated using an RV filter to smooth out blockiness.
			local new_y = _rc_coef * tracker:getWorldPositionY() + (1 - _rc_coef) * info.last_y
			ent:setWorldPositionY (new_y)

			info.last_y = new_y
		end
	end
end

-- Get or create tracker item, that will follow the wizard but stay on ground.
function _get_tracker (wizard_id)
	local id = "warp_on_ground_tracker_" .. wizard_id
	if findEntity (id) then
		return findEntity (id)
	end
	
	-- Type doesn't matter, but turn off model,
	-- Also make it so it can't be picked up (is there a better way/existing tag etc.?)
	--- Party hook onPickupItem() needs to check this.
	local tracker = spawn ("blueberry_pie")
	tracker.model:disable()
	tracker.item:addTrait ("no_pickup")
	
	return tracker
end

DaggorathMaster
Posts: 18
Joined: Thu Sep 08, 2022 7:29 pm

Re: Wizards warping off of ground?

Post by DaggorathMaster » Sun Oct 02, 2022 7:29 am

Not quite right! It glitched at the end! Had to delay the ending by a tick.

User avatar
Isaac
Posts: 3123
Joined: Fri Mar 02, 2012 10:02 pm

Re: Wizards warping off of ground?

Post by Isaac » Sun Oct 02, 2022 7:41 am

What kind of glitch?

DaggorathMaster
Posts: 18
Joined: Thu Sep 08, 2022 7:29 pm

Re: Wizards warping off of ground?

Post by DaggorathMaster » Sun Oct 02, 2022 3:36 pm

It popped back to position y of zero. Since it's a hook, it was called on the last frame but before the main warp script, which didn't then keep the position.

Also I forgot to assign the id to the tracker, so it kept spawning more. Glad I hadn't made them undetectable yet - my see-item-names-at-a-distance script still saw piles even though the models were disabled!

DaggorathMaster
Posts: 18
Joined: Thu Sep 08, 2022 7:29 pm

Re: Wizards warping off of ground?

Post by DaggorathMaster » Sun Oct 02, 2022 6:30 pm

There's still an issue, but a small-ish one.

At the same x, y, and subtile offset, the item's start and end worldPositionY isn't the same as the wizard's. So there's still a jump in height at the start and the end, and not easily predictable.

I tried using another wizard with a deactivated brain, so it would have the same collision bounds, but that didn't change it. I don't get how the same position comes up with different heights.

That was the only smoothness issue, so I omitted the filtering. To smooth out the end it would need to know A: the end height for the wizard (not a substitute) and B: when the warp is going to end, which changes because of distance and is not precise for the same distance.

Post Reply