Search In
• More options...
Find results that contain...
Find results in...

acs, how make moving mound

Recommended Posts

Say I draw a 20x20 grid of 8 pixel squares in doombuilder (400 total, all probably initially with identical floor heights).
Now I want to use acs to raise a mound composed of these square's floors upward, with the center at any square I desire, this rough pic probably explains it better:

0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0
0 0 0 0 2 2 2 0
0 0 0 2 3 3 3 2
0 0 1 2 3 4 3 2
0 0 0 2 3 3 3 2
0 0 0 0 2 2 2 0

Where 4 is highest floors and 0 is lowest.
There the 4 is highest and the center of the mound. One question is do I need a sector tag for all 400 squares, or can I simply refer to a square by its x/y position somehow. For example GetActorX seems to return the x of a thing.. well can I manipulate sectors by their x/y's as well? Like if I want the center of the mound to move wherever the player goes, maybe constantly check for the player's x/y, then have that be the center of the mound (none of this has a point, just random experimentation to get a better feel for acs).

I probably want this mound to move around at a given angle bumping off the 'walls' of the grid, so in the next frame the 4 might move to the left 1 square, with the rest of the mound following suit. That means, in the above pic, the 3 to the left of the 4 would have to turn into a 4, the current 4 would have to turn in to a 3, etc. It might be easier, if possible, to say 'make floor of square #257 EQUAL 4' and make square #258 EQUAL 3, instead of hassling with lowering vs. raising and by which fractional necessary amounts.

gggmork said:

One question is do I need a sector tag for all 400 squares

Yes. ACS doesn't have access to the PointInSector functions that exist in the Doom engine.

Ok, thanks for the help (again).

Cool, I got a single raised sector (a pole) to bounce around a rectangle much like a pong ball (could possibly be made into doom pong.. though maybe a thing would make more sense for a ball than a sector, whatever).

I tried moving a multi sector shape and it works but has issues that I can't figure out so far (commented in the acs). Here's the wad with acs (for zdoom: doom in hexen format) if you want to bother hassling with my retarded crap:

The basic algorithm is:
*first draw a mound somewhere
*loop: erase the current mound, move mound, draw mound in new location
(to animate it)

The sector tags make this grid kinda weird by the way, because instead of typical paired (x,y) coordinates, it uses a single coordinate (the sector tag) like this sorta:

1 2 3
4 5 6
7 8 9

So say you're on 9 and you want to move left.. just -1 like usual, but if you want to move up its -3 or whatever row length (instead of -1 to a y value)

I'm stumped. I've rewritten the script differently, I've tried Floor_Raise/LowerInstant and Floor_MoveToValue, I've tried setting up some system to make sure things are not processed twice by mistake... But still I get the same results as you do: with a delay or a tagwait, it works but blinks; without there's only the edges of the sector object that move.

Though I've obtained a half-working script this way:

```#define width 16
#define height 21
#define sidelen 4
#define lastrow 321

int shape[25] = {8, 0, 0, 0, 8,0,16, 0,16, 0,0, 0,24, 0, 0,0,16, 0,16, 0,8, 0, 0, 0, 8};

script 1 OPEN
{
int shapei;
int tl = 83;
int xmov = -1;
int ymov = 0 - width;
int x = 0;

while (1)
{

for (shapei = 0; shapei < 25; shapei++)
{
x = tl + (shapei%5) + ((shapei/5)*width);
if (shape[shapei])
{
Floor_MoveToValue(x,512,shape[shapei]);
}
}
delay(3);

for (shapei = 0; shapei < 25; shapei++)
{
x = tl + (shapei%5) + ((shapei/5)*width);
if (shape[shapei])
{
Floor_MoveToValue(x,512,0);
}
}

//this delay(1) is what makes it 'ghostish'. try to comment out the delay
//and it has odd behavior though.. basically if the other script for a single sector
//works then it seems this multi sector script SHOULD work too?
//delay(1);
//tagwait(tl + 2 + 32);

tl += xmov;
if(tl % width == 1 || (tl+sidelen) % width == 0)
xmov *= -1;
tl += ymov;
if(tl + (sidelen * width) >= lastrow || tl <= width)
ymov *= -1;
}

}
```
(Yeah, I put defines everywhere.)

If you try this version of the script, you'll see what I mean by "half-working". It's better and it could even look like it was supposed to do that, but it still only does half the job.

