Trouble with grid movement sugarcube

Hi everyone. I’m working on a small grid based combat system and am coding the player movement. The code works, as in, I can move the player. Except after half a dozen moves or so (the number changes) the player starts jumping around into different parts of the grid randomly with each move. I don’t understand why.

I’ll try to only post the code that seems relevant though it might be a lot.

First I make the grid in StoryInit:

<<for _y = 0; _y < $gridSize[1]; _y++>>
	<<for _x = 0; _x < $gridSize[0]; _x++>>
		<<set $grid["" + _x + "," + _y] = {pos: [_x,_y], type: "floor", status: [], contains: ""}>>
	<</for>>
<</for>>

<<set $grid["1,1"].contains = "Player">>

All good. I couldn’t figure out a better way to do the object creation because setting the $grid[objects] as just numbers without quotes made some issues.

Then the grid gets displayed in the “Battle” passage, calling a widget repeatedly.

	<<for _y = 0; _y < $gridSize[1]; _y++>>\
		<tr>\
		<<for _x = 0; _x < $gridSize[0]; _x++>>\
			<td><<DisplayGrid _x _y>></td>\
		<</for>>\
		</tr>\
	<</for>>\
<<widget "DisplayGrid">>\
<<set _cell = "" + _args[0] + "," + _args[1]>>\

DisplayGrid sets the _cell temp var which I pass along to other widgets in this process.

Later in DisplayGrid these get called in each cell:

<td><<GetMove _cell "up">></td>\
<td></td></tr>\
<tr><td><<GetMove _cell "left">></td>\
<td><<GetMove _cell "right">></td>\
</tr><tr><td>\
<td><<GetMove _cell "down">></td>\
<td></td></tr>\

And I’ll put the whole GetMove code in because I think this must be where something bad is happening.

<<widget "GetMove">>\
<<set _cell = _args[0]>>\
<<capture _cell>>\
	<<if $grid[_cell].contains == "Player">>\
		<<if _args[1] == "up">>\
			<<link ⬆️ $passage>><<Move "Player" "up" _cell>><</link>>\
		<<elseif _args[1] == "right">>\
			<<link ➡️ $passage>><<Move "Player" "right" _cell>><</link>>\
		<<elseif _args[1] == "left">>\
			<<link ⬅️ $passage>><<Move "Player" "left" _cell>><</link>>\
		<<elseif _args[1] == "down">>\
			<<link ⬇️ $passage>><<Move "Player" "down" _cell>><</link>>\
		<</if>>\
	<</if>>\
<</capture>>\
<</widget>>\

The $passage link refreshes the page.

Finally, the Move widget.

<<widget "Move">>\
    <<set _pos = $grid[_args[2]].pos>>\
	<<if _args[1] == "up">>\
		<<set $grid[_args[2]].contains = "">>\
        <<set _pos[1] -= 1>>\
		<<set _cell =  "" + _pos[0] + "," + _pos[1]>>\
		<<set $grid[_cell].contains = "Player">>\
	<<elseif _args[1] == "right">>\
		<<set $grid[_args[2]].contains = "">>\
        <<set _pos[0] += 1>>\
		<<set _cell =  "" + _pos[0] + "," + _pos[1]>>\
		<<set $grid[_cell].contains = "Player">>\
	<<elseif _args[1] == "left">>\
		<<set $grid[_args[2]].contains = "">>\
        <<set _pos[0] -= 1>>\
		<<set _cell =  "" + _pos[0] + "," + _pos[1]>>\
		<<set $grid[_cell].contains = "Player">>\
	<<elseif _args[1] == "down">>\
		<<set $grid[_args[2]].contains = "">>\
        <<set _pos[1] += 1>>\
		<<set _cell =  "" + _pos[0] + "," + _pos[1]>>\
		<<set $grid[_cell].contains = "Player">>\
	<</if>>\
<</widget>>\

So, the player moves from cell to cell with each arrow pressed, but then begins to jump around, not move at all, or skip cells.

I separated the process into individual widgets for my own sake because I like having everything segmented like that, but could that be what’s messing with it? I tried changing _cell to _cell2 in the Move widget in case the temps having the same name was messing with it somehow, but that didn’t fix it. Also, if you see a better way to do any of my other code I’m all for it.

Also, something else I was wondering since I have a thread. If you make a lot of objects in sugarcube, does that slow things down? Is there a way to clear some objects completely, like, after a combat? I generate new enemies and want to remove them after each combat.

The code examples you’ve supplied for the “battle passage” Passage and the “DisplayGrid” widget are incomplete, and structurally invalid.

eg. the loops in the “battle passage” Passage example have the outer _y representing the grid-row, and the inner _x representing the column within that row, which is “common” when generating a HTML table. But you’re passing those values as column, row to the “DisplayGrid” widget.

eg. the “battle passage” Passage example creates table rows using <tr> elements, but there is no parent <table> element wrapping those rows.

eg. the entire contents of the 2nd “DisplayGrid” widget related example appears like it should be wrapped within a <table> element, but it isn’t.

eg. the first line of the the same example uses a <td> element to create a table-detail-cell, but there is no table-row start-tag before it.

eg. the 5th line of the same example includes a table-detail-cell start-tag without an associated end-tag.

All of which makes it impossible to test your code examples.

Is the final HTML structure meant to be a grid of table cells, where each cell contains a grid of table cells.

There is a table tag and the table displays correctly, I wasn’t sure what was appropriate to include.

Yes, it’s a grid of cells with a grid of 9 inside each cell.

Here’s that full widget:

