Page 1 of 1

hangblocks.lua - Hang in there, baby! (v1.1)

Posted: Thu Mar 30, 2023 9:07 pm
by cold soup
Blocks you can grab and hang on to, sorta like horizontal ropes. They're similar to the horizontal ropes from the Donkey Kong Country games. You grab them automatically if you're falling. While grabbing one, you can move left and right, jump from them, or let go of them by pressing down.

Since I only made and configured graphics for Mario, he's the only character you can use on them by default (other characters cause an error). However, once you have graphics configured for them, the other classic characters (Luigi, Peach, Toad, Link) should work with them too. X2 characters probably won't work.

Check the readme for guides on how to register blocks or create character graphics!

EXAMPLE:
Spoiler: show
Image
Spoiler: show
Image
DOWNLOAD:
Spoiler: show

Re: hangblocks.lua - Hang in there, baby! (v1.0.2)

Posted: Fri Mar 31, 2023 1:48 pm
by ditditdit
i love this!

although to me, the climbing sprites for mario in the gif look cursed.

Re: hangblocks.lua - Hang in there, baby! (v1.0.2)

Posted: Fri Mar 31, 2023 2:20 pm
by DRACalgar Law
Will those players be able to attack while hanging?

Re: hangblocks.lua - Hang in there, baby! (v1.0.2)

Posted: Sat Apr 01, 2023 4:41 pm
by cold soup
ditditdit wrote:
Fri Mar 31, 2023 1:48 pm
i love this!

although to me, the climbing sprites for mario in the gif look cursed.
i'm gonna be honest, the sprites were just kinda thrown together. plus i'm not really well-versed in the SMB3 style
DRACalgar Law wrote:
Fri Mar 31, 2023 2:20 pm
Will those players be able to attack while hanging?
nope

Re: hangblocks.lua - Hang in there, baby! (v1.0.2)

Posted: Fri Apr 07, 2023 11:39 pm
by ChunkyChimp
Looks like a great addition, but I think Mario should face away while hanging. Or be rendered behind the rope. RN looks wacky

Re: hangblocks.lua - Hang in there, baby! (v1.0.2)

Posted: Mon Apr 10, 2023 3:46 am
by Emral
Hey there! Great script, just one thing you ought to change!
Your script seems to add new variables to the player. The player does not have a data table, so this could come into conflict with other code, or accidentally override basegame variables.
Infact, the variable p.isOnGround overwrites the basegame method p:isOnGround(). This causes scripts to error when your script is loaded, if they perform grounded checks for the player.
My recommendation is to not store this value in the player, but make your own table storing the value for each player.
local isOnGround = {false, false}

isOnGround[p.idx] = true

Gaming.

Re: hangblocks.lua - Hang in there, baby! (v1.0.2)

Posted: Mon May 08, 2023 11:00 pm
by cold soup
Emral wrote:
Mon Apr 10, 2023 3:46 am
Hey there! Great script, just one thing you ought to change!
Your script seems to add new variables to the player. The player does not have a data table, so this could come into conflict with other code, or accidentally override basegame variables.
Infact, the variable p.isOnGround overwrites the basegame method p:isOnGround(). This causes scripts to error when your script is loaded, if they perform grounded checks for the player.
My recommendation is to not store this value in the player, but make your own table storing the value for each player.
local isOnGround = {false, false}

isOnGround[p.idx] = true

Gaming.
thanks for the advice! i thought that was a variable that players had since it was in the SMBX2 documentation, and i assumed i needed it set to "true" so players have ground friction while grabbing a hangblock. turns out it's neither, so i removed the line of code that sets it in the latest update. thanks for pointing out it can cause errors, hopefully that should be fixed now

Re: hangblocks.lua - Hang in there, baby! (v1.0.2)

Posted: Tue May 09, 2023 8:50 am
by Emral
cold soup wrote:
Mon May 08, 2023 11:00 pm

