How to script a minigame-type thing?

Started by AntmanJB, Fri 15/02/2008 03:08:57

Previous topic - Next topic

AntmanJB

What I want to do is not actually run a separate mini-game, but simply have a room with objects and stuff which will be the game.

To be precise, it's going to be part of some mysterious machinery and so you'll need to solve the puzzles to start up the machine's actions. What I want to do is this:
- Have a grid at least 5x5, where the whole picture has to be lots of lines connected through a central power tile. All you do is rotate the tiles until the ends match.
- When a tile is connected to the power supply, I want them to light up, whether it's the correct extension or not.

I realise this sounds very confusing, but for a 100% perfect illustration play this game:
http://www.chiark.greenend.org.uk/~sgtatham/puzzles/netgame.exe

So basically I want to do exactly what that game does. Even if I limited the grid to 4x4, which would be very easy to solve, wouldn't that still involve an ENORMOUS amount of scripting? I really can't think of an efficient way to do this.

Edit: It's been about six months since any replies to this...
I now work full time ( :() so I don't have much time to work on my wonderful game.

I was wondering if there's anyone who would be willing to script the whole minigame for me? I will definitely give a credit in the game, and if there's anything else you want I'll see what I can do. There will be a couple more minigames after this one.
Anyone who wants the challenge, go right ahead!
I might go post this in the other board about recruiting people as well.

AntmanJB
"Marge, it takes two to lie. One to lie, and one to listen."

Khris

#1
I'd do it like this:

First off all, break it up into three separate problems.

1. Creating a random puzzle (probably the hardest part, not if you're going to use predefined levels)
2. Handling clicks (the easiest part)
3. Updating the tiles, making them light up or go out

I'll focus on part 3.
First of all, you'll need a simple way or organizing the tiles.
I'd use a one-dimensional array:
Code: ags
struct str_tile {
  bool conn[4];
  bool node;
  bool lit;

  int neighbor[4];
};

That's all we need.

conn[] is an array consisting of 4 elements, conn[0] to conn[3].
I'll use
   0
   |
 3-X-1
   |
   2

So if conn[1] of tile 12 is true, tile 12 currently connects to its right neighbor if conn[3] of that neighbor is also true.

node is true if the tile contains a rectangle in its middle. This is a purely graphical feature of the tile and doesn't concern the game's logic.

lit is true if the tile is lit. Duh.

neighbor[] is an array which holds the index numbers of the adjacent tiles. It is set at the very start, then left alone. If there is no adjacent tile in a particular direction, this is set to -1.

Initializing the puzzle:
-two global vars hold width and height of the puzzle
-neighbors are calculated and set
-every tile's initial node and conn[] bools are set (e.g. by reading them off a level file)
-another global var holds the index of the tile representing the power source

Main loop:
-draw the puzzle (read conn[], node & lit of each tile, use index, puzzle width & height to determine graphical location, draw tiles one by one)
-wait for click
-turn clicked tile by shifting conn[] bools (animate tile)
-update lit bool of every tile

The last step broken down:
-reset every lit to false, set power source tile's lit to true
-outer loop:
* iterate through all tiles
* if not lit, check if connected to a lit tile, set to lit if it is (inner loop: iterate through neighbors, check neighbor's lit, check conn[] of neighbor & tile)
* count tiles that were lit in current run, exit loop if none were lit

That's the basic method I'd use. It might seem a bit overwhelming and I have no idea how proficient you are when it comes to scripting, so if you need specific code help, just let me know.

Edit: Added semicolon to struct definition code.

AntmanJB

Hey, thanks for the speedy and helpful reply!

I'm not terrible at scripting, it's just that I don't know a lot of the commands.
I've just read up on Arrays and Structs to better understand what you said.

Im not 100% sure what you meant by Neighbour being -1 if there's no adjacent tile in a "particular" direction, and also the reading off a level file thing.

I vaguely understand what it's supposed to do, but the details of it escape me a bit. I think I'm going to have to take you up on your offer to help script it! (I'll definitely give you credits in the game.)
To summarise, a tile checks if it's connected by its neighbours to the central power tile, and if so, it lights up. If it's connected to a neighbour but no power, no light.
Is that right?

Oh, and I'll probably use a pre-set puzzle. I want it to be quite large, maybe 7x7 or more. That's an interesting idea about randomising it but I won't bother.

If you don't mind, I'm going to need some step by step scripting advice.
Sorry, thanks!
"Marge, it takes two to lie. One to lie, and one to listen."

Khris

#3
About the neighbors:
Let's assume that it's a 7x7 grid. And let's assume we're using an array called tile[].
Code: ags
str_tile tile[49];

The first two rows will have these index numbers:
 0  1  2  3  4  5  6
 7  8  9 10 11 12 13

So we'll have to set tile[0].neighbor[0] to -1 (there's no tile above tile 0), tile[0].neighbor[1] to 1, tile[0].neighbor[2] to 7 and tile[0].neighbor[3] to -1 (no tile left of it either).

The basic idea is that in order to determine if a tile is lit, we don't have to check if it's connected to the power tile, we just have to check if it's connected to a lit tile.
Since when using this method, only the four tiles adjacent to the power tile can become lit, thus we have to run through all tiles again, and again, and again, until we didn't light further tiles.

Here's setting the neighboring tiles to get you started:
Code: ags
int width=7, height=7;  // global board size vars

// define a function returning the int of the tile next to tile i in direction d
int get_neighbor(int i, int d) {
  int ni;
  if (d==0) ni = i-width;  // tile above i
  if (d==1) {         // right of i
    if (i%width==width-1) ni = -1;  // right edge if index = 6, 13, 20, ..., set neighbor index to -1 in that case
    else ni = i+1;                         // otherwise, neighbor index is index +1
  }
  if (d==2) ni = i+width;  // below i
  if (d==3) {         // left of i
    if (i%width==0) ni = -1;    // left edge
    else ni = i-1;
  }
  if (ni<0 || ni>=width*height) return -1;        // index numbers range from 0-48, if tile above or below was
                                                     // calculated, its index might be out of bounds, return -1 in that case
  return ni;
}

function init_tiles() {
  int i, d;
  while (i<width*height) {      // count from 0 to 48, going through the tile array
    d=0;
    while (d<4) {      // count from 0 to 3, going through all 3 directions
      tile[i].neighbor[d] = get_neighbor(i, d);     // set the tile's neighbor
      d++;
    }
    i++;
  }
}

AntmanJB

Ohhh now I understand! Each of the four neighbours has a number so they can be identified in relation to the given tile.

Having said that, the code you gave went straight over my head. I'm guessing I should put that simply in the room script on first start?

Sorry to be so thick about this.
P.S. Should I be private messaging you about this?
"Marge, it takes two to lie. One to lie, and one to listen."

Khris

Yep, that's right.

The only thing that has to go inside the first start function is "init_tiles();".
The rest of the code should be located somewhere before that, but yes, just put it in the room script.
Code: ags
// room script

// my code here

function room_Load() {
  init_tiles();
}


I'm not sure how to go on from here, I'd really like to help you, but it looks like this is going to become more of a programming 102 sooner or later ;)
It depends on whether you want to learn how to do stuff like this on your own or rather want a complete working solution.

We can take it to PMs if you want, that's up to you, but doing this here is fine, that's what this forum is meant for.

AntmanJB

Okay cool.
I'd like to learn as I'm implementing this stuff, but you'll probably have to put little notations next to each script command you give me to spell out what's happening!

If I put the script in the room right at the start, won't it get an error if there's no function before it? Or maybe it doesn't with those commands.

Anyway, I have to go now, I've designed the solution and I'll do all the objects (tiles) over the next few days and keep this thread updated.

Thanks, I'll definitely be in touch soon to ask for more code! Hehe
"Marge, it takes two to lie. One to lie, and one to listen."

Dualnames

Remember to put Claim events if you don;t want any key_presses/mouse clicks scripts running. Which means that if you want to use f.e arrow keys at your mini game but have them assigned to the on_key_press at the global script add a look-a-like on_key_press section at the top of the room script and after each
if (keycode==32) {
//functions put
ClaimEvent();
}

I usually add this on mini-games , I'm aware that's it's a little bit irrelevant but ok.
Worked on Strangeland, Primordia, Hob's Barrow, The Cat Lady, Mage's Initiation, Until I Have You, Downfall, Hunie Pop, and every game in the Wadjet Eye Games catalogue (porting)

AntmanJB

First of all, Dualnames, you lost me with that post, sorry.

Secondly, AGS is giving me some strife.
I'm using 640x400 backgrounds, yet as mentioned somewhere else, AGS makes them 320x200 for the purposes of the editor. This is stupid, because now I can't align the squares(objects) onto the puzzle board(background) properly.
Each time I move them one pixel, they move TWO pixels because of the resizing.

Due to this, I can't align the squares. Is there any way to get around this? I tried doing numbers like 63.5 but they weren't allowed.
"Marge, it takes two to lie. One to lie, and one to listen."

monkey0506

Well due to the down-scaling of the co-ordinates, you can only use the even numbered co-ordinates of the 640x400 background directly...so you could put an Object at (32,38) and (33,39) and they would be drawn at the same place (AFAIK).

However, you can use transparent pixels to pad your images. So if you put a row of transparent pixels to the left of the image, though your Object's (X,Y) would still read as (32,38), the visible area of the object would then be (33,39).

It's an annoyance, and I think one CJ is currently working on finally ridding the engine of. One of the major problems is that this has always been done, so it could break backward compatibility. Right now (as of AGS v3.0.0) you can use "high-res coordinates" but only with the DrawingSurface functions, used for manually drawing images onto a sprite or the room background surface. CJ has said that if it proves useful he will look into expanding hi-res coordinates to other areas of the engine.

Khris

AntmanJB, you're using a grid here so you'll be best off making the tile size an even number.
I've added more comments to the above code, hope it helps.

AntmanJB

Aaarrrggghhh

I modified each of my tiles plus the background to get them all even. Now they line up great.

However, when placing them all in, I reached the maximum 40 objects per room! I need 49 because it's 7x7! Damn it! Should I resize it 6x6 or is there a way around it?

Oh and KhrisMUC, I went over your script, it makes lots of sense. The only bit I didn't get was the "%" symbol in the left and right commands.
"Marge, it takes two to lie. One to lie, and one to listen."

Khris

You don't have to use objects, you can RawDraw everything.
If you want to show the tile turning you'll have to use a DynamicSprite anyway (in theory it could be done using an animating object, but that's a waste of resources and time).

You could even draw the tiles on the fly. If a simple style like the one netgame uses is enough that is.

The % symbol is used to calculate the remainder of a division. 10 % 4 equals 2.
In a 7x7 board, the index numbers of the left edge are 0, 7, 14, ...
So the remainder of a division by 7 is always 0. For the right edge, the remainder is always 6.

AntmanJB

Ok awesome, I didn't really know what Drawing did, but I read up on it and now I added the rest of the tiles. I guess I should probably replace all the objects with Draws to make it all the same.

I put in the codes you gave, except for the one declaring the struct_tiles and bools thing. (AGS didn't like it, something about needing a '{' or not recognising the struct command.)

The problem is, it's getting an undefined token 'tile' error.

Sorry to be so needy, but I think I'm probably going to need all the scripting for this puzzle.... at least then I can go through it and understand what's happening.

*sigh* I hope my other puzzles don't get this complicated (or if they do, that I can do them).
"Marge, it takes two to lie. One to lie, and one to listen."

SMF spam blocked by CleanTalk