Hmm, yours moves smoothly and even turns, heh. I havn't fully digested it but I like how you removed y from the for loop and looped through the array instead.
The wiki says tagwait waits at least 1 tic no matter what, so that explains why tagwait behaved the same as delay(1). The fact that raseinstant and movetovalue (i didn't even see those functions, probably because I wasn't looking at the page that sorts by name) still don't work right make me have this complete guess:

maybe a floor height can only change ONCE before the next tic/delay or something ('erasing' the old mound, then raising it again changed some floors twice in the same loop; maybe that's not allowed for some reason).

So I might use an array of floor heights for ALL squares as placeholder data for the heights (so I can manipulate them and only affect a number while I erase and draw a new mound, not the actual floor) THEN move the actual floor height only a single time (unless I'm just having a brain fart and this makes no sense).

Can you append to an array in acs, like:
int shape[25] = {8,0};
shape.append(5); // now is {8,0,5}
Or you have to manually type the whole array?

Also I don't understand how:
#define width 16
is different from:
int width = 16;

I don't think ACS' arrays are dynamic, no.

As for the difference between a define and a variable, well, the define is a constant that cannot be changed during runtime (during compilation, it's directly replaced by its value), that's all. I tend to use #defined constants for anything that should never change during runtime.

Try this, it looks like you can only move a floor once per tic.

It's a quick fix, I modded the script Gez put up to do this by adding a border of zeros around the edge of the shape. It draws the new shape and erases the previous one at the same time rather than in different steps.

BTW For this version don't set xmov or ymov greater than 1.

```#define width 16
#define height 21
#define shapewidth 7
#define shapeheight 7
#define shapeelements (shapewidth * shapeheight)

int shape[shapeelements] =
{
0,  0,  0,  0,  0,  0,  0,
0,  8,  0,  0,  0,  8,  0,
0,  0, 16,  0, 16,  0,  0,
0,  0,  0, 24,  0,  0,  0,
0,  0, 16,  0, 16,  0,  0,
0,  8,  0,  0,  0,  8,  0,
0,  0,  0,  0,  0,  0,  0
};

script 1 OPEN
{
int xmov = -1;
int ymov = -1;
int tx = 3;
int ty = 3;
int tagtl = 0;
int tag = 0;
int floorheight = 0;
int i = 0;

while(1)
{
tagtl = ty * width + tx + 1;

for(i = 0; i < shapeelements; i++)
{
tag = tagtl + (i / shapewidth) * width + (i % shapewidth);

floorheight = GetSectorFloorZ(tag, 0, 0);
if(shape[i] != floorheight)
{
Floor_MoveToValue(tag, 512, shape[i]);
}
}
delay(5);

tx += xmov;
if(tx < 0 || (tx + shapewidth) > width)
{
xmov = -xmov;
tx += 2 * xmov;
}

ty += ymov;
if(ty < 0 || (ty + shapeheight) > height)
{
ymov = -ymov;
ty += 2 * ymov;
}
}
}
```

Edit: This one's more complicated but a bit better (it removes the border and can move more than 1):
```#define width 16
#define height 21
#define shapewidth 5
#define shapeheight 5
#define shapeelements (shapewidth * shapeheight)

int shape[shapeelements] =
{
8,  0,  0,  0,  8,
0, 16,  0, 16,  0,
0,  0, 24,  0,  0,
0, 16,  0, 16,  0,
8,  0,  0,  0,  8
};

script 1 OPEN
{
int xmov = -1;
int ymov = 1;
int tx = 3;
int ty = 3;
int tag = 0;
int i = 0;
int prevx = tx;
int prevy = ty;
int minx = 0;
int miny = 0;
int maxx = 0;
int maxy = 0;
int sx = 0;
int sy = 0;
int x = 0;
int y = 0;

while(1)
{
if(prevx < tx)
{
minx = prevx;
maxx = tx + shapewidth;
}
else
{
minx = tx;
maxx = prevx + shapewidth;
}

if(prevy < ty)
{
miny = prevy;
maxy = ty + shapeheight;
}
else
{
miny = ty;
maxy = prevy + shapeheight;
}

for(y = miny; y < maxy; y++)
{
sy = y - ty;
if(sy >= 0 && sy < shapeheight)
{
for(x = minx; x < maxx; x++)
{
sx = x - tx;

tag = y * width + x + 1;
if(sx >= 0 && sx < shapewidth)
{
i = sy * shapeheight + sx;
if(GetSectorFloorZ(tag, 0, 0) != shape[i])
{
Floor_MoveToValue(tag, 512, shape[i]);
}
}
else
{   // Outside of shape
if(GetSectorFloorZ(tag, 0, 0) != 0)
{
Floor_MoveToValue(tag, 512, 0);
}
}
}
}
else
{   // Outside of shape
for(x = minx; x < maxx; x++)
{
tag = y * width + x + 1;
if(GetSectorFloorZ(tag, 0, 0) != 0)
{
Floor_MoveToValue(tag, 512, 0);
}
}
}
}
prevx = tx;
prevy = ty;
delay(3);

tx += xmov;
if(tx < 0 || (tx + shapewidth) > width)
{
xmov = -xmov;
tx += 2 * xmov;
}

ty += ymov;
if(ty < 0 || (ty + shapeheight) > height)
{
ymov = -ymov;
ty += 2 * ymov;
}
}
}
```

This is great and knowing a floor can only move once per tick will probably be helpful in the future. The 0 border seems a clever way to erase the previous frame (I only looked at the first version so far since I saw the edit just now).

It would probably be pretty trivial to make the shape change every frame thus animating the shape itself (the non trivial part would be tediously drawing the frames). If the squares were all 1 pixelish, it could be an animation of a big detailed laughing face or whatever else which is kinda interesting just because I havn't seen this type of thing in doom before. Lots of 1px might have too much slowdown though.

I suck at math so had to stare at this equation gez originally made for a long time, plugging in values for i:
tag = tagtl + (i / shapewidth) * width + (i % shapewidth);
It obviously works but I doubt I'd be able to come up with that myself unless I use my stupider x and y loop I originally used or something.
You guys seem to have no problem handling this type of 'x only, no y' modulus grid. It seems even tricker to me because the main grid and shape BOTH have their own unique width, so it seems you have to think about both simultaneously in a confusing mind jumble as to whether you should go up/down by shapewidth or totalwidth.

My only mentionable programming experience is python's pygame and I've kinda been wondering if I should learn C with some c game library (not c++), because I probably want to learn NON-object oriented programming before (if ever) learning OOP.. but that's kinda hard when most pygame tutorials/examples are OOP.

My guess is tx/ty is probably a convention where the t stands for a geometric translation? But I see tx seems to be the nth square from the left starting on 0 and ty is the same from the top.

I learned in pygame to CAPITALIZE_CONSTANTS (like defines I guess..) but maybe doom code reserves caps for some other purpose or something.

gggmork said:

I suck at math so had to stare at this equation gez originally made for a long time, plugging in values for i:
tag = tagtl + (i / shapewidth) * width + (i % shapewidth);
It obviously works but I doubt I'd be able to come up with that myself unless I use my stupider x and y loop I originally used or something.

It's something I had to familiarize myself with when I was coding in support for more image data formats in SLADE3.

The underlying idea is pretty simple actually. You have a two-dimensional array, like this:

0, 1, 2, 3
4, 5, 6, 7,
8, 9, 10, 11,
etc.

Obviously, you could store it as a mono-dimensional array:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, etc.

This is basically what every image format does by the way. The only subtleties are deciding whether to go row after row (aka, row-major format) or column after column (column-major). For example, Doom sprites and texture patches are stored in a column-major format, whereas the flats are stored in a row-major format. (Then there are additional complexities in the form of compression, and of storing bit planes or pixels, but those digressions are straying us further away from the point.)

Anyway, I've had to devise conversion formulas for turning a column-major array into a row-major one, so it's still kinda fresh in my mind. But it's really very simple math.

Let's look again at our example.
0, 1, 2, 3
4, 5, 6, 7,
8, 9, 10, 11,
etc.

Notice something? Each value is worth 4 less than the value below it (and 4 more than the value above it). Because this is a row-major format, when you go "down" to the next row in the same column, you increase the value by width.

If we number the X and Y axes:
x--0--1--2--3
0--0, 1, 2, 3
1--4, 5, 6, 7,
2--8, 9, 10, 11,
etc.

So you can see that, for instance, the element 6 is X:2 and Y:1. Here the width is 4. Y*width + X = 1*4 + 2 = 6.

So inversely, if you have an index in a mono-dimensional array, you can compute its position in a bi-dimensional array:
6/4 = 1 = Y
6%4 = 2 = X

Divide by width and you get the number of the row, since each row has a width of, well, width. Take the remainder of the division (that's what % does, it gives you the remainder of a division) and you have the column.

So you can just iterate on the total size of the array and compute the X and Y position on the fly from it.