thanks for the advice! i thought that was a variable that players had since it was in the SMBX2 documentation, and i assumed i needed it set to "true" so players have ground friction while grabbing a hangblock. turns out it's neither, so i removed the line of code that sets it in the latest update. thanks for pointing out it can cause errors, hopefully that should be fixed now
Ahh, I understand the misunderstanding there. This function is actually only used by SMBX2 user code, so it wouldn't affect friction. I believe one of the memory offsets that handles bottom block collision is responsible for that, but that also controls jumping ability, so it might be best to not mess too much with the guts of the system.
I looked over the update and it looks good now (thumbsup).

Re: hangblocks.lua - Hang in there, baby! (v1.0.3)

Posted: Fri Sep 22, 2023 6:54 am
by FutureNyanCat
I've reported a bug where if the script is loaded in a level, players won't be able to grab NPCs that have the grabtop flag enabled, including SMB2 grass and Shy Guys. I'm not sure how to fix that.

Re: hangblocks.lua - Hang in there, baby! (v1.0.3)

Posted: Fri Sep 22, 2023 7:38 am
by Master of Disaster
FutureNyanCat wrote:
Fri Sep 22, 2023 6:54 am
I've reported a bug where if the script is loaded in a level, players won't be able to grab NPCs that have the grabtop flag enabled, including SMB2 grass and Shy Guys. I'm not sure how to fix that.
Fixed it. The Cause was that the grabDelay was always set when ducking, even if the player is not hanging on a rope. I just moved the part that sets the grabDelay so it only executes when holding a rope.

Here's the code:

Code: Select all

--[[
	hangblocks.lua v1.0.3 (v.1.0.4 now hehehe)

	A library that adds blocks that function as Donkey Kong Country 2-esque horizontal ropes.
	This script handles player behavior when hanging from a block, as well as collision detection
	for hangblocks.

	by cold soup
]]

local blockManager = require("blockManager")

local hangblocks = {}

local blockIDs = {}

hangblocks.playerData = {}

---------------------- CUSTOMIZABLE PROPERTIES ----------------------
--[[
	This script by default is only configured properly for Mario, as he's the only one I made 
	graphics for. However, you can set the graphics and properties of other characters by adding 
	their constants to the tables of individual properties.
	
	Example: 
	hangblocks.grabframes = {
	 	[CHARACTER_MARIO] = Graphics.loadImageResolved("grabframes-mario.png"),
		[CHARACTER_LUIGI] = Graphics.loadImageResolved("grabframes-luigi.png")
	}

	List of character constants: https://docs.codehaus.moe/#/constants/characters
]]

-- grab graphics
hangblocks.grabframes = {
	[CHARACTER_MARIO] = Graphics.loadImageResolved("grabframes-mario.png")
}

-- width and height of an individual frame
hangblocks.framewidth = {
	[CHARACTER_MARIO] = 36
}
hangblocks.frameheight = {
	[CHARACTER_MARIO] = 60
}

-- x and y offsets of a single frame
hangblocks.frameXoffset = {
	[CHARACTER_MARIO] = -4
}
hangblocks.frameYoffset = {
	[CHARACTER_MARIO] = -6
}

-- number of frames for moving while on a hangblock
hangblocks.numframes = {
	[CHARACTER_MARIO] = 2
}

-- render priority of the climbing animation
-- mainly here if you want a character to be behind the blocks
hangblocks.framepriority = {
	[CHARACTER_MARIO] = -25
}

-- speed of the animation
hangblocks.animSpeed = 12

---------------------------------------------------------------------

function hangblocks.register(id)
	blockManager.registerEvent(id, hangblocks, "onTickEndBlock")
	blockIDs[id] = true
end

function hangblocks.onInitAPI()
	registerEvent(hangblocks, "onTick")
	registerEvent(hangblocks, "onDraw")
end

local function initializePlayerData(index)
	if hangblocks.playerData[index] then return end
	hangblocks.playerData[index] = {
		grabbing = false,
		currentBlock = nil,
		grabDelay = 0, -- player can't grab blocks if this value is above 0, used so small players can drop 
		currentFrame = 0,
		animationTimer = hangblocks.animSpeed;
	}
