DQoL - SMBX2 V1.1.2

Share and discuss custom LunaLua code and content packs for SMBX2.
Donkdonker124
Rex
Rex
Posts: 31
Joined: Wed Mar 27, 2024 10:31 am
Flair: Fictionally stronger than man
Pronouns: he/him
Contact:

DQoL - SMBX2 V1.1.2

Postby Donkdonker124 » Wed May 22, 2024 11:32 am

Image

(Acronym pronounced as DEE-kawl)

This is just a really basic pack that aims to add new things that fit in with the base game. It includes the following features:
NPCs: show
Mega Maverick Thwomp: A giant version of the Maverick Thwomp which can smash through fragile blocks.
Image

Extra Plant Variants: Includes sideways Fire-Spewing Piranha Plants and Piranhacus Giganticus, and vertical red smb3 plant.
Image

Very Fast/Slow line guide NPCs: Includes the Engine Block, Grinder, and YI Platform.
Image

More Goomba Variants: Includes Giant and Mega Gloomba, Goombrat, and Para-Goombrat.
Image

Charged Spiny Egg: A variant of the Spiny Egg that cannot be spinjumped on. Upon hitting the ground, it turns into a Charged Spiny.
Charged Fuzzy: A variant of the Fuzzy that cannot be spinjumped on. Moves slightly faster than the regular variant.
Image

More Yoshi's House Bird Colors: Specifically teal, pink, and white.
Image

Blert: A previously unused enemy! Gradually spreads into its surroundings through mitosis. Killing the core wipes out all the duplicates.
Image

Various .ini files: These primarily fix errors in the editor, such as typos, descriptions, and animations.
Costumes: show
Image
From left to right, we have...

Donkey Kong: Goes over Bowser. Comes with a custom sound. (Sprites made by PetoMico on TSR)
Shawn Cobalt: An original character made by me (painful self-insert). Goes over Uncle Broadsword.
Minesweeper: Goes over Ultimate Rinka, comes with an overhaul of all its sounds (Sprites ripped by Black Squirrel, edited by me)
Gabriel Chimes: Another original character made by me, and is also the girlfriend of Shawn. Goes over Zelda.
Blocks: show
A resprite of the Muncher to make it not look ugly.
BeforeImageAfter
One of the NPC passthrough blocks has been moved one pixel down, too.

Passthrough Synced Switches: Upon contact with a player or NPC, they'll toggle to the respective polarity.
Image

Various .ini files (same goes for this as the NPC category.)

To download the latest version, Click the logo at the top!
changelog: show
V1.0: Initial release
V1.0.1: added a few new things
V1.1: Removed Golden Clawgrip and added various .ini files and sprite fixes
V1.1.1: Fixed Goombrat and Mega Gloomba, and added Passthrough Synced Switches
V1.1.2: Fixed numerous issues with costumes, added Blert and Gabby Chimes
All showcases use MrDoubleA's ExtraBGOProperties.lua.
Last edited by Donkdonker124 on Thu Jun 06, 2024 8:25 am, edited 11 times in total.

Just_Thomas
Spike
Spike
Posts: 296
Joined: Sat Dec 16, 2023 3:32 am
Pronouns: he/him

Re: DQoL - SMBX2

Postby Just_Thomas » Wed May 22, 2024 4:10 pm

Ok, the packs highlight is the DK costume, this is for sure. It is really really well done and does fit SMBX so nice. Great job!

It really looks great and will work quite well with all these DK based npcs which have been already created.
BUT I see 2 flaws here:

Image
The Bowser Icon is very likely not a problem to replace (a DK logo like on the barrels should work best here)
https://www.mariowiki.com/DK_Barrel
\data\graphics\bowser
(bowser-hud-hit and bowser-hud-hit-empty)

