AddPoint

Started by Slasher, Sat 20/04/2013 10:53:46

Previous topic - Next topic

Slasher

Hi

Khris mentioned about AddPoint a little while back. Could he (or someone else who knows) please explain this a bit more as it is would come in very handy. Referencing areas of a Room would be a great asset.

cheers



Khris


Cerno

Since it was in reply to one of my questions, here's my take on this excellent suggestion, if I find the time I'll try to code it and add some working code (if Khris does not beat me to it) ;).

Create a new global script
Add a struct "Point" with members "int room", "int x", "int y" and "string name", alternatively "name" can be enum, whatever you prefer.
Add a dynamic array "Points" of type Point
Add a function AddPoint(int room, int x, int y, string name) that just adds the values into a new Point struct and adds that struct to the array.

Add an extender function WalkTo(this Character*, string fname)
Add an extender function Reposition(this Character*, string fname)
Both extender functions simply run a loop over the dynamic array and compare the current Point.name with fname and calls the correct this.Walk(Point.x, Point.y) or this.Changeroom(Point.room).

I would prefer using enums because you will be notified of typos at compile time and can also make sure that the length of the array is the same as the number of enum values so any inconsistencies will be revealed at compile time or when the game starts.

I would code it like this, although there might be better ways to do this.
123  Currently working on: Sibun - Shadow of the Septemplicon

DoorKnobHandle

Quote from: Cerno on Sat 20/04/2013 12:44:17
Add a dynamic array "Points" of type Point

Won't work as dynamic arrays of custom structs are not allowed. You'll need to use a primitive array with a constant maximum array size.

Slasher

#4
Hi

This is only a trying example to get the code working. It works on the throw of a dice. Of course I would prefer to use co-ord reference names.

I made an int(50) named Point

Then functioned these in Global Header:

Code: AGS

function Points1()
{
 cEgo.Walk(66, 389,  eBlock, eAnywhere);
}

function Points2()
{
 cEgo.Walk(66, 301, eBlock, eAnywhere);
}


function Points3()
{
 cEgo.Walk(66, 135,  eBlock, eAnywhere);
}


Then Run them:

Code: AGS

function Button2_OnClick(GUIControl *control, MouseButton button) // STOP DICE
{
 object[2].StopAnimating();
 if (object[2].Frame==0)
{
  Points1();
}   
 else if (object[2].Frame==5)
{
 Points2(); 
}   
 else if (object[2].Frame==1)
{
 Points3();
}
}


As I have said, I would prefer to reference co-ords by NAME.


Cerno

#5
I managed to flesh out my implementation. This works after a short test, improvements are welcome.
This implementation uses enums, Khris's suggested string variant should work similarly.

Advantages:
    Compiler error in case of enum typos (will not happen if you use strings)
    No need to specify the array size
    Slightly faster, since you won't have to loop over the point array (should be negligible though)
Disadvantage:
    You will have to define your room points within this module, which reduces flexibility and is not the best style.
    Edit: Also it is a bit of a hassle that you have to add an enum value in one script file and the point definition in another just to add a single point.

Code: AGS
//Addpoints.ash
enum RoomPoint
{
//enter names for your room points below
  ELibraryExitLeft, 
  ELibraryExitRight, 
  ECaveExitTop, 
  ECliffJumpPoint, 
//enter names for your room points above
//do not change the next line
  ERoomPointCount // automatically contains the number of total room points
};

import function Addpoint(int room, int x, int y, RoomPoint point);
import function ChangeRoomToPoint(this Character*, RoomPoint point);
import function WalkToPoint(this Character*, RoomPoint point, BlockingStyle block = eNoBlock, WalkWhere walkWhere = eWalkableAreas);

// Addpoints.asc
struct RoomPointInfo
{
  int room;
  int x;
  int y;
  RoomPoint point;
};

RoomPointInfo roomPoints[ERoomPointCount];

function Addpoint(int room, int x, int y, RoomPoint point)
{
  roomPoints[point].room = room;
  roomPoints[point].x = x;
  roomPoints[point].y = y;
}

function ChangeRoomToPoint(this Character*, RoomPoint point)
{
  this.ChangeRoom(roomPoints[point].room, roomPoints[point].x, roomPoints[point].y);
}

function WalkToPoint(this Character*, RoomPoint point, BlockingStyle block, WalkWhere walkWhere)
{
  this.Walk(roomPoints[point].x, roomPoints[point].y, block, walkWhere);
}


Usage:
Code: AGS
// In game_start you can add points like this:
Addpoint(4, 270, 460, ECaveExitTop);
Addpoint(1, 185, 400, ELibraryExitLeft);

//In any room, you can use the functions like this:
player.WalkToPoint(ECaveExitTop, eBlock);
cOrk.ChangeRoomToPoint(ELibraryExitLeft);


Question: Is it possible to add error handling to AGS, like assertions or something?
I have some ideas to make this more robust against errors. I could work around this by using any invalid command, but I was hoping there was a better way.
123  Currently working on: Sibun - Shadow of the Septemplicon

Slasher

#6
Hi

I have done that all and I am trying it out.

cheers



Crimson Wizard

Quote from: Cerno on Sat 20/04/2013 18:27:03
Question: Is it possible to add error handling to AGS, like assertions or something?
I have some ideas to make this more robust against errors. I could work around this by using any invalid command, but I was hoping there was a better way.
You may use Display command, or DisplayTopBar (displays text with custom header) to show error message. You may also call AbortGame to instantly quit in case of irrecoverable error (with error message).

Slasher

#8
I get this error after compiling ok and then try to run game:

Script link failed: Runtime error: Unresolved import 'Character::WalkToPoint;


All appears to be working. I will run some reals tests.

Thank you Cerno ;)

slasher





Cerno

Thanks, Crimson Wizard, AbortGame() will do what I want, it even shows the position where the error occurred in the code. Nice.

@slasher: Good luck, I hope it will continue to work.

I was thinking of adding a valid flag to the point struct and initializing it with false, so that if you try to access a point that has not been defined you get an error message and the game stops (better than the character trying to walk to some arbitrary uninitialized position.) However I have trouble implementing the initialization to false. I assume AGS does not have constructors or anything? I could add an init() function to perform the task, but it would force the user to call it manually. Is there no better way?
123  Currently working on: Sibun - Shadow of the Septemplicon

DoorKnobHandle

Cerno, your enum to array size trick is not optimal because in AGS enums actually are represented by integers starting at ONE instead of ZERO. So you're constantly allocating your array to be one element too large. This is not a deal breaker but something to keep in mind. And you can't array[enum - 1] either, as AGS does not consider a constant subtracted by another constant a constant.

Cerno

#11
Okay, thanks for the info. :)
Something new everyday with AGS :-D

I guess it will work nevertheless, right? Since we can only access the array via the specified functions and those require enum values, we can never access the array position 0 by mistake.
The only real disadvantage I see is the wasted memory. If the AGS array is implemented efficiently, we should waste one struct object which should amount to about 4 bytes. Compared to the string solution which would take much more memory to store all the strings, it's quite insignificant (plus string comparisons are slower than direct array look ups).

So I guess I can live with that caveat ;)

But you are right, I should keep this in mind. Should I ever use the ERoomPointCount to actually get the number of points it would be quite the surprise if I have one more than I reckoned with.

<offtopic> By the way, congrats on your MAGS award, I've seen one of your livestream vids (I plan on watchin more of them) and found them very inspiring. Your working speed is quite astounding.</offtopic>
123  Currently working on: Sibun - Shadow of the Septemplicon

Cerno

I just realized another severe disadvantage (at least for me). Since the call to WalkToPoint has a similar signature that Walkto, the following error can happen:

What you want to use:
Code: AGS
player.WalkToPoint(eLibrary, eBlock);

What you accidentally use (typo):
Code: AGS
player.Walk(eLibrary, eBlock);


The second line will happily compile and cause the player to walk to the coordinates x = eLibrary and y = eBlock (enums are numbers after all), which would be somewhere in the top left corner. :/
123  Currently working on: Sibun - Shadow of the Septemplicon

Crimson Wizard

#13
POST DELETED

Actually, nevermind :).

SMF spam blocked by CleanTalk