end

function hangblocks.onTick()
	for _,p in ipairs(Player.get()) do
		initializePlayerData(p.idx)

		if (hangblocks.playerData[p.idx].grabDelay > 0) then
			hangblocks.playerData[p.idx].grabDelay = hangblocks.playerData[p.idx].grabDelay - 1
			p:mem(0x172,FIELD_BOOL,false) -- added so the player doesn't shoot a projectile/tail swipe when they jump off
		end

		--[[if (player:mem(0x12E, FIELD_BOOL)) then -- prevents players from grabbing ropes while crouching		-- THIS IS MOVED SINCE IT BREAKS GRABBING NPCS FROM ABOVE.
			hangblocks.playerData[p.idx].grabDelay = 10 -- set to 10 because link gets stuck otherwise
		end--]]--

		-- behavior for grabbing a rope
		if (hangblocks.playerData[p.idx].grabbing) then
			p.y = hangblocks.playerData[p.idx].currentBlock.y
			p.speedY = 0

			-- prevents the player from running or using projectiles while grabbing a rope
			p.keys.run = false
			p.keys.altRun = false

			if (player:mem(0x12E, FIELD_BOOL)) then -- prevents players from grabbing ropes while crouching	-- NOW ONLY EXECUTED WHEN THE PLAYER GRABS THE ROPE; Breaks grabbing from above otherwise
				hangblocks.playerData[p.idx].grabDelay = 10 -- set to 10 because link gets stuck otherwise
			end
			
			-- animation-related code
			if (p.speedX == 0) then
				hangblocks.playerData[p.idx].currentFrame = 0
				hangblocks.playerData[p.idx].animationTimer = hangblocks.animSpeed + 1
			else
				hangblocks.playerData[p.idx].animationTimer = hangblocks.playerData[p.idx].animationTimer + 1

				if (hangblocks.playerData[p.idx].animationTimer > hangblocks.animSpeed) then
					hangblocks.playerData[p.idx].currentFrame = (1 + (hangblocks.playerData[p.idx].currentFrame % hangblocks.numframes[p.character]))
					if (p.direction == -1) then hangblocks.playerData[p.idx].currentFrame = hangblocks.playerData[p.idx].currentFrame + hangblocks.numframes[p.character] end
					
					hangblocks.playerData[p.idx].animationTimer = 0
				end
			end

			-- lets go of the rope if the player jumped or pressed down
			if(p.keys.jump == KEYS_PRESSED or p.keys.altJump == KEYS_PRESSED or p.keys.down == KEYS_DOWN) then
				hangblocks.playerData[p.idx].grabbing = false
				hangblocks.playerData[p.idx].grabDelay = 10
			end
		end

		hangblocks.playerData[p.idx].grabbing = false
	end
end

function hangblocks.onDraw()
  	for _,p in ipairs(Player.get()) do
  		if (hangblocks.playerData[p.idx].grabbing and p.deathTimer <= 0) then
  			p:setFrame(-50)

			if (not p:mem(0x142, FIELD_BOOL)) then -- don't draw the player if they're blinking
  				Graphics.draw{
  					type = RTYPE_IMAGE,
  					image = hangblocks.grabframes[p.character],
  					sceneCoords = true,
  					x = p.x + hangblocks.frameXoffset[p.character],
  					y = p.y + hangblocks.frameYoffset[p.character],
  					priority = hangblocks.framepriority[p.character],
  					sourceX = hangblocks.playerData[p.idx].currentFrame * hangblocks.framewidth[p.character],
  					sourceY = (hangblocks.frameheight[p.character] * (p.powerup)) - hangblocks.frameheight[p.character],
  					sourceWidth = hangblocks.framewidth[p.character],
  					sourceHeight = hangblocks.frameheight[p.character]
  				}
  			end
		end
  	end
end

