Room with plates

Started by Lt. Smash, Fri 23/02/2007 10:32:54

Previous topic - Next topic

Lt. Smash

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:




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

Khris

#1
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:

Code: ags
// 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:
Code: ags
on_mouse_click() {
  ...
  if (button==eMouseLeft && mouse.Mode==eModeWalkto && player.Room==PLATEROOM) CallRoomScript(1);
  else {
    // standard stuff here
  }
  ...
}


In the room script:
Code: ags
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.

Lt. Smash

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?

Khris

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.

Lt. Smash

#4
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:
Code: ags
//	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?

Khris

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:
Code: ags
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():

Code: ags
  if (keycode=='D') CallRoomScript(2);


Now move the mouse over different plates and press D, this should display the number of the plate.

Lt. Smash

#6
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?

Khris

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:

Code: ags
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:
Code: ags
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.

Lt. Smash

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?

Khris

Here's a tested, working version:
Code: ags
// 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.

Lt. Smash

#10
That works fine!

But the other code with the dummy doesn't work.

Lt. Smash

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.

Khris

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 ;)

Lt. Smash

#13
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.

Khris

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.)

SMF spam blocked by CleanTalk