Hey all. Wish I weren't posting for help again so soon after the last time, but here we are. :(
I have been testing out small blocks of code in a mostly empty default template game, and those block work perfectly there. Unfortunately, when I write the same code in my actual game, they don't work anymore.
Here are the issues, which I theorize are related because they both have to do with the custom inventory GUI:
1. I have a speech-based countdown timer running. In the test game it works perfectly. In my real game it causes the inventory GUI to flicker once per second as the timer counts down. I know it's the timer that causes it because when I remove the timer block, the blinking stops. There's no such behavior in the test game, however.
2. There's also a hovering text label that follows the cursor and gives the name of whatever inventory item is under the cursor. It runs perfectly in the test game. In the real game the hover text does not show up at all (the label is marked enabled, visible, and clickable in settings).
I have scanned the code multiple times trying to figure out what the discrepancy is. The settings for the inventory and hovertext GUIs are identical. The code itself, other than character names (cTimer vs. cRoger), inventory item names (iKey vs. iTest1) and GUI label numbers (Label1 vs. Label11), is also nearly identical. The timer is nested within an if statement in the real game (if dying == true), but removing that made no difference.
I then commented out every piece of the room script until I was left with only the relevant code, which still looks identical.
I am well and truly stumped. :(
I honesty can't find where the two rooms are significantly diverging, or what could be causing these issues. Many thanks and a place in the game credits for anyone who can help me solve this.
This is the room script where everything works perfectly:
// room script file
int min;
int sec;
String TimerText;
bool timeout;
function room_load()
{
min = 2; //set number of minutes before timeout
sec = 15; // set number of seconds before end of current minute
timeout = false; //timer has not run out yet
SetGameSpeed(40); // game loops per second
SetTimer(1, 600); // set minutes timer (seconds * 40) and whether initial minute expires in less than 59 seconds
SetTimer(2, 40); // set seconds timer
cRoger.y = 100;
cRoger.AddInventory(iCup);
InvItemTotal ++;
cRoger.AddInventory(iKey);
InvItemTotal++;
}
function repeatedly_execute_always()
{
if (IsTimerExpired(1)) //check minutes timer
{
min -=1; // decrease counter by 1 minute
SetTimer(1, 2360); //reset timer for another minute
if (min == -1) // keep minutes from going below 0
{
min = 0;
}
}
if (IsTimerExpired(2)) // check seconds timer
{
sec -=1;
if (sec >=10)
{
TimerText = String.Format("%d:%d", min, sec); // countdown from 60 to 10
cRoger.SayBackground(TimerText);
SetTimer(2, 40); //trigger next code block
}
else if (sec <10 && sec >-1)
{
TimerText = String.Format("%d:0%d", min, sec); // count from 9 to 0 and switch format from "9" to "09" etc.
cRoger.SayBackground(TimerText);
SetTimer(2, 40);
if (sec == 0 && min == 0 && timeout == false)
{
sec +=1; // cancel out seconds countdown
timeout = true; // time has run out
SetTimer(3, 180); // how long to display final second of timer (0:00)
}
if (sec == 0 && min == 0 && timeout == true && IsTimerExpired(3) == false)
{
sec +=1;
}
}
if (sec == -1 && timeout == false)
{
sec = 59; // reset to keep seconds from counting into negative numbers and change format back to 2 digits
TimerText = String.Format("%d:%d", min, sec);
cRoger.SayBackground(TimerText);
SetTimer(2, 40);
}
}
}
function room_AfterFadeIn()
{
}
function oHandbag_AnyClick()
{
if (gInventory.Visible == false)
{
gInventory.Visible = true;
gInventory.Y = oHandbag.Y;
gInventory.X = (oHandbag.X -1) - (InvItemTotal * 26);
}
else if (gInventory.Visible == true)
{
gInventory.Visible = false ;
}
}
function room_RepExec()
{
InventoryItem *InvUnderMouse;
InvUnderMouse = InventoryItem.GetAtScreenXY(mouse.x, mouse.y);
if (InventoryItem.GetAtScreenXY(mouse.x, mouse.y) == null)
{
gInvLabel.Visible = false;
}
else
{
gInvLabel.Visible = true;
gInvLabel.X = mouse.x;
gInvLabel.Y = mouse.y - 20;
Label1.Text = InvUnderMouse.Name;
}
}
And this is the code from the room with problems:
// room script file
int min;
int sec;
String TimerText;
bool timeout;
function room_Load()
{
Mouse.Mode = eModePointer;
min = 1; //set number of minutes before timeout
sec = 31; // set number of seconds before end of current minute
timeout = false; //timer has not run out yet
SetTimer(1, 1300); // set minutes timer (seconds * 40) and whether initial minute expires in less than 59 seconds
SetTimer(2, 40); // set seconds timer
cRoomController.AddInventory(iTest1);
cRoomController.AddInventory(iTest2);
cRoomController.AddInventory(iTest3);
InvItemTotal +=3;
}
function repeatedly_execute_always()
{
if (IsTimerExpired(1)) //check minutes timer
{
min -=1; // decrease counter by 1 minute
SetTimer(1, 2360); //reset timer for another minute
if (min == -1) // keep minutes from going below 0
{
min = 0;
}
}
if (IsTimerExpired(2)) // check seconds timer
{
sec -=1;
if (sec >=10)
{
TimerText = String.Format("%d:%d", min, sec); // countdown from 60 to 10
cTimer.SayBackground(TimerText);
SetTimer(2, 40);
}
else if (sec <10 && sec >-1)
{
TimerText = String.Format("%d:0%d", min, sec); // count from 9 to 0 and switch format from "9" to "09" etc.
cTimer.SayBackground(TimerText);
SetTimer(2, 40);
if (sec == 0 && min == 0 && timeout == false)
{
sec +=1; // cancel out seconds countdown
timeout = true; // time has run out
SetTimer(3, 180); // how long to display final second of timer (0:00)
DisplayAtY(10, "Crap, what is... Feels like I'm falling.");
}
if (sec == 0 && min == 0 && timeout == true && IsTimerExpired(3) == false)
{
s ec +=1;
}
}
if (sec == -1 && timeout == false)
{
sec = 59; // reset to keep seconds from counting into negative numbers
TimerText = String.Format("%d:%d", min, sec); //change format back to 2 digits
cTimer.SayBackground(TimerText);
SetTimer(2, 40);
}
}
}
function oHandbag_AnyClick()
{
if (gInventory.Visible == false)
{
gInventory.Visible = true;
gInventory.Y = oHandbag.Y;
gInventory.X = (oHandbag.X -1) - (InvItemTotal * 28);
}
else if (gInventory.Visible == true)
{
gInventory.Visible = false ;
}
}
function room_RepExec()
{
InventoryItem *InvUnderMouse;
InvUnderMouse = InventoryItem.GetAtScreenXY(mouse.x, mouse.y);
if (InventoryItem.GetAtScreenXY(mouse.x, mouse.y) == null)
{
gInvLabel.Visible = false;
}
else
{
gInvLabel.Visible = true;
gInvLabel.X = mouse.x;
gInvLabel.Y = mouse.y - 20;
Label11.Text = InvUnderMouse.Name;
}
}
What am I missing? ???
Your first issue could have to do with the General Setting "When player interface is disabled, GUIs should...", if that differs between your test game and real game. Try making sure that it is set to "display normally."
Your second issue, if I understand it correctly (it should display a label over your inventory), might have to do with the z-order of the GUIs. Check that gInvLabel.ZOrder is set to a higher value than that of the inventory GUI itself, or it will be displayed behind it.
BTW, the way you're doing the timer is rather more complicated than necessary.
1. Rather than keeping two separate code branches just to add a 0 when the seconds-counter is in single digits, you can use the automatic 0-padding (https://adventuregamestudio.github.io/ags-manual/StringFormats.html) formatting style:
// This will always display sec with two digits
TimerText = String.Format("%d:%02d", min, sec);
2. It would be a lot easier to not keep track of the minutes and seconds separately, but only the actual remaining time, and just convert it into minute and second parts for display:
int totalSec;
function room_load()
{
int totalSec = 2*60 + 15;
// ...
}
function repeatedly_execute_always()
{
// ...
// Divide by 60 to give the number of minutes,
// take the remainder to give just seconds within the minute
TimerText = String.Format("%d:%02d", totalSec/60, totalSec % 60);
}
Then you don't need all the logic for when the minute changes; you only need to test whether the total countdown has reached the end.
3. If you're already keeping manual countdown variables, you don't really need timers at all. At most a single timer to give you a "tick" per second, just for convenience.
So taken all together, the timer code can be simplified to:
// room script file
int totalSec;
function room_Load()
{
totalSec = 1*60 + 30;
SetTimer(1, GetGameSpeed()); // set "seconds tick" Timer
}
function repeatedly_execute_always()
{
// This runs once every second while timeout displayed
if (IsTimerExpired(1))
{
if (totalSec > 0)
cTimer.SayBackground("%d:02d", totalSec/60, totalSec%60);
else
cTimer.SayBackground("0:00");
if (totalSec == 0)
DisplayAtY(10, "Crap, what is... Feels like I'm falling.");
totalSec--; // Count down our timer
// How long we keep re-displaying 0:00 after the timeout has ended
// (the time for DisplayAtY to time out comes on top of this)
if (totalSec > -5)
SetTimer(1, GetGameSpeed());
}
}
Hi Snarky,
Thanks so much for your response! I changed the general settings as you suggested which fixed the blinking issue. Much appreciated.
Changing the ZOrder on the floating label unfortunately did nothing, but I managed to fix it somehow by moving the code block from rep_exec to rep_exec_always. I have no idea what was blocking it before, but I'm not complaining.
Also, thanks for your code to simplify the timer -- it seems so simple reading it, but my brain goes to needlessly complicated logic before it goes to simple math. :) Also I didn't know about the automatic 0-padding which is awesome.
Thanks again for your help! You and the others who've helped me out here will get a mention in the acknowledgements for sure.