function hangblocks.onTickEndBlock(v)
    -- Don't run code for invisible entities
	if v.isHidden or v:mem(0x5A, FIELD_BOOL) then return end
	
	local data = v.data
	
	-- checks for players touching the block
	for _,p in ipairs(Player.getIntersecting(v.x,v.y,v.x+v.width,v.y+v.height)) do
		if (p.speedY >= 0 and -- player is falling
			p.y > v.y-6 and -- top of player is touching the block
			hangblocks.playerData[p.idx].grabDelay <= 0 and -- doesn't have grab delay
			not p:mem(0x36,FIELD_BOOL) and -- not underwater/in quicksand
			p.forcedState <= 0
		) then
			hangblocks.playerData[p.idx].grabbing = true
			hangblocks.playerData[p.idx].currentBlock = v
		end
	end
end

return hangblocks

Re: hangblocks.lua - Hang in there, baby! (v1.0.3)

Posted: Fri Sep 22, 2023 12:39 pm
by cold soup
Master of Disaster wrote:
Fri Sep 22, 2023 7:38 am
FutureNyanCat wrote:
Fri Sep 22, 2023 6:54 am
I've reported a bug where if the script is loaded in a level, players won't be able to grab NPCs that have the grabtop flag enabled, including SMB2 grass and Shy Guys. I'm not sure how to fix that.
Fixed it. The Cause was that the grabDelay was always set when ducking, even if the player is not hanging on a rope. I just moved the part that sets the grabDelay so it only executes when holding a rope.
thanks for pointing this out! the bit of code you moved was there originally since otherwise big characters couldn't let go of ropes properly when pressing down. turns out that had some bad side effects. the latest version should let you pick up grabtop enemies now while still allowing big characters to let go of ropes properly. if anyone still finds any issues though let me know

Re: hangblocks.lua - Hang in there, baby! (v1.0.4)

Posted: Tue Feb 06, 2024 7:11 pm
by The Thwomp King
Wow this is really cool, and I love how smooth everything runs. Definitely using this for my project, and I'm hoping someone makes more hanging graphics for other characters in the near future!

Re: hangblocks.lua - Hang in there, baby! (v1.0.5)

Posted: Wed Feb 14, 2024 8:44 pm
by cold soup
cold soup wrote:
Thu Mar 30, 2023 9:07 pm
Blocks you can grab and hang on to, sorta like horizontal ropes. They're similar to the horizontal ropes from the Donkey Kong Country games. You grab them automatically if you're falling. While grabbing one, you can move left and right, jump from them, or let go of them by pressing down.

Since I only made and configured graphics for Mario, he's the only character you can use on them by default (other characters cause an error). However, once you have graphics configured for them, the other classic characters (Luigi, Peach, Toad, Link) should work with them too. X2 characters probably won't work.

Check the readme for guides on how to register blocks or create character graphics!

EXAMPLE:
Spoiler: show
Image
Spoiler: show
Image
DOWNLOAD:
pushed another bugfix. this fixes an issue where the library occasionally threw an error upon the level loading, which i think only really started happening in beta 5 (though it stemmed from a programming error on my part Oops). i'd suggest updating the library if one of your levels uses it

Re: hangblocks.lua - Hang in there, baby! (v1.1)

Posted: Tue Aug 20, 2024 11:46 pm
by cold soup
new update! this makes it so you can no longer fall off of hangblocks from the side, which makes a lot more sense visually. this change is enabled by default but it's toggleable. aside from a few QoL features there's a pretty significant optimization change so i'd update the script asap if you're using it in your episode

full changelog
Spoiler: show
- Added an parameter that prevents players from falling off the sides of hangblocks that's active by default. To go back to the original behavior, set hangblocks.canFallOffSide to true.
- Default block's ID has been changed because the reserved blocks range doesn't start at 750. Oops!

- Hangblock-related data now use the player's data table, meaning it's easier to check said data from other scripts. Data is stored under Player.data.hangblocks.
- Collisions with hangblocks are now better optimized because the original way they checked for collisions was really stupid and bad