Adventure Game Studio

AGS Support => Advanced Technical Forum => Topic started by: Lt. Smash on Fri 23/02/2007 10:32:54

Title: Room with plates
Post by: Lt. Smash on Fri 23/02/2007 10:32:54
hi,

I have drawn a room with plates, similar to the second challenge in 'Indiana and the Last Crusade'.

Here's a bit of my plates map:


(http://666kb.com/i/am2ushfhiz1kwhzp6.gif)

So my questions now are:

How can I check, if the character is now on the upper left plate or on the 1st plate in the 3rd line?(without using hotspots, regions, etc. because there are more than 50 plates!)

And is it possible to make the character jump(walk) only to one plate at once?

Example: If he's on the first plate(second line) and the player clicks on a plate far away(upper right) he doesn't walk or he only walks to the next plate(which is in the right direction) and than stops.

If it's possible, how?

Hope that someone can help me,

Lt. Smash
Title: Re: Room with plates
Post by: Khris on Fri 23/02/2007 11:11:08
You'll need some advanced scripting to do this, and there's no short explanation.

Some pointers:
You can use a combination of hotspots and regions. Draw hotspot 1 over the first row of plates, hotspot 2 over the second one, aso, then draw region 1 over the first stone in each row, then region 2 over the second stone in each row, aso.
Thus every plate uses an exclusive combination of hotspot and region. This should allow you to use up to 400 plates, in theory.

Use a struct to manage the plates:

// script header
struct plate_struct {
  int hs;  //hotspot
  int re;  //region
  int x;  // center coords
  int y;
  bool deadly;
}

plate_struct plate[51];


Now you'll have to set up every plate. That's tiresome, but there's no quick way to do it.
You could use a function that'll parse a string for the numbers, then use strings like "01 01 020 035 1" to set the params of each plate.

Then intercept Walkto-clicks:
on_mouse_click() {
  ...
  if (button==eMouseLeft && mouse.Mode==eModeWalkto && player.Room==PLATEROOM) CallRoomScript(1);
  else {
    // standard stuff here
  }
  ...
}


In the room script:function getPlate(Character*c) {
  Hotspot*hs=Hotspot.GetAtScreenXY(c.x, c.y);
  Region*re=Region.GetAtScreen(c.x, c.y);
  int i=1;
  while(i<=50) {
    if (plate[i].hs==hs.ID && plate[i].re==re.ID) return i;
    i++;
  }
}

function on_call(int p) {
  cDummy.x=player.x;
  cDummy.y=player.y;
  cDummy.Transparency=100;
  cDummy.Walk(mouse.x+GetViewportX(), mouse.y+GetViewportY(), eNoBlock);
  int p;
  while(cDummy.Moving) {
    p=getPlate(cDummy);
    if (p!=getPlate(player)) cDummy.StopMoving;
    Wait(1);
  }
  player.Walk(plate[p].x+GetViewportX(), plate[p].y+GetViewportY(),eBlock);
  if (plate[p].deadly) {
    //kill player
  }
}


The last function will send a dummy on it's way from where the player char stands to where the player clicked, then wait for it to reach another plate, then send the player char to the center of that plate.

This is by no means a full step-by-step solution, so please don't consider it as one.

EDIT:
Added GetViewport stuff to Walk commands.

Btw, walkable areas should have a width of at least 3 pixels, otherwise AGS's pathfinding will go haywire.
Title: Re: Room with plates
Post by: Lt. Smash on Fri 23/02/2007 18:03:57
When I click on a plate the character always moves to his starting position(room entering position) and then he walks to the plate.

Do you know how I can fix that?
Title: Re: Room with plates
Post by: Khris on Fri 23/02/2007 18:32:20
Hmmm, that's weird.

I'd comment out this line:
cDummy.Transparency=100;

and check what Dummy does after clicking.

Wait, you did set .x and .y of all the plates, right?
And you prevented ProcessClick from still getting called in on_mouse_click, right?

Cause if you didn't, that's what might cause it.
Title: Re: Room with plates
Post by: Lt. Smash on Sat 24/02/2007 12:15:07
QuoteI'd comment out this line:

cDummy.Transparency=100;


and check what Dummy does after clicking.

I've done that. The dummy walks to the plate I've clicked but he doesn't stop on the plate between. When he stops the character walks to the starting point and then he walks to the plate where the dummy stands.(crossing some other plates)
But sometimes the dummy stops on a plate between the plate I've clicked but the character walks to the plate I've clicked.
Sometimes also the other way round -> dummy walks to plate character stops somewhere between.

QuoteWait, you did set .x and .y of all the plates, right?

Yes, I did that.

QuoteAnd you prevented ProcessClick from still getting called in on_mouse_click, right?

I'm not quite sure, because I use the FOA template.

The code is:
// Handling room clicks:
//-----------------------------------------------------
else if (button==LEFT || button==RIGHT) {
   
    if (button==RIGHT) SetMode(GetDefaultAction(mouse.x,mouse.y));// set the default mode when right click
   
    if (button == LEFT && GetMode()==WALK && player.Room==4)// plates room on_call
   {
  CallRoomScript(1);
   }

    if (Extension(GSlocname)==EXITS_EXTENSION){// if its an 'exit'
        HighlightActionBar();
        WalkOffScreen();
    }
    else if (ALWAYS_GO_TO_HOTSPOTS==1 && ExtensionEx(2,GSlocname)!='d' && GetLocationType(mouse.x,mouse.y)!=0 ){
       UpdateActionBar(mrx,mry);
       HighlightActionBar();
       if (Go()==1)   ProcessAction(GetMode(),mrx,mry);
       return;
    }
   
    else ProcessAction(GetMode(),mrx,mry);
  }
//-----------------------------------------------------


Hope you can help me.

EDIT:

where exactly must I set up the plates?
Title: Re: Room with plates
Post by: Khris on Sat 24/02/2007 12:48:28
You can set up the plates in game_start, or in player enters screen (before fadein) of the room. You can use ... (after fadein) or even first time ..., too.

ProcessAction will still get called, just insert "return;" directly after "CallRoomScript(1);".

I haven't tested my code, but it looks ok. You can debug by changing on_call:
function on_call(int param) {
  if (param==1) {

    // old code here

  }
  else {
    cDummy.x=mouse.x+GetViewportX();
    cDummy.y=mouse.y+GetViewportY();
    Display("%d", getPlate(cDummy));
  }
}


Then add this line to on_key_press():

  if (keycode=='D') CallRoomScript(2);

Now move the mouse over different plates and press D, this should display the number of the plate.
Title: Re: Room with plates
Post by: Lt. Smash on Sat 24/02/2007 13:03:03
When I insert Return; after "CallRoomScript(1);" the character doesn't move. Only the dummy.

When I move the mouse over a plate and press 'D' the char always says  0.

Could you write an example of how you set up a plate?
Title: Re: Room with plates
Post by: Khris on Sat 24/02/2007 17:31:18
Well, if getPlate() always returns 0, the whole code won't work, so there's your answer.

Setting up a plate can be done like this:

plate[1].hs=1;
plate[1].re=1;
plate[1].x=30;
plate[1].y=20;
plate[1].deadly=true;

As I mentioned before, this is tedious and not very elegant.

I'd do it like this:

1. Add a new module.
2. Move the struct definition and "plate_struct plate[X];" to its header.
3. Add this to the module's main script:
function setPlates() {
  File*txt=File.Open("d:\plates.txt", eFileWrite);
  txt.WriteRawLine("function game_start() {");
  Overlay*o=Overlay.CreateTextual(0,0,200,2,15,"");
  int i=1;
  int x;
  int y;
  while(i<=PLATECOUNT) {
    o.SetText(200,2,15,"Click the center of plate %d", i);
    while(!mouse.IsButtonDown(eMouseLeft) && !mouse.IsButtonDown(eMouseRight)) {
      Wait(1);
    }
    bool deadly=mouse.IsButtonDown(eMouseRight);
    while(mouse.IsButtonDown(eMouseLeft) || mouse.IsButtonDown(eMouseRight)) {
      x=mouse.x+GetViewPortX();
      y=mouse.y+GetViewPortY();
      Wait(1);
    }
    Hotspot*hs=Hotspot.GetAtScreenXY(x, y);
    Region*re=Hotspot.GetAtScreenXY(x, y);
    txt.WriteRawLine("  plate[%d].hs=%d;", i, hs.ID);
    txt.WriteRawLine("  plate[%d].re=%d;", i, re.ID);
    txt.WriteRawLine("  plate[%d].x=%d;", i, x);
    txt.WriteRawLine("  plate[%d].y=%d;", i, y);
    String s;
    if (deadly) s="true";
    else s="false";
    txt.WriteRawLine("  plate[%d].deadly=%s;", i, s);
    i++;
  }
  txt.WriteRawLine("}");
  txt.Close();
  o.Remove();
}

function on_key_press(int keycode) {
  if (keycode=='P') {
    setPlates();
    ClaimEvent();
  }
}


4. Run the game, go to the room with the plates and press p.
5. Click the center of each plate with the left mouse button if it's a safe plate, with the right button, if it's deadly. (Change the room's background, if necessary.)
6. Quit the game.
7. Open D:\plates.txt and check if it looks ok.
8. If it does, add the contents of the textfile to the module.

You might have to add scrolling code to on_key_press, if it's a scrolling room.
Title: Re: Room with plates
Post by: Lt. Smash on Sun 25/02/2007 11:03:35
Quotetxt.WriteRawLine("  plate[%d].hs=%d;", i, hs.ID);
txt.WriteRawLine("  plate[%d].re=%d;", i, re.ID);
txt.WriteRawLine("  plate[%d].x=%d;", i, x);
txt.WriteRawLine("  plate[%d].y=%d;", i, y);

That doesn't work because WriteRawLine doesn't support variables(I think so).

Could you make a demogame or a template where the whole plates thing work?
Title: Re: Room with plates
Post by: Khris on Sun 25/02/2007 14:50:16
Here's a tested, working version:
// Main script for module 'plate_setup'

function setPlates() {
  File*txt=File.Open("plates.txt", eFileWrite);
  txt.WriteRawLine("function game_start() {");
  Overlay*o=Overlay.CreateTextual(0,0,200,2,15,"");
  int i=1;
  int x;
  int y;
  mouse.ChangeModeGraphic(eModeWait, mouse.GetModeGraphic(eModePointer));
  while(i<=10) {
    o.SetText(200,2,15,"Click the center of plate %d", i);
    while(!mouse.IsButtonDown(eMouseLeft) && !mouse.IsButtonDown(eMouseRight)) {
      Wait(1);
    }
    bool deadly=mouse.IsButtonDown(eMouseRight);
    while(mouse.IsButtonDown(eMouseLeft) || mouse.IsButtonDown(eMouseRight)) {
      x=mouse.x+GetViewportX();
      y=mouse.y+GetViewportY();
      Wait(1);
    }
    Hotspot*hs=Hotspot.GetAtScreenXY(x, y);
    Region*re=Region.GetAtRoomXY(x, y);
    String w;
    w=String.Format("  plate[%d].hs=%d;", i, hs.ID);
    txt.WriteRawLine(w);
    w=String.Format("  plate[%d].re=%d;", i, re.ID);
    txt.WriteRawLine(w);
    w=String.Format("  plate[%d].x=%d;", i, x);
    txt.WriteRawLine(w);
    w=String.Format("  plate[%d].y=%d;", i, y);
    txt.WriteRawLine(w);
    String s;
    if (deadly) s="true";
    else s="false";
    w=String.Format("  plate[%d].deadly=%s;", i, s);
    txt.WriteRawLine(w);
    i++;
  }
  txt.WriteRawLine("}");
  txt.Close();
  o.Remove();
}

function on_key_press(int keycode) {
  if (keycode=='P') {
    setPlates();
    ClaimEvent();
  }
}


I'll make a demo later.
Title: Re: Room with plates
Post by: Lt. Smash on Sun 25/02/2007 18:34:17
That works fine!

But the other code with the dummy doesn't work.
Title: Re: Room with plates
Post by: Lt. Smash on Wed 28/02/2007 14:08:26
KhrisMUC could you post a working code for the plates room, please?
It would be very useful because the code you first post doesn't really work.
Title: Re: Room with plates
Post by: Khris on Wed 28/02/2007 17:37:10
I haven't had the time yet, I might do a working example tonight. Please be patient, since I didn't get your paycheck yet ;)
Title: Re: Room with plates
Post by: Lt. Smash on Thu 08/03/2007 19:00:18
I only wanted to ask you, if you had enough time of making an example and if you could post it, please?

I think the problem is in the getPlate() function but I could be wrong.
Title: Re: Room with plates
Post by: Khris on Thu 08/03/2007 20:14:02
Yes, I had a go at the plate room myself and getPlate() seems to return 0 regardless of the char's position.
I'll look into it soon. (Had a busy week.)