Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Topics - Lord Vetinari

#1
As the title says. Depending on the resolution and size of your monitor and the size of the GUI (especially if you're working with high resolutions), it's possible that some bits of it end up out of the editor window and, unlike rooms, there's no zoom nor scroll bars to move around and see the whole thing.
I'm working on a small test project at 1920x1200, I'm making a status bar that spans the whole top of the screen (and therefore is 1920 pixels wide), and its left side is constantly out of sight; to work with that corner of the GUI I have to hide the Project tree and Properties windows every time to let the main editor window stretch the whole screen, which is not really a big deal but can get annoying after a while.
Would it be doable to add at least scroll bars?
#2
Hi everybody.
I'm a mostly self-tought programmer. I learned the very basics in high school, then dabbled with it in and out throughout the years until a couple of years ago I got infected by the coding virus and started spending way too many weekends writing stuff. I believe I reached the point when I'm quite confident when it comes to the "grammar" of AGS Script, C++ and C#, or at least confident enough to find almost always a way of putting together something that does what I want it to do thanks to the ancient and revered art of the kludge.
I can only describe what I write as the coding equivalent of a splatter movie, and I really want to learn to write better, less messy programs, but I believe that keep trying on my own is not the answer; at least, it has not been the answer so far, and I can't imagine it changing in the near future.

I know that the ideal solution would be to follow a proper course, but that's not an option right now. What resources (ideally online) would you suggest me to get better in this field?

If you feel like sharing, how many of you never had a formal education in coding and computer science and managed to become a good coder? How?
#3
The title is kinda cryptic due to title length limitations, I'll try to explain more here.

Baically, I've been using custom properties a lot recently, mostly to consolidate stuff and track/set custom statuses. So, for example, I have the inventory items custom properties "batteryCharge" and "powerSetting" (think Star Trek phasers with stun and kill settings for this one, ans ammo for the other one), which allows me to have a single laser gun that behaves differently depending on those properties without the need to create multiple inventory items and swap them to represent the different statuses of the gun. I don't know if this was the intended use for custom properties, anyway it works really well and I find it easier to wrap my mind around design this way for this kind of purpose.

I have two tiny gripes with the custom properties used this way, though: the first is that the editor does not autocomplete or indeed recognize if what you're writing is a thing until you compile, which means that I have to either write down their names on paper or keep opening the custom properties window to check the correct spelling, and I'm missing the easiness of use that comes with the ability to choose one item from the autocomplete list rather than manually typing their names.

The second is that you can only create custom properties per broader categories (i.e, all objects, all inventory items, all rooms, etc), which means that when I use this approach a lot, I end up with a long list of mostly useless properties that are meant to be relevant for one or two objects/rooms/chracters/etc and instead pop up in every single same-type-thingie I create in the rest of the game (the iBucket doesn't need batteryCharge nor powerSetting, but it may need the fullnessThresholds one that is however useless for the iGun, and so on. This would become an even bigger problem if autocomplete will ever be added). Not a big issue per se (even though I don't know how big an impact a large amount of custom properties have on the game's performance and memory usage, if any), but sometimes tracking down the right property in the middle of the other useles ones becomes more tedious that it should be.
Would it be possible to add the option to enable/disable any property from any specific item/object/etc (or at least hide it from it's properties list)?
#4
My game uses a custom interaction menu that you guys helped me code a while ago (thanks!)
It works perfectly fine for everything during normal gameplay, but it seems that there's something that prevents it to work or appear when used to interact with the inventory items. So far I handled the inventory via custom buttons in the inventory window itself, now I really want to consolidate the ways the player interacts with anything, plus I wanted to add some custom interactions to a couple of items, so I thought I could easily expand the interction menu's function to work with inventory items too just like I expanded it to work with objects and characters. Apparently I was wrong :P

This is the code for the interaction menu:

Code: ags

function on_mouse_click(MouseButton button)
{
  // called when a mouse button is clicked. button is either LEFT or RIGH
  
  if (IsGamePaused() == 1) {
    // Game is paused, so do nothing (ie. don't allow mouse click)
  }
  else if (button == eMouseLeft)  {
    //reset everything before a fresh start on a new interactive element
    gInteractionsMenu.Visible = false;
    ListInteractions.Clear();
    gInteractionsMenu.Height = 40;
    ListInteractions.Height = 0;
    
    if (Room.GetProperty("InteractionMenu_Available") == true) {
      //set the required variables
      int AreThereInteractions = 0;
      MouseActionX = mouse.x;
      MouseActionY = mouse.y;
      String CurrentOption;

      //these are pointers I properly created outside this function because I use them for a couple other stuff too
      activeHotspot = Hotspot.GetAtScreenXY(mouse.x, mouse.y);
      activeObject = Object.GetAtScreenXY(mouse.x, mouse.y);
      activeCharacter = Character.GetAtScreenXY (mouse.x, mouse.y);
      activeItem = InventoryItem.GetAtScreenXY (mouse.x, mouse.y);
      
      //Handling Inventory items
      if(activeItem != null) { //safety check: pointers can be "null", and if so you can't call methods on them
        ActionNumber = activeItem.GetProperty("Interaction_Number"); //Get the number of custom interactions thisobject/item/hotspot/character has
        int ActionCounter = 1;
        while (ActionNumber > 0) 
        {
          CurrentOption = String.Format("Action_%d", ActionCounter);
          ListInteractions.AddItem (String.Format("%s", activeItem.GetTextProperty(String.Format("%s", CurrentOption))));  
          gInteractionsMenu.Height += 26;
          ListInteractions.Height += 26;
          ActionNumber -=1;
          AreThereInteractions += 1;
          ActionCounter +=1;
        }
        if (AreThereInteractions > 0) {
          gInteractionsMenu.X = mouse.x;
          gInteractionsMenu.Y = mouse.y;
          ListInteractions.SelectedIndex = -1;
          lbMenuTitle.Text = String.Format("%s", activeItem.Name);
          gInteractionsMenu.Visible = true;
        }
      }

      // Repeat the same code with appropriate pointers for objects, characters and hotspots
      // They have an additional else clause at the end to handle character movement when you're not clicking on something interactable


As I said, it works perfectly during regular room browsing, but nothing happens when the inventory GUI is open.
I checked the z-order of the guis, in case it was drawn under the inventory screen. Nope, gInteractionMenu is set to appear on top of everything else.
I also made it so that the inventory menu didn't pause the game even though it leads to some undesirable behaviour if you click outside the GUI (By the way, any suggestion on how to handle that in case I have to keep it this way?), and of course I also made sure that the inventory item I was clicking on had the proper custom properties correctly set. Still nothing.

Help, please?
#5
Hi everybody, I run into another issue.
There's a random chance that an object appears on the floor as my character moves between rooms.
I set up one copy of the object in each room and set it to initially invisible.
Then I defined a global Object *currentRoomLostObject in the global script header (which I know works, because I used it for other global pointers, and they work fine), tried to assign it a value in the room_load function and made a global function that randomizes the chance of it happaning and turns the object visible. In theory. In practice, the pointer is always null, therefore the test run crashes and highlights the line in the method to tell me that there's no value to use in that pointer, but I can't figure out why the assignment does not work.

Code: ags

//this in each room script

function room_Load()
{
    currentRoomLostObject = objLostObject1 //whatever the object script name is in the current room
}

function room_AfterFadeIn()
{
    object_appears_event();
}

// this in the global script

void object_appears_event () {
    int RandomChance = Random (100);
    if ((RandomChance > 90) && (ObjectCanAppear == true)) { //the second one is a global varialbe that tracks if the player has solved a certain other puzzle elsewhere
        currentRoomLostObject.visible = true;
    }
}


Here I simplified the object_appears_event function to remove some bits of irrelevant coreography (that anyway work properly, as the test run complains about the pointer, not everything else), hoing that this helps.
The same error happens even if I assign currentRoomLostObject in the room_AfterFadeIn function, I tried.

I suppose I could repeat the function code in each room rather than make a single global function, but that seems a less elegant solution (besides, I might have another situation that requires to be handled in a similar way, so it's helpful to understand what I'm doing wrong here even if there is a better solution for this case - mind you, advice on how to handle this differently, if needed, are very welcome anyway).

Help, please? I reckon that the issue is that I'm not assigning a value to the pointer in the same function that uses it, but how can a global function figure out which object should become visible if assigning it each time I enter a room doesn't work? Besides, what's the point of a global pointer if I can't assign it's value in one place and then use it in another?
#6
Hello, I once again need your help figuring out how to update a system in my game in a sane way.

Context: in the bottom right corner of my game's screen there's a GUI that dispalys the avatar of the protagonist's starship's AI, Stantok (think Holly from Red Dwarf). It is made of a floating head, torso and hands. It is not actually animated, however it does change it's pose depending on it's “mood”, let's say; this “mood” is tracked by an integer variable whose values are not incremental, they just indicate a specific state (so 0 means neutral, 1 happy, 2 surprised, 3 sarcastic, etc.). Then there are add-ons that represent the ship's systems and are meant to be displayed on top of each part as if they were weared gadgets; they can be activated and deactivated by the player and are handled graphics-wise using the gStantok GUI buttons graphics. On top of all this, there's a ShipPowerLevel variable that tracks how much of the ship's power grid is restored and how much stuff is activated (so, once again, it's not a linear progression) and, from the graphics point of view, means that the add-ons look a bit different depending on that variable's value.
So, to recap, in terms of graphics there are a GUI background that is used to display the generic figure of the avatar and is updated every time AI's mood changes, and a series of GUI buttons that display some gadgets and they too are updated every time the mood changes because they need to match the base figure's pose, plus updated every time the player fixes some bits of the ship AND every time the player chooses to activate or deactivate something on the ship's computer.
In terms of parameters, we have the mood (which changes mostly on it's own), activated gadgets (which are activated manually so depend entirely on the player) and power level (which is only indirectly controlled by the player)

Now, the gameplay logic for all of this is in place and working properly and it's good enough (at least for me), what I'm really unhappy with is the logic that handles the graphics of this GUI elements.
Right now it works with a very brute force and dumb approach, which means a maze of if-else clauses that checks all possible combinations and is really unwieldy and basically impossible to upgrade/expand (not to mention read and debug) without major headaches; what I'd like to do is to make it more flexible and modular and possibly use less game resources to boot.

The current function is something like this:
Code: ags

void UpdateStantokGUI() {
	if (StantokMood == 1) {
		gStantok.BackgroundGraphic = 15;  //or whatever the sprite ID number is
		if (HandsActiveSystem == 1) {
			if (ShipPowerLevel == 1) {
				btnStantokHandsGear.NormalGraphics = 25;
			}
			else if (ShipPowerLevel == 2) {
				btnStantokHandsGear.NormalGraphics = 26;
			}
			//repeat the above for each power level
		}
		//repeat the above for each system and each “body” part.
	}
	else if (StantokMood ==2) {
	//repeat all the above for each mood state.
}


What I'd like to do is simplify that function to something like this:

Code: ags

if (StantokMood == 1) {
	gStantok.BackgroundGraphic = 15;
	btnStantokHandsGear.NormalGraphic = HandsGraphicsID; // by which I mean something like a pointer or variable containing the proper graphics ID depending on type, pose and upgrade, all defined and selected elsewhere.
	btnStantokHeadGear. NormalGraphic = HeadGraphicsID; //same
	btnStantokTorsoGear.NormalGraphic = TorsoGraphicsID;//same
}
else if (StantokMood = 2) {
//repeat
}
//and so on for the other mood values.


and use another function, hopefully less convoluted than the one I have now, to figure out which are the proper graphics at any given moment. The final goal is to make this logic completely indipendent from the actual sprite IDs so that adding new add-ons or removing/replacing exhisting ones becomes as easy and painless as possible, without messing too much (if any) with the functions that handle this GUI or the scripts that call these functions.

Therefore I started to create a struct that contains the list as int variables of all the sprite IDs correlated to any specific add-on, then created an array of these structs, so that each array entry represents one add-on. The (admittedly pretty vague) idea for this second function was some kind of “sorting engine” that could tell the game “this array entry and this struct entry are the ones we're currently using”.
Then I discovered why you need good plans, because the whole thing started to go down the wrong track and finally derailed when I discovered that I can't create a pointer to a custom array or struct (which was the way I thought I could use to set the right array entries as the currently used ones), so I had to once again use the if-else nest.

Help, please? Am I missing something or is this approach completely wrong for what I'd like to do? What should I look at in that case? Thanks in advance
#7
This is a follow-up to my old thread about a custom interaction menu: https://www.adventuregamestudio.co.uk/forums/index.php?topic=54624.0

I'm still usign the code you see in post 3, with updated variables names to fix the silly quasi-hungarian notation. I'll just quote the post for simplicity's sake:
Quote from: Lord Vetinari on Sun 26/03/2017 21:52:14
Code: ags

function on_mouse_click(MouseButton button) {
  // called when a mouse button is clicked. button is either LEFT or RIGHT
  Hotspot *activeHotspot;
  if (IsGamePaused() == 1) {
    // Game is paused, so do nothing (ie. don't allow mouse click)
  }
  else if (button == eMouseRight)  {
    //Here I put all the menu code.

    //Begin by resetting everything before a fresh start on a new hotspot
    gInteractionsMenu.Visible = false;  //This one is required because I had some fringe cases when a previous menu remained visible but with no options in the listbox, no idea why.
    ListInteractions.Clear();
    gInteractionsMenu.Height = 38;
    ListInteractions.Height = 0;
    
    //set the required control variables
    int AreThereInteractions = 0;
    vIntMouseActionX = mouse.x;
    vIntMouseActionY = mouse.y;
    activeHotspot = Hotspot.GetAtScreenXY(mouse.x, mouse.y);
    
    if(activeHotspot != null) //safety check: pointers can be "null", and if so you can't call methods on them (Thanks Snarky!)
    {
      //We add the options with a loop that automatically ends when there are no more options to be added
      vIntActionNumber = activeHotspot.GetProperty("Interaction_Number");
      String CurrentOption;
      int vActionCounter = 1;
      while (vIntActionNumber > 0) {
        CurrentOption = String.Format("Action_%d", vActionCounter);
        ListInteractions.AddItem (String.Format("%s", activeHotspot.GetTextProperty(String.Format("%s", CurrentOption))));  
        //The GUI is resized depending on how many items there are in the listbox
        gInteractionsMenu.Height += 23;
        ListInteractions.Height += 23;
        vIntActionNumber -=1;
        AreThereInteractions += 1;
        vActionCounter +=1;
      }
        if (AreThereInteractions > 0) {
        //If there are no options, the menu is not displayed.
        //I know that it would be enough to set this to 1 and call it a day, but I thought that adding and additional if clause in the loop to check it was superfluous
        gInteractionsMenu.X = mouse.x;
        gInteractionsMenu.Y = mouse.y;
        ListInteractions.SelectedIndex = -1;

        //Now we title the menu with the name of the hotspot
        if (activeHotspot.ID != 0) {
          lbMenuTitle.Text = String.Format("%s", activeHotspot.Name);
        }
        else {
          lbMenuTitle.Text = "Room";
          //This is here because sometimes even hotspot[0] has special interactions that are meant
          //to be available everywhere in the room, not only on actual interactive items.
          //For all the room this is not the case, the menu won't even appear if you don't click on an actual hotspot because AreThereInteractions is 0.
        }
        gInteractionsMenu.Visible = true;
      }
    }
  }
  else if (button == eMouseLeft) {
  cEgo.Walk(mouse.x, mouse.y eWalkableAreas);
  }
}

Now, that menu is supposed to work not only for hotspots, but for objects and characters as well.
Currently it does, but again I feel I did it in the least smart way possible: I set three pointers (Hotspot *activeHotspot, Object *activeObject, Character *activeCharacter), check each one at a time if it's not null (plus an additional safety check to exclude hotspot 0 when it comes to objects and characters), and copy/paste under each check the same code, just replacing the proper pointer each time.

I don't know much about coding, but I know that if you're copypasting the same lines of code several times, you're doing it the silly way and there's usually a better way of doing it. This has been bothering me for a while and today I feel in the right mood to try and fix it, but I can't seem to find a way. What should I look to understand what I should do? I thought enums, but, other than not knowing if I can make an enum with pointers, I still have to check which option of the enum is the right one, so that's not it.
#8
A bit of context: I made a game last year, a quite crappy one if I say so myself, by playing it VERY safe, using the most basic scripting and interactions and the least amount of custom made stuff based on examples, code monkey style. Now I'm trying to spice things up and customize things and generally trying to be more daring by upgrading/remaking it with some neater features (like the clock I asked about last time).

What I'm trying to replicate at the moment is something like Leisure Suit Larry 7's interactions menu. If you are not familiar with it, LSL7 used left click to move the character and possibly to execute the default interaction with something (but I'm not sure; I haven't replayed it recently and anyway I'm not really interested in this second functionality), and right clicks opened a small drop-down menu at the cursor's position with a list of potential interactions with that object/hotspot that changed depending on what you clicked on. It was also used to access the inventory and the ship's map and possibly some other generic options I can't remember when you right-clicked anywhere outside an interactive element (but again I'm not interested in that as much - which is kinda ironic, because it seems it's the only bit I can replicate as I know that no hotspot in AGS script is hotspot[0]).
I like this kind of interface, it's somewhat of a mix between the complexity of having multiple modes/verbs and the simplicity of the more modern one-click-does-everything systems. It also allowed to sneak in the occasional nice joke interactions (well, actually coarse joke interactions since we're speaking of LSL, but that's beyond the point).
I know, I'm trying this despite tha fact that a couple of days ago I wasn't able to properly format a string containing a couple of variables (later I found that my mistake is explained in the manual too - really sorry for that -, but for some reason I find it hard to navigate the manual and all those legacy references to very old versions of AGS make it hard for me to understand how you are supposed to properly spell things now in 3.4); It does seem a bit excessive for my skills, but as Ron Gilbert said, "sometimes it pays to be so stupid that you don't understand you can't do what you're trying to do" (ok, I'll probably never be as good a coder as Ron Gilbert and I'm definitely not making something as groundbreaking as Maniac Mansion, but hopefully even I can manage to achieve something).

I kinda managed to have something that resembles what I described using a custom GUI with a listbox and resizing both depending on the amount of options available, and basically bruteforcing it by replicating the same code (or at least variations of the same given the different actions allowed on each object/hotspot) in every single interactive element in one room, which I know is very bad programming and a good hint you need a generic function.
So I created a bunch of custom text properties for objects, characters and hotspots called Action1, Action2, etc, and tried to create a generic function in the general script that reads those and use them to fill the listbox.
The problem is that I can't find a way to read them if I don't specify the ID of the hotspot. I tried to use
Code: ags
Hotspot.GetProperty(Action1)
but it doesn't work, the compiler returns an error and, since every example I can find of it's use on the manual and the wiki use
Code: ags
hotspot[somenumber].GetProperty(action1)
I figured that you need to know what specific hotspot you are clicking on.
I may work around that by starting with a series of checks like
Code: ags
if (Hotspot.GetAtScreenXY(mouse.x, mouse.y) == hotspot[1])
which I know it works because it comes straight from the manual (and I used it to check that hotspot[0] case I talked about), but that seems blunt and bruteforceish too.
Is there a way to get the values of those properties without knowing the hotspot ID in advance? Or am I approaching the matter from a completely wrong angle?
If I'm doing everything wrong I'd like you to just point me in the right direction rather than giving me actual working code, I'd like to understand and figure it out myself, otherwise I'll never stop opening threads on the help forums :P
#9
Apparently I don't know how to do anything, please send help  :P
The very basic idea is this: there's a player controlled clock in game (I mean that time advances only if the player interacts with anything) and I'm trying to display it in a label on temporary GUI for testing purposes and I ended up immediately stuck.
I created three global variables in the global variables editor, vIntDays, vIntMinutes and vIntHours, created the gui and the label, then put in the repetedly_execute function of the global script this very simple line:
Code: ags

    lbClock.Text = ("Day %d", vIntDays, ", %d", vIntHours, ":%d", vIntMinutes);


The goal is to have it display something like "Day 1, 6:30")
I compile it and it says GlobalScript.asc(66): Error (line 66): Parse error in expr near '"Day %d"'
I dug an old question from the forums, stuff that dates back to more than 10 years ago, and the solution was to convert the int variables into strings before displaying them, so I did that:

Code: ags

    vStrDays = ("%d", vIntDays);
    vStrMinutes = ("%d", vIntMinutes);
    vStrHours = ("%d", vIntHours);
    lbClock.Text = ("Day ", vStrDays, " ", vStrHours, ":", vStrMinutes);


(of course I created the three new string global variables beforehand the same way).

Same results, except that the error now point to the very first line, Error (line 66): Parse error in expr near '"%d"'. So obviously either %d doesn't work (but the forums says it does) or the game doesn't recognize the variables (but I can find them in the autocomplete), or repetedly_execute is not the right place (but where else could I put it to keep the clock updated?) What am I doing wrong? The manual says that if you create a variable in the global variable editor you don't have to declare and/or import it the old way.
I haven't coded the clock, yet (this was the preparatory bit before delving into more complex scripting. I'm off to a great start!), but it shouldn't be an issue because I gave all the int variable the base value 0 in the editor.
#10
After quite a few unreleased test games, I'm trying to make something more complex. In this game, the main character's sidekick is an AI thingy that lives in the protagonist's wrist watch. I'm planning to make a closeup of the watch in the bottom right corner. The appearence of the AI's head, arms and torso will change a few times throughout the game depending on the solution of certain puzzles. I'd like to give the player relative freedom to choose which goal to tackle first, which means that I can't draw a linear progression of the AI's looks.
I think I know how to do this, my question is: what is better from the coding, performance and memory usage point of view? Three overlapping AGS characters (head, torso, arms) so that I can change each one's view individually, or a single AGS character with an exponentially larger amount of views and loops to cover each potential combination?
SMF spam blocked by CleanTalk