The Dink Network

overlapping hitboxes + touch()

December 10th 2024, 06:28 AM
custom_iplaydink.gif
iplaydink
Peasant He/Him Sweden
Hmm.. 
I'm trying to run some code once the hitbox of dink overlaps with the hitbox of another sprite.

Apparently touch is NOT run when the sprite and dinks hitboxes overlap, but when dinks depth dot is INSIDE the sprites hitbox, disregarding dinks hitbox. This becomes very obvious when dink is replaced by a bigger sprite.

My second attempt was using brain 11 (missile) to trigger the function, but that has the same problem but in reverse as it checks if the sprites depth dot is inside dink's (or another sprite's). Offsetting this with range would work if it was square, but not on a thinner rectangle.

Is there really no way to get a callback when two sprite's hitboxes overlap? I notice that even just walking around and collision with hardness is done with just dink's depth dot disregarding dink's hitbox. How would you handle this case?
December 10th 2024, 08:05 AM
custom_iplaydink.gif
iplaydink
Peasant He/Him Sweden
Hmm.. 
There is no way to get a sprite's hitbox width/height, right? Or even the sprite height/Width?

That complicates making your own overlap-checker if you want it to work on different objects.
December 10th 2024, 02:10 PM
goblins.gif
drone1400
Peasant He/Him Romania
C# nerd 
Uh, I have no idea about DinkC... I guess RobJ might know more

Edit: Actually looking at the DinkC reference website, the sp_range seems to depend on the sprite's direction huh... Hmmm...

Edit2: Here's a stupid idea: what if you had an additional invisible sprite with a modified hardbox that is extended by the size of Dink's own hardbox. If Dink's depthdot is centered on his hardbox and if the two sprites are overlapped then theoretically the touch would trigger on the 2nd sprite as Dink's own hardbox would touch the 1st sprite's hardbox right?...

Wait actually, if a sprite is invisible can it even touch anymore?... (You could get around this by having the 2nd sprite be a fully transparent frame I suppose, instead of using sp_nodraw)
December 10th 2024, 03:32 PM
death.gif
Seseler
Peasant He/Him Heard Island And Mcdonald Islands
 
Why do you want to get the hardbox intersection? As you noted, the game does not use them for anything?

Also do you need exact solution or is something within ± a few pixels good enough?

If you don't need the exactness you could make a brain 15 sprite with a hitbox suitably larger than the original's and use its touch procedure. (Edit: I guess that drone had the same idea )

If you really want an exact solution, there's the problem that dink's hitbox size depends on the sequence, so you should either adjust dink.ini (and the weapon scripts), or if you really want to do everything the hard way, set the brain 15 sprite's hitbox even larger and then re-check the distance on the touch procedure.

December 10th 2024, 05:37 PM
death.gif
Seseler
Peasant He/Him Heard Island And Mcdonald Islands
 
For whatever reason, I decided to take a stab at making a generic exact solution for hitbox intersection.

First, for getting hitbox data to dink-readable format, I made this AWK-script
BEGIN {
	FS = " "
	last_map = 0;
	base_map = 748;
	print "void main(void)\n{"
}

tolower($1) ~ "^load_sequence" {
	print "//", $0;
	map = base_map + int($3 / 100);
	spr = $3 % 100;
	print "&player_map = " map ";";
	print "editor_seq(" spr ", " ($7 * -1) ");";
	print "editor_frame(" spr ", " ($8 * -1) ");";

	map += 10;
	print "&player_map = " map ";";
	print "editor_seq(" spr ", " $9 ");";
	print "editor_frame(" spr ", " $10 ");";
}

END {
	print "kill_this_task();\n}"
}

which can then be run with command "awk -f boundmaker.awk dink.ini > story/mkbounds.c" to create a DinkC script that stores the hardbox data in sprite info.

Then you should add a
spawn("mkbounds")
line to start-1.c, to actually run the script.

Then, to check the hitbox intersection, you use this script:

// Bounds.c

void get_left(void)
{
	int &real_map = &player_map;
	int &tmp_map = sp_pseq(&arg1, -1);
	&tmp_map /= 100;
	&tmp_map += 748;
	&player_map = &tmp_map;
	int &spr = sp_pseq(&arg1, -1);
	&spr = math_mod(&spr, 100);
	int &ret = editor_seq(&spr, -1);
	&player_map = &real_map;
	return(&ret);
}

void get_top(void)
{
	int &real_map = &player_map;
	int &tmp_map = sp_pseq(&arg1, -1);
	&tmp_map /= 100;
	&tmp_map += 748;
	&player_map = &tmp_map;
	int &spr = sp_pseq(&arg1, -1);
	&spr = math_mod(&spr, 100);
	int &ret = editor_frame(&spr, -1);
	&player_map = &real_map;
	return(&ret);
}

void get_right(void)
{
	int &real_map = &player_map;
	int &tmp_map = sp_pseq(&arg1, -1);
	&tmp_map /= 100;
	&tmp_map += 758;
	&player_map = &tmp_map;
	int &spr = sp_pseq(&arg1, -1);
	&spr = math_mod(&spr, 100);
	int &ret = editor_seq(&spr, -1);
	&player_map = &real_map;
	return(&ret);
}

void get_bottom(void)
{
	int &real_map = &player_map;
	int &tmp_map = sp_pseq(&arg1, -1);
	&tmp_map /= 100;
	&tmp_map += 758;
	&player_map = &tmp_map;
	int &spr = sp_pseq(&arg1, -1);
	&spr = math_mod(&spr, 100);
	int &ret = editor_frame(&spr, -1);
	&player_map = &real_map;
	return(&ret);
}