Second one is very likely much harder to solve: You might see it already; I don`t think DK has a good reason to hire bowsers minions. :mrgreen:
Somehow this should be "programmed out". It simply does not make any sense.
(I admit I have no idea if something like that is already done by somebody else and also can not judge how difficult this might be).

A flaw to hear are missing or rather fitting sounds.
You might find some good ones here:
http://www.dkc-atlas.com/forum/viewtopic.php?t=1604

Normally a good costume is everything, but unfortunately in this particular case the difference between the characters as a base and as a costume is just too big. So I'm not complaining for no reason (therefore sorry about that). Without these changes, it will be almost impossible to make good use of this work and that would be a shame in the end. Maybe some of the big guys here on the forum is willing to help out, if you are stuck (at least it does happen quite a lot, from my impression).

Code: Select all

function onStart()
	player:transform(CHARACTER_BOWSER);
	player.speedX = -1
	Player.setCostume(CHARACTER_BOWSER, "PetoMico-Donkey Kong")
end
[project whatever]\costumes\bowser\PetoMico-Donkey Kong

Donkdonker124
Rex
Rex
Posts: 31
Joined: Wed Mar 27, 2024 10:31 am
Flair: Fictionally stronger than man
Pronouns: he/him
Contact:

Re: DQoL - SMBX2

Postby Donkdonker124 » Wed May 22, 2024 5:52 pm

Just_Thomas wrote:
Wed May 22, 2024 4:10 pm
The Bowser Icon is very likely not a problem to replace (a DK logo like on the barrels should work best here)
A flaw to hear are missing or rather fitting sounds.
I made a simple addition to DK, which is the replacement for the Bowser icon.
As for the sounds... I could only find two used, which are internally referred to as sound-5 and sound-77.

These are his hit and punching sounds respectively.

Just_Thomas
Spike
Spike
Posts: 296
Joined: Sat Dec 16, 2023 3:32 am
Pronouns: he/him

Re: DQoL - SMBX2

Postby Just_Thomas » Thu May 23, 2024 8:23 am

Donkdonker124 wrote:
Wed May 22, 2024 5:52 pm
Just_Thomas wrote:
Wed May 22, 2024 4:10 pm
The Bowser Icon is very likely not a problem to replace (a DK logo like on the barrels should work best here)
A flaw to hear are missing or rather fitting sounds.
I made a simple addition to DK, which is the replacement for the Bowser icon.
As for the sounds... I could only find two used, which are internally referred to as sound-5 and sound-77.

These are his hit and punching sounds respectively.
I actually do not know how this stuff works, but according to "...\data\graphics\bowser" there are more used sounds.
But it is hard to figure WHERE they are used (situation based I mean).

Image

*caught* Emral? Now would be a good time for you to write - once again - a clarifying post here. ... please!

fexhvhbdsf864
Shy Guy
Shy Guy
Posts: 6
Joined: Mon Jan 30, 2023 2:57 am

Re: DQoL - SMBX2 V1.0.1

Postby fexhvhbdsf864 » Thu May 23, 2024 10:56 am

This Asset Pack zip is empty,Please Added File.

Donkdonker124
Rex
Rex
Posts: 31
Joined: Wed Mar 27, 2024 10:31 am
Flair: Fictionally stronger than man
Pronouns: he/him
Contact:

Re: DQoL - SMBX2 V1.0.1

Postby Donkdonker124 » Thu May 23, 2024 12:17 pm

fexhvhbdsf864 wrote:
Thu May 23, 2024 10:56 am
This Asset Pack zip is empty,Please Added File.
Oh! It should be fixed now. Sorry 'bout that.

Just_Thomas
Spike
Spike
Posts: 296
Joined: Sat Dec 16, 2023 3:32 am
Pronouns: he/him

Re: DQoL - SMBX2 V1.0.1

Postby Just_Thomas » Thu May 23, 2024 1:10 pm

sound-5 does not get loaded from the costume directory. It still uses the "stock" file from the ...\data\graphics\bowser directory.
Makes me wonder, if this is a bug related to SMBX itself, but I can not tell through.
I was about to seek for proper replacement sound files for DK, but if this does not even work, it barely makes sense atm.

mariobrigade2018
Flurry
Flurry
Posts: 354
Joined: Wed May 24, 2023 7:00 pm
Flair: Normie in coding who dreams of making a Mario game
Pronouns: he/him

Re: DQoL - SMBX2 V1.0.1

Postby mariobrigade2018 » Sat May 25, 2024 3:41 pm

The INI file for the SMB3 Red Piranha is borked, so I fixed it and uploaded it here:
https://drive.google.com/file/d/1VAc8L0 ... sp=sharing


Fixed in the main link, no longer needed.
Last edited by mariobrigade2018 on Tue May 28, 2024 2:55 pm, edited 1 time in total.

Donkdonker124
Rex
Rex
Posts: 31
Joined: Wed Mar 27, 2024 10:31 am
Flair: Fictionally stronger than man
Pronouns: he/him
Contact:

Re: DQoL - SMBX2 V1.0.1

Postby Donkdonker124 » Tue May 28, 2024 6:36 am

mariobrigade2018 wrote:
Sat May 25, 2024 3:41 pm
The INI file for the SMB3 Red Piranha is borked, so I fixed it and uploaded it here:
https://drive.google.com/file/d/1VAc8L0 ... sp=sharing
Oh. My bad

Added in 7 minutes 47 seconds:
Re: DQoL - SMBX2 V1.1
Yo.

Version 1.1 just dropped. Here's the changelog
Spoiler: show
  • Removed Golden Clawgrip (It was just made as a joke :\)
  • Added various .ini files to fix editor-related stuffs

Donkdonker124
Rex
Rex
Posts: 31
Joined: Wed Mar 27, 2024 10:31 am
Flair: Fictionally stronger than man
Pronouns: he/him
Contact:

Re: DQoL - SMBX2 V1.1.2

Postby Donkdonker124 » Wed Jun 05, 2024 9:20 am

Yet another update has came out! This time, it adds...

  • Gabriel Chimes as a costume for Zelda
  • Blert (an unused enemy restored)
  • Numerous costume fixes
Oh also before anyone asks, no I'm not adding the Golblert to the pack, as it seems unfinished.

mariobrigade2018
Flurry
Flurry
Posts: 354
Joined: Wed May 24, 2023 7:00 pm
Flair: Normie in coding who dreams of making a Mario game
Pronouns: he/him

Re: DQoL - SMBX2 V1.1.2

Postby mariobrigade2018 » Wed Jun 05, 2024 10:23 am

Donkdonker124 wrote:
Wed Jun 05, 2024 9:20 am
Yet another update has came out! This time, it adds...

  • Blert (an unused enemy restored)
Oh also before anyone asks, no I'm not adding the Golblert to the pack, as it seems unfinished.
What do you mean the golblerts are unfinished?
Last edited by mariobrigade2018 on Thu Jun 06, 2024 1:09 am, edited 1 time in total.

AToMIC
Hoopster
Hoopster
Posts: 106
Joined: Fri Jan 24, 2014 2:57 pm
Flair: I'm here
Pronouns: He/Him

Re: DQoL - SMBX2 V1.1.2

Postby AToMIC » Wed Jun 05, 2024 2:47 pm

Are the very majestic SMW bushes in the background of all these demonstrations included in the package too?

mariobrigade2018
Flurry
Flurry
Posts: 354
Joined: Wed May 24, 2023 7:00 pm
Flair: Normie in coding who dreams of making a Mario game
Pronouns: he/him

Re: DQoL - SMBX2 V1.1.2

Postby mariobrigade2018 » Wed Jun 05, 2024 4:22 pm

AToMIC wrote:
Wed Jun 05, 2024 2:47 pm
Are the very majestic SMW bushes in the background of all these demonstrations included in the package too?
No. You use this:
viewtopic.php?t=28888

Donkdonker124
Rex
Rex
Posts: 31
Joined: Wed Mar 27, 2024 10:31 am
Flair: Fictionally stronger than man
Pronouns: he/him
Contact:

Re: DQoL - SMBX2 V1.1.2

Postby Donkdonker124 » Thu Jun 06, 2024 8:24 am

mariobrigade2018 wrote:
Wed Jun 05, 2024 4:22 pm
AToMIC wrote:
Wed Jun 05, 2024 2:47 pm
Are the very majestic SMW bushes in the background of all these demonstrations included in the package too?
No. You use this:
viewtopic.php?t=28888
Yeah I forgot to mention that. I'll update the post.
mariobrigade2018 wrote:
Wed Jun 05, 2024 10:23 am
Donkdonker124 wrote:
Wed Jun 05, 2024 9:20 am
Yet another update has came out! This time, it adds...

  • Blert (an unused enemy restored)
Oh also before anyone asks, no I'm not adding the Golblert to the pack, as it seems unfinished.
What do you mean the golblerts are unfinished?
As seen in this code, the (presumed) functionality of the Golblerts seem to be inactive.
Note: The function in question is located all the way at the bottom.
Edit: This code seems to be very similar to the Arrow Lift Platform.

Code: Select all

local blert = {}
local npcManager = require("npcManager")

local blertID = {
                 normal = 490,
                 gol = 491
                }
local blertIDMap = {}

local killEffect = 10


-- Register properties, harm types, etc. for each blert type
local blertBase = {}
for  k,v in pairs(blertID)  do
	blertIDMap[v] = k

	blertBase[k] = {
		config = npcManager.setNpcSettings({
			id = v,
			gfxwidth = 36,
			gfxheight = 36,
			gfxoffsety = 0,
			width = 32,
			height = 32,
			frames = 6,
			framespeed = 8,
			framestyle = 0,
			score = 0,
			blocknpctop = 0,
			blocknpc = 0,
			playerblocktop = 0,
			playerblock = 0,
			nohurt = 0,
			nogravity = 1,
			noblockcollision = 1,
			jumphurt = 0
		})
	}

	npcManager.registerHarmTypes(
		v,
		{
			HARM_TYPE_JUMP,
			HARM_TYPE_FROMBELOW,
			HARM_TYPE_NPC,
			HARM_TYPE_HELD,
			HARM_TYPE_TAIL,
			HARM_TYPE_SPINJUMP,
			HARM_TYPE_SWORD,
			HARM_TYPE_LAVA
		},
		{
			[HARM_TYPE_JUMP]=10,
			[HARM_TYPE_FROMBELOW]=10,
			[HARM_TYPE_NPC]=10,
			[HARM_TYPE_HELD]=10,
			[HARM_TYPE_TAIL]=10,
			[HARM_TYPE_SPINJUMP]=10,
			[HARM_TYPE_LAVA]={id=13, xoffset=0.5, xoffsetBack = 0, yoffset=1, yoffsetBack = 1.5}
		}
	)
end



local function round(val)
	local int,frac = math.modf(val)
	if frac >= 0.5  then
		return math.ceil(val)
	else
		return math.floor(val)
	end
end



local function cor_spawnBlert(args)
	local blob = args.parent
	local parentData = blob.data._basegame
	args.x = round(args.x  or  0)
	args.y = round(args.y  or  0)
	local w,h = round(blob.width),round(blob.height)

	--windowDebug (tostring(args.x)..", "..tostring(args.y))

	local x1,y1,x2,y2 = blob.x+args.x+2, blob.y+args.y+2, blob.x+args.x+w-2, blob.y+args.y+h-2
	local npcFree = (#NPC.getIntersecting(x1,y1,x2,y2) == 0)
	local blockFree = (#Block.getIntersecting(x1,y1,x2,y2) == 0)

	if  npcFree  and  blockFree  and  blob.isValid  then
		parentData.animFrame = 1
		Routine.waitFrames(20)

		if  blob.isValid  then
			npcFree = (#NPC.getIntersecting(x1,y1,x2,y2) == 0)
			blockFree = (#Block.getIntersecting(x1,y1,x2,y2) == 0)

			if  npcFree  and  blockFree  then
				SFX.play(72)
				local child = NPC.spawn (blob.id, blob.x+args.x, blob.y+args.y, player.section, false)
				child.data._basegame = {
				                        parent = parentData.parent  or  blob,
				                        radius = parentData.radius,
				                        growrate = parentData.growrate,
				                        spawned = true
				                       }
			end
		end
	end
end

local function cor_trySpawn(v)
	local blob = v
	local size = {}
	local data = blob.data._basegame

	local parent = data.parent  or  blob
	if  blob.isValid  and  parent.isValid  then
		local x1,x2,y1,y2 = blob.x+0.5*blob.width,parent.x+0.5*parent.width, blob.y+0.5*blob.height,parent.y+0.5*parent.height
		--windowDebug(tostring(x1)..","..tostring(x2)..","..tostring(y1)..","..tostring(y2))

		local distance = math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))
		data.distance = distance

		local surrounding = NPC.getIntersecting(blob.x-0.5*blob.width, blob.y-0.5*blob.height, blob.x+1.5*blob.width, blob.y+1.5*blob.height)

		if  #surrounding < 9  and  distance < data.radius*math.min(blob.width, blob.height)  and  not data.killChain  then
			for  _,v in ipairs{{x=blob.width},{x=-blob.width},{y=blob.height},{y=-blob.height}}  do
				v.parent = blob
				Routine.run(cor_spawnBlert, v)
			end
		end

		Routine.skip()
		if  blob.isValid  then
			if  data.animFrame ~= 0  then
				if  blob.isValid  then
					data.animFrame = 1
					Routine.waitFrames(4)
				end

				if  blob.isValid  then
					data.animFrame = 2
					Routine.waitFrames(16)
				end

				if  blob.isValid  then
					data.animFrame = 3
					Routine.waitFrames(4)
				end
				
				if  blob.isValid  then
					data.animFrame = 0
				end
			end
		end
	end
end

local function cor_blertDie(args)
	SFX.play(38)
	local blob = args.npc
	local data = blob.data._basegame
	data.animFrame = 1
	Routine.waitFrames(4)
	data.animFrame = 2

	if  blob.isValid  then
		local size = {width=round(blob.width), height=round(blob.height), id=blob.id}
		blob.speedX = 0
		blob.speedY = 0

		-- Only go through with the pop effect and ripple kill if the parent reference is a valid normal blert
		if  data.parent.isValid  and  size.id == blertID.normal  then

			-- If killing the core, destroy all others in a ripple
			if  data.parent == blob  then

				-- Make all the surrounding blerts look dead
				local x1,y1 = blob.x + 0.5*size.width, blob.y + 0.5*size.height
				local x2,y2,x3,y3 = x1-size.width*(data.radius+1), y1-size.height*(data.radius+1), x1+size.width*(data.radius+1), y1+size.height*(data.radius+1)

				local surrounding = NPC.getIntersecting(x2,y2,x3,y3)
				for  k,v in ipairs(surrounding)  do
					if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  then
						if  v.data._basegame.parent == blob  then
							v.data._basegame.killChain = true
						end
					end
				end

				-- Make the core invisible
				blob.isHidden = true
				Routine.waitFrames(32)

				-- Double-check (I'm sure there's a much better way to handle this)
				surrounding = NPC.getIntersecting(x2,y2,x3,y3)
				for  k,v in ipairs(surrounding)  do
					if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  then
						if  v.data._basegame.parent == blob  then
							v.data._basegame.killChain = true
						end
					end
				end

				-- Kill the blerts ring by ring
				SFX.play(41)
				for  i=1,data.radius+1,0.25  do
					local surroundingB = NPC.getIntersecting(x1-i*size.width, y1-i*size.height, x1+i*size.width, y1+i*size.height)

					for  k,v in ipairs(surroundingB)  do
						if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  then
							if  v.data._basegame.killChain == true  then
								v.data._basegame.dead = true
								v:kill()
							end
						end
					end
					Routine.skip()
				end

			else
				local x1,y1 = blob.x + 0.5*size.width, blob.y + 0.5*size.height

				local surroundingA = NPC.getIntersecting(x1-2*size.width, y1-2*size.height, x1+2*size.width, y1+2*size.height)
				local surroundingB = NPC.getIntersecting(x1-size.width,   y1-1,             x1+size.width,   y1+1)
				local surroundingC = NPC.getIntersecting(x1-1,            y1-size.height,   x1+1,            y1+size.height)

				for  k,v in ipairs(surroundingA)  do
					if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  then
						v.data._basegame.timer = v.data._basegame.growrate*32  or  32
					end
				end

				for  k,v in ipairs(table.append(surroundingB,surroundingC))  do
					if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  and  v ~= args.npc  then
						if  v ~= v.data._basegame.parent  then
							v.data._basegame.dead = true
						end
						v:kill()
					end
				end
			end
		end

		-- Final kill
		Routine.waitFrames(4)
		if  blob.isValid  then
			data.dead = true
			blob:kill()
		end
	end
end


function dieCheck(v)
end


function blert.onTickNPC(v)
	local blob = v


	-- Initialize properties
	if  blob.data._basegame == nil  then 
		blob.data._basegame = {spawned = false}
	end
	local data = blob.data._basegame

	data.growrate = data.growrate  or  blob.data.growrate  or  1
	data.radius = data.radius  or  blob.data.radius  or  3
	data.parent = data.parent  or  blob
	data.distance = data.distance  or  0

	-- Never despawn offscreen, but don't start multiplying until onscreen
	if  blob:mem(0x12A, FIELD_WORD) == 180  then
		data.spawned = true
	end
	if  data.spawned == true  then
		blob:mem(0x12A, FIELD_WORD, 179)
	end

	-- Manage animation
	data.animFrame = data.animFrame  or  0
	if  not data.parent.isValid  or  data.killChain == true  then
		data.animFrame = 4
	end

	blob.animationFrame = data.animFrame


	-- Manage multiplication timer
	if  data.timer == nil  then  data.timer = data.growrate*65;  end;
	data.timer = data.timer-1

	if  data.timer <= 0  and  blob.isValid  and  not data.killChain  and  data.spawned  then
		data.timer = data.growrate*65
		Routine.run(cor_trySpawn, blob)
		dieCheck (blob)
	end


	-- Force speed to 0
	--blob.speedX = 0
	--blob.speedY = 0
end


function blert.onNPCKill(eventobj, npc, reason)
	if (not npc.isValid) then
		return;
	end

	if  npc.id == blertID.normal  and  not npc:mem(0x64,FIELD_BOOL)  and  reason ~= HARM_TYPE_OFFSCREEN  then
		local blob = npc
		local data = blob.data._basegame
		if  data.dead ~= true  then
			eventobj.cancelled = true
			Routine.run(cor_blertDie, {npc=npc})
		else
			Animation.spawn(killEffect, npc.x, npc.y)
		end
	end
end


function blert.onInitAPI()
	for  k,v in pairs(blertID) do
		npcManager.registerEvent(v, blert, "onTickNPC")
	end

	registerEvent(blert, "onNPCKill")
end

return blert

--BASE
  -- v.ai1 // v.data.type {
	-- 	/What Type?
  --    0 = !
	-- 	  1 = Up
	-- 	  2 = Left
	-- 	  3 = Right
	--   }
	-- v.ai2 v.data.life {
	--   /How long should the ghost created last?
	--   }

--Ghost
  -- v.ai1 v.data.type {
	-- 	/Direction?
	-- 	0 = up
	-- 	1 = Left
	-- 	2 = Right
	-- }
	-- v.ai2 v.data.sp {
	-- 	/Should change direction when jumped?
	-- 	true = Yes
	-- 	false = No
	-- }
	-- v.ai3 v.data.life {
	--   /How long should the ghost last?
  -- }

mariobrigade2018
Flurry
Flurry
Posts: 354
Joined: Wed May 24, 2023 7:00 pm
Flair: Normie in coding who dreams of making a Mario game
Pronouns: he/him

Re: DQoL - SMBX2 V1.1.2

Postby mariobrigade2018 » Thu Jun 06, 2024 9:35 pm

Donkdonker124 wrote:
Thu Jun 06, 2024 8:31 am
As seen in this code, the (presumed) functionality of the Golblerts seem to be inactive.
Note: The function in question is located all the way at the bottom.
Edit: This code seems to be very similar to the Arrow Lift Platform.

Code: Select all

local blert = {}
local npcManager = require("npcManager")

local blertID = {
                 normal = 490,
                 gol = 491
                }
local blertIDMap = {}

local killEffect = 10


-- Register properties, harm types, etc. for each blert type
local blertBase = {}
for  k,v in pairs(blertID)  do
	blertIDMap[v] = k

	blertBase[k] = {
		config = npcManager.setNpcSettings({
			id = v,
			gfxwidth = 36,
			gfxheight = 36,
			gfxoffsety = 0,
			width = 32,
			height = 32,
			frames = 6,
			framespeed = 8,
			framestyle = 0,
			score = 0,
			blocknpctop = 0,
			blocknpc = 0,
			playerblocktop = 0,
			playerblock = 0,
			nohurt = 0,
			nogravity = 1,
			noblockcollision = 1,
			jumphurt = 0
		})
	}

	npcManager.registerHarmTypes(
		v,
		{
			HARM_TYPE_JUMP,
			HARM_TYPE_FROMBELOW,
			HARM_TYPE_NPC,
			HARM_TYPE_HELD,
			HARM_TYPE_TAIL,
			HARM_TYPE_SPINJUMP,
			HARM_TYPE_SWORD,
			HARM_TYPE_LAVA
		},
		{
			[HARM_TYPE_JUMP]=10,
			[HARM_TYPE_FROMBELOW]=10,
			[HARM_TYPE_NPC]=10,
			[HARM_TYPE_HELD]=10,
			[HARM_TYPE_TAIL]=10,
			[HARM_TYPE_SPINJUMP]=10,
			[HARM_TYPE_LAVA]={id=13, xoffset=0.5, xoffsetBack = 0, yoffset=1, yoffsetBack = 1.5}
		}
	)
end



local function round(val)
	local int,frac = math.modf(val)
	if frac >= 0.5  then
		return math.ceil(val)
	else
		return math.floor(val)
	end
end



local function cor_spawnBlert(args)
	local blob = args.parent
	local parentData = blob.data._basegame
	args.x = round(args.x  or  0)
	args.y = round(args.y  or  0)
	local w,h = round(blob.width),round(blob.height)

	--windowDebug (tostring(args.x)..", "..tostring(args.y))

	local x1,y1,x2,y2 = blob.x+args.x+2, blob.y+args.y+2, blob.x+args.x+w-2, blob.y+args.y+h-2
	local npcFree = (#NPC.getIntersecting(x1,y1,x2,y2) == 0)
	local blockFree = (#Block.getIntersecting(x1,y1,x2,y2) == 0)

	if  npcFree  and  blockFree  and  blob.isValid  then
		parentData.animFrame = 1
		Routine.waitFrames(20)

		if  blob.isValid  then
			npcFree = (#NPC.getIntersecting(x1,y1,x2,y2) == 0)
			blockFree = (#Block.getIntersecting(x1,y1,x2,y2) == 0)

			if  npcFree  and  blockFree  then
				SFX.play(72)
				local child = NPC.spawn (blob.id, blob.x+args.x, blob.y+args.y, player.section, false)
				child.data._basegame = {
				                        parent = parentData.parent  or  blob,
				                        radius = parentData.radius,
				                        growrate = parentData.growrate,
				                        spawned = true
				                       }
			end
		end
	end
end

local function cor_trySpawn(v)
	local blob = v
	local size = {}
	local data = blob.data._basegame

	local parent = data.parent  or  blob
	if  blob.isValid  and  parent.isValid  then
		local x1,x2,y1,y2 = blob.x+0.5*blob.width,parent.x+0.5*parent.width, blob.y+0.5*blob.height,parent.y+0.5*parent.height
		--windowDebug(tostring(x1)..","..tostring(x2)..","..tostring(y1)..","..tostring(y2))

		local distance = math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))
		data.distance = distance

		local surrounding = NPC.getIntersecting(blob.x-0.5*blob.width, blob.y-0.5*blob.height, blob.x+1.5*blob.width, blob.y+1.5*blob.height)

		if  #surrounding < 9  and  distance < data.radius*math.min(blob.width, blob.height)  and  not data.killChain  then
			for  _,v in ipairs{{x=blob.width},{x=-blob.width},{y=blob.height},{y=-blob.height}}  do
				v.parent = blob
				Routine.run(cor_spawnBlert, v)
			end
		end

		Routine.skip()
		if  blob.isValid  then
			if  data.animFrame ~= 0  then
				if  blob.isValid  then
					data.animFrame = 1
					Routine.waitFrames(4)
				end

				if  blob.isValid  then
					data.animFrame = 2
					Routine.waitFrames(16)
				end

				if  blob.isValid  then
					data.animFrame = 3
					Routine.waitFrames(4)
				end
				
				if  blob.isValid  then
					data.animFrame = 0
				end
			end
		end
	end
end

local function cor_blertDie(args)
	SFX.play(38)
	local blob = args.npc
	local data = blob.data._basegame
	data.animFrame = 1
	Routine.waitFrames(4)
	data.animFrame = 2

	if  blob.isValid  then
		local size = {width=round(blob.width), height=round(blob.height), id=blob.id}
		blob.speedX = 0
		blob.speedY = 0

		-- Only go through with the pop effect and ripple kill if the parent reference is a valid normal blert
		if  data.parent.isValid  and  size.id == blertID.normal  then

			-- If killing the core, destroy all others in a ripple
			if  data.parent == blob  then

				-- Make all the surrounding blerts look dead
				local x1,y1 = blob.x + 0.5*size.width, blob.y + 0.5*size.height
				local x2,y2,x3,y3 = x1-size.width*(data.radius+1), y1-size.height*(data.radius+1), x1+size.width*(data.radius+1), y1+size.height*(data.radius+1)

				local surrounding = NPC.getIntersecting(x2,y2,x3,y3)
				for  k,v in ipairs(surrounding)  do
					if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  then
						if  v.data._basegame.parent == blob  then
							v.data._basegame.killChain = true
						end
					end
				end

				-- Make the core invisible
				blob.isHidden = true
				Routine.waitFrames(32)

				-- Double-check (I'm sure there's a much better way to handle this)
				surrounding = NPC.getIntersecting(x2,y2,x3,y3)
				for  k,v in ipairs(surrounding)  do
					if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  then
						if  v.data._basegame.parent == blob  then
							v.data._basegame.killChain = true
						end
					end
				end

				-- Kill the blerts ring by ring
				SFX.play(41)
				for  i=1,data.radius+1,0.25  do
					local surroundingB = NPC.getIntersecting(x1-i*size.width, y1-i*size.height, x1+i*size.width, y1+i*size.height)

					for  k,v in ipairs(surroundingB)  do
						if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  then
							if  v.data._basegame.killChain == true  then
								v.data._basegame.dead = true
								v:kill()
							end
						end
					end
					Routine.skip()
				end

			else
				local x1,y1 = blob.x + 0.5*size.width, blob.y + 0.5*size.height

				local surroundingA = NPC.getIntersecting(x1-2*size.width, y1-2*size.height, x1+2*size.width, y1+2*size.height)
				local surroundingB = NPC.getIntersecting(x1-size.width,   y1-1,             x1+size.width,   y1+1)
				local surroundingC = NPC.getIntersecting(x1-1,            y1-size.height,   x1+1,            y1+size.height)

				for  k,v in ipairs(surroundingA)  do
					if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  then
						v.data._basegame.timer = v.data._basegame.growrate*32  or  32
					end
				end

				for  k,v in ipairs(table.append(surroundingB,surroundingC))  do
					if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  and  v ~= args.npc  then
						if  v ~= v.data._basegame.parent  then
							v.data._basegame.dead = true
						end
						v:kill()
					end
				end
			end
		end

		-- Final kill
		Routine.waitFrames(4)
		if  blob.isValid  then
			data.dead = true
			blob:kill()
		end
	end
end


function dieCheck(v)
end


function blert.onTickNPC(v)
	local blob = v


	-- Initialize properties
	if  blob.data._basegame == nil  then 
		blob.data._basegame = {spawned = false}
	end
	local data = blob.data._basegame

	data.growrate = data.growrate  or  blob.data.growrate  or  1
	data.radius = data.radius  or  blob.data.radius  or  3
	data.parent = data.parent  or  blob
	data.distance = data.distance  or  0

	-- Never despawn offscreen, but don't start multiplying until onscreen
	if  blob:mem(0x12A, FIELD_WORD) == 180  then
		data.spawned = true
	end
	if  data.spawned == true  then
		blob:mem(0x12A, FIELD_WORD, 179)
	end

	-- Manage animation
	data.animFrame = data.animFrame  or  0
	if  not data.parent.isValid  or  data.killChain == true  then
		data.animFrame = 4
	end

	blob.animationFrame = data.animFrame


	-- Manage multiplication timer
	if  data.timer == nil  then  data.timer = data.growrate*65;  end;
	data.timer = data.timer-1

	if  data.timer <= 0  and  blob.isValid  and  not data.killChain  and  data.spawned  then
		data.timer = data.growrate*65
		Routine.run(cor_trySpawn, blob)
		dieCheck (blob)
	end


	-- Force speed to 0
	--blob.speedX = 0
	--blob.speedY = 0
end


function blert.onNPCKill(eventobj, npc, reason)
	if (not npc.isValid) then
		return;
	end

	if  npc.id == blertID.normal  and  not npc:mem(0x64,FIELD_BOOL)  and  reason ~= HARM_TYPE_OFFSCREEN  then
		local blob = npc
		local data = blob.data._basegame
		if  data.dead ~= true  then
			eventobj.cancelled = true
			Routine.run(cor_blertDie, {npc=npc})
		else
			Animation.spawn(killEffect, npc.x, npc.y)
		end
	end
end


function blert.onInitAPI()
	for  k,v in pairs(blertID) do
		npcManager.registerEvent(v, blert, "onTickNPC")
	end

	registerEvent(blert, "onNPCKill")
end

return blert

--BASE
  -- v.ai1 // v.data.type {
	-- 	/What Type?
  --    0 = !
	-- 	  1 = Up
	-- 	  2 = Left
	-- 	  3 = Right
	--   }
	-- v.ai2 v.data.life {
	--   /How long should the ghost created last?
	--   }

--Ghost
  -- v.ai1 v.data.type {
	-- 	/Direction?
	-- 	0 = up
	-- 	1 = Left
	-- 	2 = Right
	-- }
	-- v.ai2 v.data.sp {
	-- 	/Should change direction when jumped?
	-- 	true = Yes
	-- 	false = No
	-- }
	-- v.ai3 v.data.life {
	--   /How long should the ghost last?
  -- }
But aren't Golblert just normal ones with no reproduction cap?

Donkdonker124
Rex
Rex
Posts: 31
Joined: Wed Mar 27, 2024 10:31 am
Flair: Fictionally stronger than man
Pronouns: he/him
Contact:

Re: DQoL - SMBX2 V1.1.2

Postby Donkdonker124 » Fri Jun 07, 2024 6:56 am

mariobrigade2018 wrote:
Thu Jun 06, 2024 9:35 pm
Donkdonker124 wrote:
Thu Jun 06, 2024 8:31 am
As seen in this code, the (presumed) functionality of the Golblerts seem to be inactive.
Note: The function in question is located all the way at the bottom.
Edit: This code seems to be very similar to the Arrow Lift Platform.

Code: Select all

local blert = {}
local npcManager = require("npcManager")

local blertID = {
                 normal = 490,
                 gol = 491
                }
local blertIDMap = {}

local killEffect = 10


-- Register properties, harm types, etc. for each blert type
local blertBase = {}
for  k,v in pairs(blertID)  do
	blertIDMap[v] = k

	blertBase[k] = {
		config = npcManager.setNpcSettings({
			id = v,
			gfxwidth = 36,
			gfxheight = 36,
			gfxoffsety = 0,
			width = 32,
			height = 32,
			frames = 6,
			framespeed = 8,
			framestyle = 0,
			score = 0,
			blocknpctop = 0,
			blocknpc = 0,
			playerblocktop = 0,
			playerblock = 0,
			nohurt = 0,
			nogravity = 1,
			noblockcollision = 1,
			jumphurt = 0
		})
	}

	npcManager.registerHarmTypes(
		v,
		{
			HARM_TYPE_JUMP,
			HARM_TYPE_FROMBELOW,
			HARM_TYPE_NPC,
			HARM_TYPE_HELD,
			HARM_TYPE_TAIL,
			HARM_TYPE_SPINJUMP,
			HARM_TYPE_SWORD,
			HARM_TYPE_LAVA
		},
		{
			[HARM_TYPE_JUMP]=10,
			[HARM_TYPE_FROMBELOW]=10,
			[HARM_TYPE_NPC]=10,
			[HARM_TYPE_HELD]=10,
			[HARM_TYPE_TAIL]=10,
			[HARM_TYPE_SPINJUMP]=10,
			[HARM_TYPE_LAVA]={id=13, xoffset=0.5, xoffsetBack = 0, yoffset=1, yoffsetBack = 1.5}
		}
	)
end



local function round(val)
	local int,frac = math.modf(val)
	if frac >= 0.5  then
		return math.ceil(val)
	else
		return math.floor(val)
	end
end



local function cor_spawnBlert(args)
	local blob = args.parent
	local parentData = blob.data._basegame
	args.x = round(args.x  or  0)
	args.y = round(args.y  or  0)
	local w,h = round(blob.width),round(blob.height)

	--windowDebug (tostring(args.x)..", "..tostring(args.y))

	local x1,y1,x2,y2 = blob.x+args.x+2, blob.y+args.y+2, blob.x+args.x+w-2, blob.y+args.y+h-2
	local npcFree = (#NPC.getIntersecting(x1,y1,x2,y2) == 0)
	local blockFree = (#Block.getIntersecting(x1,y1,x2,y2) == 0)

	if  npcFree  and  blockFree  and  blob.isValid  then
		parentData.animFrame = 1
		Routine.waitFrames(20)

		if  blob.isValid  then
			npcFree = (#NPC.getIntersecting(x1,y1,x2,y2) == 0)
			blockFree = (#Block.getIntersecting(x1,y1,x2,y2) == 0)

			if  npcFree  and  blockFree  then
				SFX.play(72)
				local child = NPC.spawn (blob.id, blob.x+args.x, blob.y+args.y, player.section, false)
				child.data._basegame = {
				                        parent = parentData.parent  or  blob,
				                        radius = parentData.radius,
				                        growrate = parentData.growrate,
				                        spawned = true
				                       }
			end
		end
	end
end

local function cor_trySpawn(v)
	local blob = v
	local size = {}
	local data = blob.data._basegame

	local parent = data.parent  or  blob
	if  blob.isValid  and  parent.isValid  then
		local x1,x2,y1,y2 = blob.x+0.5*blob.width,parent.x+0.5*parent.width, blob.y+0.5*blob.height,parent.y+0.5*parent.height
		--windowDebug(tostring(x1)..","..tostring(x2)..","..tostring(y1)..","..tostring(y2))

		local distance = math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))
		data.distance = distance

		local surrounding = NPC.getIntersecting(blob.x-0.5*blob.width, blob.y-0.5*blob.height, blob.x+1.5*blob.width, blob.y+1.5*blob.height)

		if  #surrounding < 9  and  distance < data.radius*math.min(blob.width, blob.height)  and  not data.killChain  then
			for  _,v in ipairs{{x=blob.width},{x=-blob.width},{y=blob.height},{y=-blob.height}}  do
				v.parent = blob
				Routine.run(cor_spawnBlert, v)
			end
		end

		Routine.skip()
		if  blob.isValid  then
			if  data.animFrame ~= 0  then
				if  blob.isValid  then
					data.animFrame = 1
					Routine.waitFrames(4)
				end

				if  blob.isValid  then
					data.animFrame = 2
					Routine.waitFrames(16)
				end

				if  blob.isValid  then
					data.animFrame = 3
					Routine.waitFrames(4)
				end
				
				if  blob.isValid  then
					data.animFrame = 0
				end
			end
		end
	end
end

local function cor_blertDie(args)
	SFX.play(38)
	local blob = args.npc
	local data = blob.data._basegame
	data.animFrame = 1
	Routine.waitFrames(4)
	data.animFrame = 2

	if  blob.isValid  then
		local size = {width=round(blob.width), height=round(blob.height), id=blob.id}
		blob.speedX = 0
		blob.speedY = 0

		-- Only go through with the pop effect and ripple kill if the parent reference is a valid normal blert
		if  data.parent.isValid  and  size.id == blertID.normal  then

			-- If killing the core, destroy all others in a ripple
			if  data.parent == blob  then

				-- Make all the surrounding blerts look dead
				local x1,y1 = blob.x + 0.5*size.width, blob.y + 0.5*size.height
				local x2,y2,x3,y3 = x1-size.width*(data.radius+1), y1-size.height*(data.radius+1), x1+size.width*(data.radius+1), y1+size.height*(data.radius+1)

				local surrounding = NPC.getIntersecting(x2,y2,x3,y3)
				for  k,v in ipairs(surrounding)  do
					if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  then
						if  v.data._basegame.parent == blob  then
							v.data._basegame.killChain = true
						end
					end
				end

				-- Make the core invisible
				blob.isHidden = true
				Routine.waitFrames(32)

				-- Double-check (I'm sure there's a much better way to handle this)
				surrounding = NPC.getIntersecting(x2,y2,x3,y3)
				for  k,v in ipairs(surrounding)  do
					if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  then
						if  v.data._basegame.parent == blob  then
							v.data._basegame.killChain = true
						end
					end
				end

				-- Kill the blerts ring by ring
				SFX.play(41)
				for  i=1,data.radius+1,0.25  do
					local surroundingB = NPC.getIntersecting(x1-i*size.width, y1-i*size.height, x1+i*size.width, y1+i*size.height)

					for  k,v in ipairs(surroundingB)  do
						if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  then
							if  v.data._basegame.killChain == true  then
								v.data._basegame.dead = true
								v:kill()
							end
						end
					end
					Routine.skip()
				end

			else
				local x1,y1 = blob.x + 0.5*size.width, blob.y + 0.5*size.height

				local surroundingA = NPC.getIntersecting(x1-2*size.width, y1-2*size.height, x1+2*size.width, y1+2*size.height)
				local surroundingB = NPC.getIntersecting(x1-size.width,   y1-1,             x1+size.width,   y1+1)
				local surroundingC = NPC.getIntersecting(x1-1,            y1-size.height,   x1+1,            y1+size.height)

				for  k,v in ipairs(surroundingA)  do
					if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  then
						v.data._basegame.timer = v.data._basegame.growrate*32  or  32
					end
				end

				for  k,v in ipairs(table.append(surroundingB,surroundingC))  do
					if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  and  v ~= args.npc  then
						if  v ~= v.data._basegame.parent  then
							v.data._basegame.dead = true
						end
						v:kill()
					end
				end
			end
		end

		-- Final kill
		Routine.waitFrames(4)
		if  blob.isValid  then
			data.dead = true
			blob:kill()
		end
	end
end


function dieCheck(v)
end


function blert.onTickNPC(v)
	local blob = v


	-- Initialize properties
	if  blob.data._basegame == nil  then 
		blob.data._basegame = {spawned = false}
	end
	local data = blob.data._basegame

	data.growrate = data.growrate  or  blob.data.growrate  or  1
	data.radius = data.radius  or  blob.data.radius  or  3
	data.parent = data.parent  or  blob
	data.distance = data.distance  or  0

	-- Never despawn offscreen, but don't start multiplying until onscreen
	if  blob:mem(0x12A, FIELD_WORD) == 180  then
		data.spawned = true
	end
	if  data.spawned == true  then
		blob:mem(0x12A, FIELD_WORD, 179)
	end

	-- Manage animation
	data.animFrame = data.animFrame  or  0
	if  not data.parent.isValid  or  data.killChain == true  then
		data.animFrame = 4
	end

	blob.animationFrame = data.animFrame


	-- Manage multiplication timer
	if  data.timer == nil  then  data.timer = data.growrate*65;  end;
	data.timer = data.timer-1

	if  data.timer <= 0  and  blob.isValid  and  not data.killChain  and  data.spawned  then
		data.timer = data.growrate*65
		Routine.run(cor_trySpawn, blob)
		dieCheck (blob)
	end


	-- Force speed to 0
	--blob.speedX = 0
	--blob.speedY = 0
end


function blert.onNPCKill(eventobj, npc, reason)
	if (not npc.isValid) then
		return;
	end

	if  npc.id == blertID.normal  and  not npc:mem(0x64,FIELD_BOOL)  and  reason ~= HARM_TYPE_OFFSCREEN  then
		local blob = npc
		local data = blob.data._basegame
		if  data.dead ~= true  then
			eventobj.cancelled = true
			Routine.run(cor_blertDie, {npc=npc})
		else
			Animation.spawn(killEffect, npc.x, npc.y)
		end
	end
end


function blert.onInitAPI()
	for  k,v in pairs(blertID) do
		npcManager.registerEvent(v, blert, "onTickNPC")
	end

	registerEvent(blert, "onNPCKill")
end

return blert

--BASE
  -- v.ai1 // v.data.type {
	-- 	/What Type?
  --    0 = !
	-- 	  1 = Up
	-- 	  2 = Left
	-- 	  3 = Right
	--   }
	-- v.ai2 v.data.life {
	--   /How long should the ghost created last?
	--   }

--Ghost
  -- v.ai1 v.data.type {
	-- 	/Direction?
	-- 	0 = up
	-- 	1 = Left
	-- 	2 = Right
	-- }
	-- v.ai2 v.data.sp {
	-- 	/Should change direction when jumped?
	-- 	true = Yes
	-- 	false = No
	-- }
	-- v.ai3 v.data.life {
	--   /How long should the ghost last?
  -- }
But aren't Golblert just normal ones with no reproduction cap?
According to their .ini description, they would've behaved much like Conway's Game of Life.

mariobrigade2018
Flurry
Flurry
Posts: 354
Joined: Wed May 24, 2023 7:00 pm
Flair: Normie in coding who dreams of making a Mario game
Pronouns: he/him

Re: DQoL - SMBX2 V1.1.2

Postby mariobrigade2018 » Fri Jun 07, 2024 12:33 pm

Donkdonker124 wrote:
Fri Jun 07, 2024 6:56 am
mariobrigade2018 wrote:
Thu Jun 06, 2024 9:35 pm
Donkdonker124 wrote:
Thu Jun 06, 2024 8:31 am
As seen in this code, the (presumed) functionality of the Golblerts seem to be inactive.
Note: The function in question is located all the way at the bottom.
Edit: This code seems to be very similar to the Arrow Lift Platform.

Code: Select all

local blert = {}
local npcManager = require("npcManager")

local blertID = {
                 normal = 490,
                 gol = 491
                }
local blertIDMap = {}

local killEffect = 10


-- Register properties, harm types, etc. for each blert type
local blertBase = {}
for  k,v in pairs(blertID)  do
	blertIDMap[v] = k

	blertBase[k] = {
		config = npcManager.setNpcSettings({
			id = v,
			gfxwidth = 36,
			gfxheight = 36,
			gfxoffsety = 0,
			width = 32,
			height = 32,
			frames = 6,
			framespeed = 8,
			framestyle = 0,
			score = 0,
			blocknpctop = 0,
			blocknpc = 0,
			playerblocktop = 0,
			playerblock = 0,
			nohurt = 0,
			nogravity = 1,
			noblockcollision = 1,
			jumphurt = 0
		})
	}

	npcManager.registerHarmTypes(
		v,
		{
			HARM_TYPE_JUMP,
			HARM_TYPE_FROMBELOW,
			HARM_TYPE_NPC,
			HARM_TYPE_HELD,
			HARM_TYPE_TAIL,
			HARM_TYPE_SPINJUMP,
			HARM_TYPE_SWORD,
			HARM_TYPE_LAVA
		},
		{
			[HARM_TYPE_JUMP]=10,
			[HARM_TYPE_FROMBELOW]=10,
			[HARM_TYPE_NPC]=10,
			[HARM_TYPE_HELD]=10,
			[HARM_TYPE_TAIL]=10,
			[HARM_TYPE_SPINJUMP]=10,
			[HARM_TYPE_LAVA]={id=13, xoffset=0.5, xoffsetBack = 0, yoffset=1, yoffsetBack = 1.5}
		}
	)
end



local function round(val)
	local int,frac = math.modf(val)
	if frac >= 0.5  then
		return math.ceil(val)
	else
		return math.floor(val)
	end
end



local function cor_spawnBlert(args)
	local blob = args.parent
	local parentData = blob.data._basegame
	args.x = round(args.x  or  0)
	args.y = round(args.y  or  0)
	local w,h = round(blob.width),round(blob.height)

	--windowDebug (tostring(args.x)..", "..tostring(args.y))

	local x1,y1,x2,y2 = blob.x+args.x+2, blob.y+args.y+2, blob.x+args.x+w-2, blob.y+args.y+h-2
	local npcFree = (#NPC.getIntersecting(x1,y1,x2,y2) == 0)
	local blockFree = (#Block.getIntersecting(x1,y1,x2,y2) == 0)

	if  npcFree  and  blockFree  and  blob.isValid  then
		parentData.animFrame = 1
		Routine.waitFrames(20)

		if  blob.isValid  then
			npcFree = (#NPC.getIntersecting(x1,y1,x2,y2) == 0)
			blockFree = (#Block.getIntersecting(x1,y1,x2,y2) == 0)

			if  npcFree  and  blockFree  then
				SFX.play(72)
				local child = NPC.spawn (blob.id, blob.x+args.x, blob.y+args.y, player.section, false)
				child.data._basegame = {
				                        parent = parentData.parent  or  blob,
				                        radius = parentData.radius,
				                        growrate = parentData.growrate,
				                        spawned = true
				                       }
			end
		end
	end
end

local function cor_trySpawn(v)
	local blob = v
	local size = {}
	local data = blob.data._basegame

	local parent = data.parent  or  blob
	if  blob.isValid  and  parent.isValid  then
		local x1,x2,y1,y2 = blob.x+0.5*blob.width,parent.x+0.5*parent.width, blob.y+0.5*blob.height,parent.y+0.5*parent.height
		--windowDebug(tostring(x1)..","..tostring(x2)..","..tostring(y1)..","..tostring(y2))

		local distance = math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1))
		data.distance = distance

		local surrounding = NPC.getIntersecting(blob.x-0.5*blob.width, blob.y-0.5*blob.height, blob.x+1.5*blob.width, blob.y+1.5*blob.height)

		if  #surrounding < 9  and  distance < data.radius*math.min(blob.width, blob.height)  and  not data.killChain  then
			for  _,v in ipairs{{x=blob.width},{x=-blob.width},{y=blob.height},{y=-blob.height}}  do
				v.parent = blob
				Routine.run(cor_spawnBlert, v)
			end
		end

		Routine.skip()
		if  blob.isValid  then
			if  data.animFrame ~= 0  then
				if  blob.isValid  then
					data.animFrame = 1
					Routine.waitFrames(4)
				end

				if  blob.isValid  then
					data.animFrame = 2
					Routine.waitFrames(16)
				end

				if  blob.isValid  then
					data.animFrame = 3
					Routine.waitFrames(4)
				end
				
				if  blob.isValid  then
					data.animFrame = 0
				end
			end
		end
	end
end

local function cor_blertDie(args)
	SFX.play(38)
	local blob = args.npc
	local data = blob.data._basegame
	data.animFrame = 1
	Routine.waitFrames(4)
	data.animFrame = 2

	if  blob.isValid  then
		local size = {width=round(blob.width), height=round(blob.height), id=blob.id}
		blob.speedX = 0
		blob.speedY = 0

		-- Only go through with the pop effect and ripple kill if the parent reference is a valid normal blert
		if  data.parent.isValid  and  size.id == blertID.normal  then

			-- If killing the core, destroy all others in a ripple
			if  data.parent == blob  then

				-- Make all the surrounding blerts look dead
				local x1,y1 = blob.x + 0.5*size.width, blob.y + 0.5*size.height
				local x2,y2,x3,y3 = x1-size.width*(data.radius+1), y1-size.height*(data.radius+1), x1+size.width*(data.radius+1), y1+size.height*(data.radius+1)

				local surrounding = NPC.getIntersecting(x2,y2,x3,y3)
				for  k,v in ipairs(surrounding)  do
					if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  then
						if  v.data._basegame.parent == blob  then
							v.data._basegame.killChain = true
						end
					end
				end

				-- Make the core invisible
				blob.isHidden = true
				Routine.waitFrames(32)

				-- Double-check (I'm sure there's a much better way to handle this)
				surrounding = NPC.getIntersecting(x2,y2,x3,y3)
				for  k,v in ipairs(surrounding)  do
					if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  then
						if  v.data._basegame.parent == blob  then
							v.data._basegame.killChain = true
						end
					end
				end

				-- Kill the blerts ring by ring
				SFX.play(41)
				for  i=1,data.radius+1,0.25  do
					local surroundingB = NPC.getIntersecting(x1-i*size.width, y1-i*size.height, x1+i*size.width, y1+i*size.height)

					for  k,v in ipairs(surroundingB)  do
						if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  then
							if  v.data._basegame.killChain == true  then
								v.data._basegame.dead = true
								v:kill()
							end
						end
					end
					Routine.skip()
				end

			else
				local x1,y1 = blob.x + 0.5*size.width, blob.y + 0.5*size.height

				local surroundingA = NPC.getIntersecting(x1-2*size.width, y1-2*size.height, x1+2*size.width, y1+2*size.height)
				local surroundingB = NPC.getIntersecting(x1-size.width,   y1-1,             x1+size.width,   y1+1)
				local surroundingC = NPC.getIntersecting(x1-1,            y1-size.height,   x1+1,            y1+size.height)

				for  k,v in ipairs(surroundingA)  do
					if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  then
						v.data._basegame.timer = v.data._basegame.growrate*32  or  32
					end
				end

				for  k,v in ipairs(table.append(surroundingB,surroundingC))  do
					if  v.id == size.id  and  not v:mem(0x64,FIELD_BOOL)  and  v ~= args.npc  then
						if  v ~= v.data._basegame.parent  then
							v.data._basegame.dead = true
						end
						v:kill()
					end
				end
			end
		end

		-- Final kill
		Routine.waitFrames(4)
		if  blob.isValid  then
			data.dead = true
			blob:kill()
		end
	end
end


function dieCheck(v)
end


function blert.onTickNPC(v)
	local blob = v


	-- Initialize properties
	if  blob.data._basegame == nil  then 
		blob.data._basegame = {spawned = false}
	end
	local data = blob.data._basegame

	data.growrate = data.growrate  or  blob.data.growrate  or  1
	data.radius = data.radius  or  blob.data.radius  or  3
	data.parent = data.parent  or  blob
	data.distance = data.distance  or  0

	-- Never despawn offscreen, but don't start multiplying until onscreen
	if  blob:mem(0x12A, FIELD_WORD) == 180  then
		data.spawned = true
	end
	if  data.spawned == true  then
		blob:mem(0x12A, FIELD_WORD, 179)
	end

	-- Manage animation
	data.animFrame = data.animFrame  or  0
	if  not data.parent.isValid  or  data.killChain == true  then
		data.animFrame = 4
	end

	blob.animationFrame = data.animFrame


	-- Manage multiplication timer
	if  data.timer == nil  then  data.timer = data.growrate*65;  end;
	data.timer = data.timer-1

	if  data.timer <= 0  and  blob.isValid  and  not data.killChain  and  data.spawned  then
		data.timer = data.growrate*65
		Routine.run(cor_trySpawn, blob)
		dieCheck (blob)
	end


	-- Force speed to 0
	--blob.speedX = 0
	--blob.speedY = 0
end


function blert.onNPCKill(eventobj, npc, reason)
	if (not npc.isValid) then
		return;
	end

	if  npc.id == blertID.normal  and  not npc:mem(0x64,FIELD_BOOL)  and  reason ~= HARM_TYPE_OFFSCREEN  then
		local blob = npc
		local data = blob.data._basegame
		if  data.dead ~= true  then
			eventobj.cancelled = true
			Routine.run(cor_blertDie, {npc=npc})
		else
			Animation.spawn(killEffect, npc.x, npc.y)
		end
	end
end


function blert.onInitAPI()
	for  k,v in pairs(blertID) do
		npcManager.registerEvent(v, blert, "onTickNPC")
	end

	registerEvent(blert, "onNPCKill")
end

return blert

--BASE
  -- v.ai1 // v.data.type {
	-- 	/What Type?
  --    0 = !
	-- 	  1 = Up
	-- 	  2 = Left
	-- 	  3 = Right
	--   }
	-- v.ai2 v.data.life {
	--   /How long should the ghost created last?
	--   }

--Ghost
  -- v.ai1 v.data.type {
	-- 	/Direction?
	-- 	0 = up
	-- 	1 = Left
	-- 	2 = Right
	-- }
	-- v.ai2 v.data.sp {
	-- 	/Should change direction when jumped?
	-- 	true = Yes
	-- 	false = No
	-- }
	-- v.ai3 v.data.life {
	--   /How long should the ghost last?
  -- }
But aren't Golblert just normal ones with no reproduction cap?
According to their .ini description, they would've behaved much like Conway's Game of Life.
Yea here’s the blog post:
https://codehaus.wohlsoft.ru/blog/2017/ ... -500-npcs/


Return to “LunaLua”

Who is online

Users browsing this forum: No registered users and 0 guests

SMWCentralTalkhausMario Fan Games GalaxyKafukaMarioWikiSMBXEquipoEstelari