I have a script (header/code) for my little Tankman module. In room52, where I have the graphics for the game, I have a game_start() function.
In game_start(), I've put all the initialisation code, and the code for 'theGrid', an array which represents my game-map:
Tankman.ash
Tile *theGrid[]; //dynamic array of pointers
Room52.asc
function game_start()
{
...
theGrid=new Tile[numRows*numCols]; //define it as a new Tile array of size rows*colums
Display("rgarghath");
SetupGridValues(); <------Display output for testing, sets up grid values now it's been properly instantiated
GetTargetRed(); <----- function than generates Display output
}
Nothing displays output, nothing runs.
If I put SetupGridValues() in room_Load(), it runs it, and then says theGrid is a null pointer (obviously never instantiated).
I would've thought that game_start() would be the first and foremost thing to run?
How and where do I run 'theGrid=new Tile[numRows*numCols];' to set it as a global variable (within game/room52), then run all 'setup' code, and then just get to the actual game-loop in room_RepExec()?
game_start is not run in the room scripts, because no room is loaded at the game start yet.
You may only have these in script modules, but not in room scripts.
Quote from: bx83 on Sat 17/07/2021 04:57:06
How and where do I run 'theGrid=new Tile[numRows*numCols];' to set it as a global variable (within game/room52), then run all 'setup' code, and then just get to the actual game-loop in room_RepExec()?
room_Load ("room before fade-in") event is meant for setting a room up, have you tried putting everything there?
Okay, that explains that.
Now I've got:
Room52.asc:
function room_Load()
{
...
theGrid=new Tile[numRows*numCols]; //define it as a new Tile array of size rows*colums
Display("%d %d",numRows, numCols);
theGrid[0].x=25; theGrid[0].y=25; theGrid[0].up=0; theGrid[0].right=1; theGrid[0].down=1; theGrid[0].left=0; theGrid[0].content=0;
theGrid[1].x=75; theGrid[1].y=25; theGrid[1].up=0; theGrid[1].right=1; theGrid[1].down=0; theGrid[1].left=1; theGrid[1].content=0;
theGrid[2].x=125; theGrid[2].y=25; theGrid[2].up=1; theGrid[2].right=1; theGrid[2].down=0; theGrid[2].left=1; theGrid[2].content=0;
Display("rgarghath");
SetupGridValues();
GetTargetRed();
It displays:
8 10
'Runtime error: null pointer referenced'
The first line, theGrid[0].x=25, etc etc is the null pointer. So instantiation is right, but then 'theGrid' drops from memory. Or something. :/
Quote from: bx83 on Sat 17/07/2021 05:17:03
It displays:
8 10
'Runtime error: null pointer referenced'
Well, that's weird. May you try printing theGrid value as well?
Display("%p %d %d", theGrid, numRows, numCols);
How and where is theGrid declared?
EDIT: oh I see in your first post:
Quote
Tankman.ash
Tile *theGrid[]; //dynamic array of pointers
This is a wrong thing to put into a header. The variables should be put in the script body, and headers should have import declarations.
https://adventuregamestudio.github.io/ags-manual/TheScriptHeader.html
https://adventuregamestudio.github.io/ags-manual/ImportingFunctionsAndVariables.html
Tankman.ash
// new module header
struct __Tankman
{
import function Enable();
import function Disable();
};
import __Tankman Tankman;
#define numRows 8
#define numCols 10
#define tankLoop 314
managed struct Tile {
int x; //x centre of tile
int y; //y centre of tile
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 4
managed struct Crowd {
int aX; //actual x eg. 312
int aY; //actual y eg. 472
int tX; //target x - x centre of target tile
int tY; //target y - y centre of target tile
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 timercontrol; //a timer is on=1/off=0
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
bool attack; //are they programmed to be aggressive? 0-no 1-yes
int id; //object ID, leads to control of object loop/position
bool dead; //0=alive, 1=dead
import void ResetTimers(Crowd *crowd);
import void FindTargetBasedOnMode(Crowd *crowd, int mode);
import void MoveCrowdToTarget(Crowd *crowd);
import int CleanUpDeadCrowd(Crowd *crowd);
};
managed struct TM {
int tileX; //tm's current tile
int tileY;
int dir; //tm's current dir
int nogo; //diametric opposite to dir
int mode;
int id; //object id
};
import int SetNogo(int dir);
import int DirToLoop(int dir);
import int LoopToDir(int loop);
import Tile* GetTile(int x=-1, int y=-1);
Tankman.asc
__Tankman Tankman;
export Tankman;
bool gEnabled = false;
export gEnabled;
function __Tankman::Enable()
{
gEnabled = true;
}
function __Tankman::Disable()
{
gEnabled = false;
}
Tile *theGrid[]; //dynamic array of pointers
Crowd* rabble[4]; //4 crowds: red, yellow, green, purple
TM* TMan;
...
Room52
...
function room_Load()
{
Tankman.Enable();
gIconbar.Visible=false;
oTM.Baseline=768;
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;
//set crowd views
oC0.SetView(330); //red
oC1.SetView(332); //yellow
oC2.SetView(331); //green
oC3.SetView(329); //purple
oTM.SetView(314); //tankman
theGrid=new Tile[numRows*numCols]; <------ Error: Undefined token 'theGrid'
EDIT: added these to the bottom of Tankman.ash:
import Tile *theGrid[];
import Crowd* rabble[4]; //4 crowds: red, yellow, green, purple
import TM* TMan;
Gets through compile, but then has a runtime error on startup in room52:
Error: unable to create local script: Runtime error: unresolved import 'theGrid'
Quote from: bx83 on Sat 17/07/2021 05:54:00
theGrid=new Tile[numRows*numCols]; <------ Error: Undefined token 'theGrid'
[/code]
Well, you need to declare an import for this variable.
https://adventuregamestudio.github.io/ags-manual/ImportingFunctionsAndVariables.html
In the script body:
Tile *theGrid[]; //dynamic array of pointers
...
export theGrid;
In the script header:
import Tile *theGrid[];
Alrighty, that worked, exports were the missing piece (which I... should have remembered. I need lunch).
Now, it gets back in room 52 to:
function room_Load()
{
Tankman.Enable();
gIconbar.Visible=false;
oTM.Baseline=768;
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;
//set crowd views
oC0.SetView(330); //red
oC1.SetView(332); //yellow
oC2.SetView(331); //green
oC3.SetView(329); //purple
oTM.SetView(314); //tankman
theGrid=new Tile[numRows*numCols]; //define it as a new Tile array of size rows*colums
Display("%p %d %d",theGrid, numRows, numCols); <---------displays "<8 random hex characters> 8 10"
theGrid[0].x=25; <---------------------------------Error: null pointer referenced, PROGRAM STOPS HERE
Display("%p %d %d",theGrid, numRows, numCols);
Display("rgarghath");
SetupGridValues();
GetTargetRed();
Ah, I got it.
"theGrid" is an array of Tiles, but Tile is a managed struct, so each tile should also be created.
It's not theGrid which is the null pointer, it's theGrid[0] which is a null pointer.
So you have to do this:
int size = numRows*numCols;
theGrid=new Tile[size ];
for (int i = 0; i < size; i++)
theGrid[i] = new Tile;
It all works now, thank you :)
....except TMan. As usual I will try all combinations of definition before complaining.
*does this over 20 minutes*
Okay, I'm out of ideas.
Tankman.ash
...
import Tile *theGrid[];
import Crowd* rabble[numCrowds]; //4 crowds: red, yellow, green, purple
import TM* TMan;
Tankman.asc
__Tankman Tankman;
export Tankman;
bool gEnabled = false;
export gEnabled;
function __Tankman::Enable()
{
gEnabled = true;
}
function __Tankman::Disable()
{
gEnabled = false;
}
Tile *theGrid[]; //dynamic array of pointers
Crowd* rabble[4]; //4 crowds: red, yellow, green, purple
TM* TMan;
export theGrid;
export rabble;
export TMan;
...
Room52.asc
function room_Load()
{
Tankman.Enable();
gIconbar.Visible=false;
oTM.Baseline=768;
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;
//set crowd views
oC0.SetView(330); //red
oC1.SetView(332); //yellow
oC2.SetView(331); //green
oC3.SetView(329); //purple
oTM.SetView(314); //tankman
//fill out theGrid with Tile objects
int size = numRows*numCols;
theGrid=new Tile[size]; //instantiate theGrid
for (int i = 0; i < size; i++) {
theGrid[i] = new Tile;
}
SetupGridValues();
for (int j=0; j<numCrowds;j++) {
rabble[j]=new Crowd;
}
//now, setup crowds:
///////////
// 0 RED //
///////////
rabble[0].mode=Random(1); //chase or scatter
//get random start tile
Tile *rt0 = GetTile();
rabble[0].aX=rt0.x;
....
else if (rt3.left==1) rabble[3].dir=3;
//set nogo based on dir
rabble[3].nogo=SetNogo(rabble[3].dir);
//setup tankman:
/////////////
// Tankman //
/////////////
TMan.tileX=6; <----NULL POINTER REFERENCED
so you know :(
TMan is still not acting as a nice global.
Well, it's the exactly same reason, you have a "TM* TMan;" which is a pointer, but do you create the actual object anywhere with "new"?
...
//fill out theGrid with Tile objects
int size = numRows*numCols;
theGrid=new Tile[size]; //instantiate theGrid
for (int i = 0; i < size; i++) {
theGrid[i] = new Tile;
}
SetupGridValues();
for (int j=0; j<numCrowds;j++) {
rabble[j]=new Crowd;
}
TMan=new TM;
...and it works :P
OT: You can make your code look like AGS code in your posts, instead of [ code ] use [ code=ags ] (without the spaces). ;)