// Returns 1 if arg1 and arg2's hitboxes intersect, 0 otherwise
void intersects(void)
{
	int &dist;
	int &diff;
	&diff = sp_x(&arg1, -1);
	&diff -= sp_x(&arg2, -1);
	if(&diff > 0)
	{
		get_left(&arg1);
		&dist = &return;
		get_right(&arg2);
		&dist += &return;
	}
	else
	{
		&diff *= -1;
		get_right(&arg1);
		&dist = &return;
		get_left(&arg2);
		&dist += &return;
	}
	if(&diff > &dist)
	{
		//No overlap, too much horizontal distance
		return(0);
	}

	&diff = sp_y(&arg1, -1);
	&diff -= sp_y(&arg2, -1);
	if(&diff > 0)
	{
		get_top(&arg1);
		&dist = &return;
		get_bottom(&arg2);
		&dist += &return;
	}
	else
	{
		&diff *= -1;
		get_bottom(&arg1);
		&dist = &return;
		get_top(&arg2);
		&dist += &return;
	}
	if(&diff > &dist)
	{
		//No overlap, too much vertical distance
		return(0);
	}
	//Sprites intersect!
	return(1);
}


To use it call it with external, with two parametres corresponding for the sprites to check

void main (void)
{
	loop:
	wait(0);
	external("bounds", "intersects", &current_sprite, 1);
	if(&return == 0)
	{
		say("Hey, come closer!", &current_sprite);
	}
	else
	{
		say("Hey, stop touching me!", &current_sprite);
	}
	goto loop;
}


Note that you must leave maps 748 to 768 unsused (or adjust above scripts if you want to use some other range).

Some caveats:
* It does not work on sprites with automatic hitbox sizes.
* It does not care about set_sprite_info or set_frame_frame INI lines
* Weapon scripts (and any other scripts where you call init()) can change the hitbox data.
* If you modify dink.ini, you must remember to regenerate mkchanges.c
* If you modify dink.ini, the old savegames will still have the old hitbox data. You could fix this by getting mkchanges to run on the game load.
Fixing these issues is left as an exercise for the reader.

Edit: argh, why can't DTN escape stuff inside the code-blocks, I definitely don't want "↦" to be parsed as "↦" within. Though at least now I know that html escape sequences work on DTN 🤷

Edit 2: Here's an example D-mod
December 10th 2024, 11:15 PM
custom_simon.gif
SimonK
Peasant He/Him Australia
 
Interesting...

Kinda like inside_box() but on steroids
December 11th 2024, 04:33 AM
custom_iplaydink.gif
iplaydink
Peasant He/Him Sweden
Hmm.. 
Wow. Those are some interesting techniques. Thank you so much for taking the time to try it out. I don't think I'll end up using this exactly but it gave me some ideas on how to make a simpler version that tackles my particular use-case
December 13th 2024, 04:43 PM
custom_robj.png
Robj
Jester He/Him Australia
You feed the madness, and it feeds on you. 
Nice, although does that script also factor in set_sprite_info lines when generating mkbounds, or just hitbox information for load_sequence lines.

Also the weird thing is, in game, the right and bottom hardbox edge are 1 pixel less than actually what is defined in Dink.ini. Well, it works like this: If a right and bottom hardbox value is set to let's say 20, when dink is standing against those edges, he is literally standing at x=20 or y=20 relative to the sprites depth dot (should be at 21, 20 is where the hardbox edge should go up to). The left and top edge don't do this. E.g if left and top edge are -20, Dink is standing at -21 when he is against those edges. That's just how the Dink engine works. I factored this in when I did hardbox detection in Push and Pull by just doing the hardbox info but -1 on the right and bottom edge. It's being really exact, and probably not gonna notice 1 pixel difference, but whatever.
December 13th 2024, 11:34 PM
death.gif
Seseler
Peasant He/Him Heard Island And Mcdonald Islands
 
>Nice, although does that script also factor in set_sprite_info lines when generating mkbounds, or just hitbox information for load_sequence lines.
It does not, as it uses only the per-sequence hitbox definitions. It also does not factor set_frame_frame or seqs without hitbox info on dink.ini
December 19th 2024, 11:46 PM
peasantmb.gif
yeoldetoast
Peasant They/Them Australia
Oh, NOW YOU'VE DONE IT! 
I'm making a list of other things to expose to scripting in the eventual 0.95 release with dink.ini data somewhere near the top (using multiple returns and tables). If there are any other things that you'd prefer to be included I can probably hack them in also.

I'm also planning on bundling Lume and Vivid in case you have any library requests.
December 20th 2024, 07:09 AM
custom_iplaydink.gif
Iplaydink
Peasant He/Him Sweden
Hmm.. 
That's amazing. Lume seem to some function I found myself wishing for (like lerp). Easy access to ini data sounds great too.

Does lua not have vectors and vector math per default? Is there a good library for that?

I was gonna look into a library for https requests but that might be a bit too niche to include.
December 20th 2024, 09:37 AM
peasantmb.gif
yeoldetoast
Peasant They/Them Australia
Oh, NOW YOU'VE DONE IT! 
For lerps, I usually steal one or more from this thread and then convert the output to an integer by rounding.

I haven't tried out a vector library yet, however there are a variety in this list that might be workable. I will try a few out. If you don't want to wait, you can edit init.lua yourself by adding a "require" at the top, placing the file in question alongside serpent.lua, and then whitelisting it for use in scripts around line 890 with library = library like the others. Of course, this will make whatever it is undistributable in the short-term.

Unfortunately an HTTP library would require a C module which wouldn't be platform-independent. I would have to come up with something on the C side of things for that sort of functionality.