<<widget "DisplayGrid">>\
<<set _cell = "" + _args[0] + "," + _args[1]>>\
<div class = "cell"><table>\
<<if $grid[_cell].type == "wall">>\
<tr>\
<td>wall</td>\
<td></td>\
<td>wall</td></tr>\
<tr><td></td>\
<td></td>\
<td></td>\
</tr><tr>\
<td>wall</td>\
<td></td>\
<td>wall</td></tr>
\
<<else>>\
<tr>\
<td></td>\
<td><<GetMove _cell "up">></td>\
<td></td></tr>\
<tr><td><<GetMove _cell "left">></td>\
<td><<= $grid[_cell].contains>></td>\
<td><<GetMove _cell "right">></td>\
</tr><tr><td>\
	<<if $grid[_cell].status.includes("wet")>>\
		💧\
	<</if>></td>\
<td><<GetMove _cell "down">></td>\
<td></td></tr>\
<</if>>\
</table></div>\
<</widget>>\

Walls aren’t working right now and are only display, but I’ll do that later.

Other relevant info if you’re trying to test the grid:

In StoryInit:

<<set $grid = {}>>
<<set $gridSize = [7,7]>>
<<set $passage = "Battle">>

In the stylesheet:

.grid table tr td{
	 border: 1px solid white;
  	text-align: center;
}

.grid td {
 	height: 60px;
	width: 60px;
}

.grid {
 	  	font-size: 9px;
}

.cell table tr td{
	 border: none;
  	text-align: bottom;
}

.cell td {
 	height: 0px;
	width: 0px;
}

I think that plus the widgets should be enough to recreate the grid. You’d need a widget passage and the “Battle” passage with the grid display code from earlier.

Currently I see two related issues with the code:

1: You are modifying the pos property of a cell each time the “Player” moves out of it.

When the contents of the $grid variable is generated, the pos property of each “cell” is assigned a two element Array that equals the cell’s String based key.
eg. the pos of cell “1,1” will be [1,1], the pos of cell “3,4” will be [3,4]

When the <<Move>> widget is called it assigns a reference to the current cell’s pos Array to the _pos variable, and then updates one of the elements of that array based on the direction of movement.

eg. if the cell is “1,1” and the movement is up then the 2nd element of the pos Array is decreased by one…

<<if _args[1] == "up">>
		<<set $grid[_args[2]].contains = "">>

		<<set _pos[1] -= 1>>  /* decrease the 2nd element by one */

		<<set _cell =   _pos[0] + "," + _pos[1]>>
		<<set $grid[_cell].contains = "Player">>

So if the pos property of the “1,1” cell contained [1,1] before moving up, it would now contain [1,0].

And if the “Player” directly returned (moved down) to cell “1,1” and then moved up again, the pos property of cell “1,1” would now contain [1,-1]. And that caused an error when I ran my test project.

I don’t understand why you are altering the Array value of the cell’s pos property, but if I assume that such is an unintended side effect then the following code might be a better variation of the <<Move>> widget…

<<widget "Move">>
	/* Destructure pos Array into _x and _y variables */
    <<set [_x,_y] to $grid[_args[2]].pos>>
    
	<<if _args[1] == "up">>
		<<set $grid[_args[2]].contains = "">>
        <<set _y -= 1>>
		<<set _cell =   _x + "," + _y>>
		<<set $grid[_cell].contains = "Player">>
	<<elseif _args[1] == "right">>
		<<set $grid[_args[2]].contains = "">>
        <<set _x += 1>>
		<<set _cell =  _x + "," + _y>>
		<<set $grid[_cell].contains = "Player">>
	<<elseif _args[1] == "left">>
		<<set $grid[_args[2]].contains = "">>
        <<set _x] -= 1>>
		<<set _cell =  _x + "," + _y>>
		<<set $grid[_cell].contains = "Player">>
	<<elseif _args[1] == "down">>
		<<set $grid[_args[2]].contains = "">>
        <<set _y += 1>>
		<<set _cell =  _x + "," + _y>>
		<<set $grid[_cell].contains = "Player">>
	<</if>>
<</widget>>

2: Currently the “Player” can attempt to move outside the bounds of the grid, which results in an error.

Currently the code always shows all movement arrows, even when selecting one may cause the “Player” to move outside the bounds of the grid. So the <<Move>> widget can increase / decrease an axis value pass the grid’s co-ordinate range, which results in the widget trying to reference an property of $grid that doesn’t exist. Thus causing an error.

Thanks a lot for the write-up. I thought by using the temp variable _pos and changing that variable’s value, I was leaving the .pos property unchanged. I didn’t intend for editing the temp to edit the original. Is it because I set the whole array into one temp that that happens? So if I set it your way, with two separate variables to destructure the array, when I play with those variables, the .pos property won’t update?

When you assign a Primitive value, like a Number or a Boolean, to a variable or a property the value itself is stored in that variable or property.

However, when you assign an Object data-type like Array to a variable or property the Object itself is stored else where, and a reference to the Object is stored in that variable or property.

When you assign value of one variable or property to another…

<<set $a to 10>>
<<set $b to $b>>

…a copy of the first variable’s value is assigned to the 2nd variable.

If, like the above example, the value is of a Primitive data-type then you end up with two variables both having there own copy of the same value.

However, if the value in the 1st variable is an Object data-type…

<<set $a to [1,2,3]>>
<<set $b to $a>>

…then the a copy of the Reference stored in the 1st variable is assigned to the 2nd variable, so now both variables are referencing the same Object.

warning: During the Passage Transition process the state of all known variables is cloned, and the “copy” of that state is made available to the Passage being transitioned to.

This cloning breaks Object Reference Integrity, which means that while the $a and $b variables referenced the same Array before such a Passage Transition, those two variables will reference different copies of that Array after the transiton.

Ok! I didn’t know how that worked. Thanks a lot :slight_smile: