Adventure Game Studio

AGS Support => Beginners' Technical Questions => Topic started by: bx83 on Sun 11/07/2021 02:40:28

Title: Managed types?
Post by: bx83 on Sun 11/07/2021 02:40:28
I have, in Tankman.ash (trying to make my own Pacman clone):


...

struct Crowd {
  int aX;     //actual x eg. 312
  int aY;     //actual y eg. 472
  int tX;     //target x - target char/tile x coord - '3' for 'tile 34'
  int tY;     //target y - target char/tile y coord - '4' for 'tile 34'
 
  int mode;   //mode, 0=chase, attack=1, scatter=2, scared=3, regen=4
  int timer1; //timer to stay in mode
  int timer2; //timer to stay aggressive
  int waittimer;  //timer for wait between attacks/cooldown time
  int dir;    //0=up, 1=right, 2=down, 3=left  Never Eat Soggy Wheatbix
  int nogo;   //opposite direction to where your pointed; dir/nogo 0/2 1/3 2/0 3/1
  int moving; //1=moving to next coord, 0=not moving, decision/action time
  int id;     //0..4
 
  import void SetupCrowds();
  import void ResetCrowd(Crowd *crowd);    <--ERROR!
  import void ResetTimers(Crowd *crowd);
  import void FindTargetBasedOnMode(Crowd *crowd, int mode);
  import void MoveCrowdToTarget(Crowd *crowd);
};


Tankman.ash(17): Error (line 17): Cannot declare pointer to non-managed type

When I take away the '*' pointer, it compiles fine - but it means it sends a copy of Crowd, then loses it. What am I doing wrong to not use pointers?
Title: Re: Managed types?
Post by: fernewelten on Sun 11/07/2021 10:54:39
As an aside, beware of AGS "pointers"; they look like C pointers but aren't. As far as I know, AGS is nearer to C# in this respect than to C.

Concerning your question: If you define
Code (ags) Select

struct SOMETHING {
...
}


... then you automatically define a type that is NON-managed, and all variables you declare with it will automatically need to be NON-managed: So you need to declare the variable as "SOMETHING my_variable;" (without asterisk).

On the other hand, if you define:
Code (ags) Select

managed struct SOMETHING {
...
}


... then you have a type that is managed, and all variables you declare with it must be managed: So you need to declare your variables "SOMETHING *my_variable;" (with asterisk)

So the question whether you want a "pointer" variable or a non-"pointer" variable, that is something you need to already decide at type declaration time. In principle, the "*" would be superfluous because the compiler already knows whether you want the symbol after it has read the type you have declared.

Your line
Code (ags) Select
import void ResetCrowd(Crowd *crowd);
has a parameter that is managed. This will only work if you start the declaration with "managed struct Crowd".
Title: Re: Managed types?
Post by: bx83 on Sun 11/07/2021 11:57:23
Okay, I've changed my code and got rid of most errors; but still some remain:

Tankman.ash
// new module header
struct __Tankman
{
  import function Enable();
  import function Disable();
};
import __Tankman Tankman;


#define numRows     8
#define numCols     10

struct Tile {
  int x;  //x centre of tile
  int y;  //y centre of tile
  String refName;
  bool up;      //has up direction
  bool right;   //has right direction
  bool down;    //has down direction
  bool left;    //has left direction
  int content;  //is it a free tile or blocked space?
};

managed struct Grid {
  import void SetupGridValues();
  import Tile Grid::RandomFreeSquare();         <---------------ERROR: Tankman.ash(26): Error (line 26): Member variable cannot be struct
  import Tile Grid::TileFromRef(String refName);
};


#define numCrowds   5

managed struct Crowd {
  int aX;     //actual x eg. 312
  int aY;     //actual y eg. 472
  int tX;     //target x - target char/tile x coord - '3' for 'tile 34'
  int tY;     //target y - target char/tile y coord - '4' for 'tile 34'
 
  int mode;   //mode, 0=chase, attack=1, scatter=2, scared=3, regen=4
  int timer1; //timer to stay in mode
  int timer2; //timer to stay aggressive
  int waittimer;  //timer for wait between attacks/cooldown time
  int dir;    //0=up, 1=right, 2=down, 3=left  Never Eat Soggy Wheatbix
  int nogo;   //opposite direction to where your pointed; dir/nogo 0/2 1/3 2/0 3/1
  int moving; //1=moving to next coord, 0=not moving, decision/action time
  int id;     //0..4
 
  import void SetupCrowds();
  import void ResetCrowd(Crowd *crowd);
  import void ResetTimers(Crowd *crowd);
  import void FindTargetBasedOnMode(Crowd *crowd, int mode);
  import void MoveCrowdToTarget(Crowd *crowd);
  import int SetNogo(int dir);
};



Tankman.asc
__Tankman Tankman;
export Tankman;

bool gEnabled = false;
export gEnabled;

Tile *theGrid[numRows*numCols]; //global tile grid
Crowd *rabble[5];               //global crowd array

function __Tankman::Enable()
{
  gEnabled = true;
}

function __Tankman::Disable()
{
  gEnabled = false;
}

export Tile;
export Crowd;


//true=even, false=odd
bool isEven(int n)
{
...
}

//put in y, get x
int GetRowRange(int y)
{
...
}

//put in x, get y
int GetColRange(int x)
{
...
}

//set opposite direction to current direction; you cannot ever go in this direction
//eg. you are headed right; you cannot go backwards, so nogo=left
int Crowd::SetNogo(int dir)
{
...
}

//x1 is to the left of x2
//0=left
//1=same square
//2=right
int IsLeftOf(int x1, int x2)
{
...
}

//y1 is above y2
//0=up
//1=same square
//2=down
int IsUpOf(int y1, int y2)
{
...
}

//look up tile number, get actual tile returned
Tile Grid::TileFromRef(String refName)
{
...
}

//get a tile's refName from it's x,y coords eg x=3, y=4, refname string "34"
String RefFromTile(int x, int y)
{
...
}

//reset a crowd's timers
//todo: put something here to get new decision on where to go, or put in next planned decision
void Crowd::ResetTimers(Crowd *crowd)
{
...
}

//get a random free square pointer
//free is content=0
Tile* Grid::RandomFreeSquare()
{
...
}

//is an object overlapping a character, even by a pixel?
//note: doesn't account for transparent squares overlapping
bool IsOverlappingCO(Character *ch, Object *ob)
{
...


//is an character overlapping another character, even by a pixel?
//note: doesn't account for transparent squares overlapping
bool IsOverlappingCC(Character *ch1, Character *ch2)
{
...


//reset crowd's variables/initialise a new crowd
void Crowd::ResetCrowd(Crowd *crowd)
{
...
}


//go through crowd array, intialise all
void Crowd::SetupCrowds()
{
...
}

//intial setup for grid values
void Grid::SetupGridValues()
{
...
}

//using mode, set x,y target and strategy
void Crowd::FindTargetBasedOnMode(Crowd *crowd, int mode)
{
...
}

//move a crowd it's tX,tY target coord,
//and set 'moving' to '1' for 'not on a tile centre, better move 1 closed'
//and '0' for 'on a tile's centre; better make my next decision now i'm at tile 67, my target tile'
void Crowd::MoveCrowdToTarget(Crowd *crowd)
{
...
}


Room52.asc
// room script file

///////////////
// VARIABLES //
///////////////

#define DISTANCE 1  //0000// distance player walks in Tapping mode before he stops
#define MAX_BULLETS 3
#define MAX_LNDMINE 5
#define AG_WALL 2
#define FirstCrowdIndex 5
#define LastCrowdIndex 9

int AG_BULLETS=1;
int AG_LNDMINE=0;

int KeyboardMovement_KeyDown = 380; // down arrow
int KeyboardMovement_KeyLeft = 375; // left arrow
int KeyboardMovement_KeyRight = 377; // right arrow
int KeyboardMovement_KeyUp = 372; // up arrow

KeyboardMovement_Directions KeyboardMovement_CurrentDirection;// = eKeyboardMovement_Stop; // stores current walking direction of player character
KeyboardMovement_Directions newdirection; // declare variable storing new direction

//I must add these to reference and use them outside the module.ash, but... it doesn't work :/
import bool isEven(int n);
import int GetRowRange(int y);
import int GetColRange(int x);
import int IsLeftOf(int x1, int x2);
import int IsUpOf(int y1, int y2);
import String RefFromTile(int x, int y);
import Tile Grid::RandomFreeSquare();
import Tile Grid::TileFromRef(String refName);
import bool IsOverlappingCO(Character *ch, Object *ob);
import bool IsOverlappingCC(Character *ch1, Character *ch2);
import int Crowd::SetNogo(int dir);
import void Crowd::ResetTimers(Crowd *crowd);
import void Crowd::ResetCrowd(Crowd *crowd);
import void Crowd::SetupCrowds();
import void Grid::SetupGridValues();
import void Crowd::FindTargetBasedOnMode(Crowd *crowd, int mode);
import void Crowd::MoveCrowdToTarget(Crowd *crowd);


////////////////////
// ROOM FUNCTIONS //
////////////////////


function room_Load()
{
  Tankman.Enable();
 
gIconbar.Visible=false;

  cTankman.Baseline=768;
  cTankman.SetAsPlayer();
 
  cJulius.x=2000;   //the main character of the game; he's here, but off-screen
  cJulius.y=2000;
 
  AG_playing=true;
  AG_gameover=false;
 
  newdirection=eKeyboardMovement_Up;

  oC0.SetView(322); //set crowd views
  oC1.SetView(322);
  oC2.SetView(322);
  oC3.SetView(322);
  oC4.SetView(322);
 
 
}



function room_RepExec()
{
 
    //control tank - set joystick according to direction
    if (IsKeyPressed(KeyboardMovement_KeyDown)) {
      newdirection = eKeyboardMovement_Down; // down arrow
      oJoyStick.Graphic=4122;
      //Display("currdir %d",KeyboardMovement_CurrentDirection);//2
    }
    else if (IsKeyPressed(KeyboardMovement_KeyLeft)) {
      newdirection = eKeyboardMovement_Left; // left arrow
      oJoyStick.Graphic=4124;
      //Display("currdir %d",KeyboardMovement_CurrentDirection);//3
    }
    else if (IsKeyPressed(KeyboardMovement_KeyRight)) {
      newdirection = eKeyboardMovement_Right; // right arrow
      oJoyStick.Graphic=4118;
      //Display("currdir %d",KeyboardMovement_CurrentDirection);//4
    }
    else if (IsKeyPressed(KeyboardMovement_KeyUp)) {
      newdirection = eKeyboardMovement_Up; // up arrow
      oJoyStick.Graphic=4120;
      //Display("currdir %d",KeyboardMovement_CurrentDirection);//5
    }
   
    //check if area in front of tank is a wall
    if (GetWalkableAreaAt(cTankman.x, cTankman.y)!=AG_WALL) {
      switch (newdirection) {
       
        case eKeyboardMovement_Down:
        oJoyStick.Graphic=4122;
          if (GetWalkableAreaAt(cTankman.x, cTankman.y+(DISTANCE))!=AG_WALL) {
            cTankman.Loop=0;
            cTankman.y+=DISTANCE;
          } else {
            newdirection=KeyboardMovement_CurrentDirection;
          }
          break;
     
        case eKeyboardMovement_Left:
        oJoyStick.Graphic=4124;
          if (GetWalkableAreaAt(cTankman.x-(DISTANCE), cTankman.y)!=AG_WALL) {
            cTankman.Loop=1;
            cTankman.x-=DISTANCE;
          } else {
            newdirection=KeyboardMovement_CurrentDirection;
          }
          break;
         
        case eKeyboardMovement_Right:
        oJoyStick.Graphic=4118;
          if (GetWalkableAreaAt(cTankman.x+(DISTANCE), cTankman.y)!=AG_WALL) {
            cTankman.Loop=2;
            cTankman.x+=DISTANCE;
          } else {
            newdirection=KeyboardMovement_CurrentDirection;
          }
          break;
       
        case eKeyboardMovement_Up:
        oJoyStick.Graphic=4120;
          if (GetWalkableAreaAt(cTankman.x, cTankman.y-(DISTANCE))!=AG_WALL) {
            cTankman.Loop=3;
            cTankman.y-=DISTANCE;
          } else {
            newdirection=KeyboardMovement_CurrentDirection;
          }
          break;
      }//switch
    }//if
 
    // update current direction to new direction
    KeyboardMovement_CurrentDirection = newdirection;
 
}


Basically, I'm making a Pacman clone game, called Tankman. I want to store Tankman as a module, and then a room I can 'import' it into and use the objects/art/etc. to create the final working game. Perhaps I should just fit it in 1 room?..
Title: Re: Managed types?
Post by: bx83 on Mon 12/07/2021 09:05:29
Okay, I've simplified a bit. 'Grid' is now gone; to create a map of Tiles, I've just put it into a Tile array[rows*columns]; as you can see in Tankman.asc

But...

Tankman.ash
// new module header
struct __Tankman
{
  import function Enable();
  import function Disable();
};
import __Tankman Tankman;


#define numRows     8
#define numCols     10

managed struct Tile {
  int x;  //x centre of tile
  int y;  //y centre of tile
  //String refName;
  bool up;      //has up direction
  bool right;   //has right direction
  bool down;    //has down direction
  bool left;    //has left direction
  int content;  //is it a free tile or blocked space?
};

#define numCrowds   5

managed struct Crowd {
  int aX;     //actual x eg. 312
  int aY;     //actual y eg. 472
  int tX;     //target x - target char/tile x coord - '3' for 'tile 34'
  int tY;     //target y - target char/tile y coord - '4' for 'tile 34'
 
  int mode;   //mode, 0=chase, attack=1, scatter=2, scared=3, regen=4
  int timer1; //timer to stay in mode
  int timer2; //timer to stay aggressive
  int waittimer;  //timer for wait between attacks/cooldown time
  int dir;    //0=up, 1=right, 2=down, 3=left  Never Eat Soggy Wheatbix
  int nogo;   //opposite direction to where your pointed; dir/nogo 0/2 1/3 2/0 3/1
  int moving; //1=moving to next coord, 0=not moving, decision/action time
  int id;     //0..4
 
  import void SetupCrowds();
  import void ResetCrowd(Crowd *crowd);
  import void ResetTimers(Crowd *crowd);
  import void FindTargetBasedOnMode(Crowd *crowd, int mode);
  import void MoveCrowdToTarget(Crowd *crowd);
  import int SetNogo(int dir);
};


Tankman.asc
__Tankman Tankman;
export Tankman;

bool gEnabled = false;
export gEnabled;

Tile *theGrid[numRows*numCols]; //global tile grid       <----ERROR! Tankman.asc(7): Error (line 7): expected ']'
Crowd *rabble[5];               //global crowd array

function __Tankman::Enable()
{
  gEnabled = true;
}

function __Tankman::Disable()
{
  gEnabled = false;
}

export Tile;
export Crowd;

//true=even, false=odd
bool isEven(int n)
{
...


What is wrong with line 7 of Tankman.asc?

Will these variables, theGrid[] etc, be global to the module? Any room they're defined in? I just want store it in a module, use it in a room.
Title: Re: Managed types?
Post by: Crimson Wizard on Mon 12/07/2021 10:57:46
Quote from: bx83 on Mon 12/07/2021 09:05:29
What is wrong with line 7 of Tankman.asc?

AGS script currently does not support expressions in the declaration. If you have a regular array you need to put a literal value there.
If you have a dynamic array - you allocate it with "new" inside a function.

Quote
Will these variables, theGrid[] etc, be global to the module? Any room they're defined in? I just want store it in a module, use it in a room.

These variables are global, they are directly accessible only from inside the module from what I see, because you do not have them declared in the header.

These lines are incorrect and unnecessary, as types cannot be exported, only actual variables:
Code (ags) Select

export Tile;
export Crowd;
Title: Re: Managed types?
Post by: bx83 on Mon 12/07/2021 12:27:04
So if I did a

import Tile *theGrid[] = new Tile[numRows*numCols];

Inside of Tankman.ash, it would be global to the .asc and a room which imports __Tankman?

Would I need to declare the dynamic array in the Tile struct to show its extendible?
Title: Re: Managed types?
Post by: bx83 on Mon 12/07/2021 13:14:18
So I put these:

Tile theGrid[]=new *Tile[numRows*numCols];
Crowd rabble[5];

...into a function in a room, into area above code in a room, the Tankman.ash and .asc.
Always the error: Error (line xx): Cannot declare local instance of managed type

Would it be possible to do the struct definitions, the function definition, and everything, just in one room? I am getting in over my head for a Pacman clone :/
Title: Re: Managed types?
Post by: bx83 on Mon 12/07/2021 15:00:47
Okay.

in Tankman.ash:
Tile *theGrid[];    //dynamic array of pointers
Crowd* rabble[5];    //5 crowds


in Room52.asc:
function game_start()
{
  theGrid[]=new Tile[numRows*numCols];  //define it as a new Tile array of size rows*colums  <----error this line
}


Failed to save room room52.crm; details below
room52.asc(3): Error (line 3): array index not specified


I'm going to bed, its midnight :/
Title: Re: Managed types?
Post by: Crimson Wizard on Mon 12/07/2021 21:23:52
Quote from: bx83 on Mon 12/07/2021 15:00:47in Room52.asc:
function game_start()
{
  theGrid[]=new Tile[numRows*numCols];  //define it as a new Tile array of size rows*colums  <----error this line
}


You do you not need [] after the name when you are assigning to the variable that was already declared.
function game_start()
{
  theGrid=new Tile[numRows*numCols];
}




To summarize:
Code (ags) Select

// this is variable declaration
Tile *theGrid[];

function F()
{
    // this is assignment of a new array to already declared variable
    theGrid = new Tile[10];
}

function F2()
{
    // this is declaration AND assignment combined
    Tile *theGrid2[] = new Tile[10];
}
Title: Re: Managed types?
Post by: bx83 on Tue 13/07/2021 02:20:42
Very well explained, thank you CW :)  Hopefully I wont make that mistake in the future (hopefully...)

One more one:

Tankman.ash

managed struct Crowd {
  int aX;     //actual x eg. 312
  int aY;     //actual y eg. 472
  int tX;     //target x - x centre of target tile
...
  import void ResetTimers(Crowd *crowd);
...


Tankman.asc
void Crowd::ResetTimers(Crowd *crowd)
{...}

[much lower down]

void Crowd::ResetCrowd(Crowd *crowd)
{
...
  ResetTimers(this);   <-----ERROR: 'Undefined token ResetTimers'
...
}


How the hell is this undefined? I need to put Crowd:: in front of it? If I do, it says 'Variable '::' is already defined'
Title: Re: Managed types?
Post by: Crimson Wizard on Tue 13/07/2021 02:33:39
Quote from: bx83 on Tue 13/07/2021 02:20:42
How the hell is this undefined? I need to put Crowd:: in front of it? If I do, it says 'Variable '::' is already defined'

If it's a non-static function, you got to call it from the object. In your case the object is "this". For the same reason, you don't have to pass "this" into the function that belongs to same type, this is done automatically.
So:
Code (ags) Select

import void ResetTimers();

and
Code (ags) Select

this.ResetTimers();



EDIT: Ah, I just realized you did it everywhere, in all functions.
Yes, you can remove these unnecessary "Crowd* crowd" everywhere.

If the function is a part of an object, you do not need to pass same object there, but it call "from it".
Code (ags) Select

// from the outside
Crowd *crowd;
crowd.Function();

// or from "inside" -- using "this"
function Crowd::Function()
{
    this.AnotherFunction();
}
Title: Re: Managed types?
Post by: bx83 on Tue 13/07/2021 05:38:53
Out of interest, why is there no reserved word for ‘class’ or even ‘object’? Can you have constructors/deconstructers?
Strange having an object instance never referred to as object and using ‘struct’ to define.
Title: Re: Managed types?
Post by: Crimson Wizard on Tue 13/07/2021 05:42:50
Quote from: bx83 on Tue 13/07/2021 05:38:53
Out of interest, why is there no reserved word for ‘class’ or even ‘object’?

There's a reserved word 'struct', which is practically a class, but what would 'object' mean? If you refer to the base class of all in C# and similar languages, AGS script simply does support type casting fully enough to have it.

Quote from: bx83 on Tue 13/07/2021 05:38:53
Can you have constructors/deconstructers?

No, unfortunately not.

Quote from: bx83 on Tue 13/07/2021 05:38:53
Strange having an object instance never referred to as object and using ‘struct’ to define.

I do not understand the meaning of this last sentence.
Title: Re: Managed types?
Post by: bx83 on Tue 13/07/2021 08:20:11
Basically, it looks like an object (class), but just says 'struct'. Has its own functions, is its own replicable thing, but never anymore than a struct. But it sounds like AGS script is more complex.

Update: oooh, it has casting?
Title: Re: Managed types?
Post by: Crimson Wizard on Tue 13/07/2021 08:24:32
Quote from: bx83 on Tue 13/07/2021 08:20:11
Basically, it looks like an object (class), but just says 'struct'. Has its own functions, is its own replicable thing, but never anymore than a struct. But it sounds like AGS script is more complex.

Wait... class is not an object. Class is a type, object is an instance of that type.

Are you comparing with some other programming language?

E.g. in C++ struct is same as class basically, it's just there mainly for backward compatibility with C.
In C# struct may have almost same things as the class (functions, constructors and so on), but it's allocated and managed in memory differently.
Title: Re: Managed types?
Post by: bx83 on Tue 13/07/2021 08:33:01
'Object' as in 'I've had a stroke'.
I mean, 6-7 years ago my day job was writing (well, maintaining) a mainframe interface via C# for a government web-portal. Now it's writing a Pacman clone. So, you know.
Title: Re: Managed types?
Post by: Crimson Wizard on Tue 13/07/2021 08:40:17
Quote from: bx83 on Tue 13/07/2021 08:20:11
Update: oooh, it has casting?

Unfortunately only towards the parent, and through the pointer (so it works only for managed struct pointers).

You might know GUIControl class, and that all other control classes, Button and so on - are its children.

So in AGS script you may do:
Code (ags) Select

Button* button;
...
GUIControl* control = button; // this works


but you cannot do:
Code (ags) Select

GUIControl* control;
...
Button* button = control; // this won't work


For that reason GUIControl as these "AsButton", "AsLabel" and so on, which is ugly, but currently the only way to case a parent pointer to the actual child object.