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 - eri0o

#81
I recently read some books on StoryWriting, so I would like to share them and possibly see if there are more that are recommended. If you guys have more recommendations on books for story writing be it for Screenwriting or for Videogames, I would be interested on opinions too!



The Anatomy of Story - John Truby

This is a medium-sized, well-written book, that will give multiple tools to help you layout your story. This is a book that I keep by my side on the computer, so I can consult it when writing a narrative. It does very short, one-sentence analysis of multiple concepts in different movies to help you understand the concept. It's also a great book to give you vocabulary. I thought it was a good compromise between the menacing huge Robert McCree's Story and the short and quick Syd Field's Screenplay. It's a book I finding myself picking up more often than not.

Screenplay - Syd Field

This is a good book, a bit short, and focused on the three-act structure strategy. This book gave me my first experience of reading a script and analyzing it. Overall, I find it is a good book to read at the start, it will call you to action, meaning, to read scripts of movies you like. Those can usually be found on libraries if you look into, but you can also find the pdf online if you search. Reading scripts of movies you have watched and liked is a good exercise to understand how to write your story before executing, but after having it planned.

Notes on Directing - Frank Hauser & Russel Reich

This is a brief book, with very quick tips on both leading a project and executing a story, it's cool how a lot of stuff from directing a play can be translated to directing a game. Even though the text of each note is very short, I don't recommend to read it when starting, read some other books to build vocabulary and understanding of the craft, then pick this up on a slow afternoon for a short read to help you solidify the lessons you have gotten so far.

Creating Character Arcs - K. M. Weiland

This is a good book if you need help writing good characters. It also has a  nice audiobook to pair with - most Storywriting books are read by their authors, but not this one, this has an actual professional audiobook reader that makes the experience good! This is a good book to read from start to end. The focus here is the characters since the characters exist in a different time and space than your story is told, you can kind of figure them out more easily, then go to figure out your story using tools from Screenplay and Anatomy of Story, and then come back to this book to figure out how you are going to pick the important bits from the characters arcs. This is a well written, well-organized book, with examples that are well explained.

Writing Vivid Settings - Rayne Hall

This is the only other book on this list that the audiobook is good, so you can pick it up too. This is the guide on how to write Look Ats in Adventure Games. It will give a lot for you to ponder. To me, this book clicked after reading chapter 8. After that, I finished the book, and read it again. If you find yourself drifting trying to understand what is the point, skip to chapter 8, then read chapter 7, and then read the book again. This is a good book, the way it's structured is boring and I also don't think the organization is the best. But the content is solid and I will say this is the one that is very very important when making a game.

Becca Puglisi and Angela Ackerman Writer's Guide Thesaurus collection

I am grouping here six books:

  • The Emotional Wound Thesaurus
  • The Urban Setting Thesaurus
  • The Rural Setting Thesaurus
  • The Positive Trait Thesaurus
  • The Negative Trait Thesaurus
  • The Emotion Thesaurus

When you are building your story, at some points you are either going to hit blanks or use some placeholders in your characters and in your story that will be hard to be replaced with an actual story relevant things. These are collections of really useful things. These books are to the point, well written, and they all have some initial chapters that help you understand how to use the collection that will follow. They will NOT give you a story from the void, but if you have something and need help filling the voids, these are good. I love the Emotional Wounds Thesaurus - A writer's guide to Psychological Trauma, and exploring the Urban Setting Thesaurus is fun too. My only regret is I got them digital (because I wanted to be able to search their texts) but now I think having them in paper format would be much more fun to browse and that some of they may have found a spot near my PC too to be browsed from time to time.

Psychology for Screenwriters - Wiliam Indick

Part One of this book is based on Freudian psychology. I read it with a fit of anger because the stuff Freud says falls flat to me. Overall even when considering other chapters a lot of the notions described felt dated, as the movies used to exemplify concepts. I didn't like this book overall, the tips are more useful if you are polishing some already existing character and I think there are better books for that. This is money I spent I wish I didn't. Reading this book felt like forever.

Write Your Novel from the Middle - James Scott Bell

The audiobook is terrible, don't get that, get the book and read. Also, the author tries too much to sell you his idea of writing the novel from the middle, in a constant sales pitch that is kinda nonsense since you already committed and bought the book. If you can ignore this, then this is a solid book with good advice to help you start! If you have watched Parasite and played Portal, you know the importance of the midpoint on your story. I liked this book, but I think it could be made shorter and again, not sure about how the book is organized. Still, if you are staring at a blank page, get this book.
#82
https://www.gamasutra.com/view/news/176420/Indepth_Using_Chrometracing_to_view_your_inline_profiling_data.php | webarchive link

QuoteIn-depth: Using Chrome://tracing to view your inline profiling data

If you have an inline profiling system, with some slight modifications, you can view that data in the Chrome web browser rather than writing a visualization tool yourself.

Having a nice graphical representation of profiling data is crucial to finding critical hotspots in a realtime application. Creating a viewer application for that custom data is a massive time sink, so before you start writing ones, know your options.

Getting started with profiling your game...

If you are using a Google Chrome / Chromium browser, you can click the link chrome://tracing/, to open an already included profiling tool - you need to copy and paste it into your address bar since the forums automatically add http to the link, and it's not an http link. I found the above article very interesting on how to use it offline on your game to visualize system bottlenecks by producing a json file with a predefined structured. This may be useful to someone else, so I am leaving it linked here.
#83
AgsGet  version 0.1.0

For information on how to install, see the AGS Manual on how to install plug-ins.



Warning: Dangerously Experimental! Read below before using!

Get Latest Release AGS.Plugin.AgsGet.dll | GitHub Repository

AgsGet is an Editor Plugin for Managing Script Modules. Click the gif below to see it on video.




Warning: Dangerously Experimental! Read below before using!

This Editor Plug-in has the following limitations:

  • It won't work in your game if you are using Script Folders;
  • There may be a case where it crashes and leaves your game in broken state, requiring manual edit of Game.agf;
  • If you install or remove a script module that has the same name of a script in your game it will delete your script and replace it for the module;
  • If you ever install or uninstall a package, any script module listed in the lockfile will replace any changes you do to them for the ones in the Package Cache.


Available features

Supports installing packages from my AGS Module List. You can also uninstall packages installed through AgsGet interface. If a package depends on other packages, the dependencies will be installed too, on the correct order in the scripts list.

A simplified search is provided too, an empty parameter will return all modules available.

For now, it doesn't care for versions, so if you install a package and a newer version of the package is made available on the package index the module you are using may get overwritten by a new version if the package is updated in your package cache.

This plugin should not save anything on itself on your Game.agf, so you should be able to just load a game you used it in an AGS Editor that doesn't have it.

How it works

AgsGet may create the following files in your game directory.

Code: ags

â"œâ"€â"€ ags_packages_cache/
â",   â"œâ"€â"€ package_index
â",   â"œâ"€â"€ pkg1/
â",   â",   â""â"€â"€ pkg1.scm
â",   â""â"€â"€ pkg2/
â",       â""â"€â"€ pkg2.scm
â",   ...
â", 
â"œâ"€â"€ agsget-lock.json
â"œâ"€â"€ agsget-lock.json.removal
â""â"€â"€ agsget-manifest.json


Let's see the steps involved in installing a package:


  • At first, when loading AgsGet, if no package_index is found, it will create the ags_packages_cache directory, and place it inside the package_index file, which is obtained directly from here: https://ericoporto.github.io/agsModuleList/index/package_index.json .
  • Whenever you install a package, AgsGet reads the agsget-manifest.json to figure out which packages are already installed and recreates it including the new package.
  • If there's any agsget-lock.json.removal, it's deleted, and if there's any agsget-lock.json, it's renamed to agsget-lock.json.removal.
  • Then, it reads this generated agsget-manifest.json and the package_index to figure out if the packages have any dependencies, and order them correctly, using topological sorting, writing the resulting list in agsget-lock.json.
  • It then removes and deletes any script and headers in the project with the same name as a package in the agsget-lock.json.
  • It then will download from https://ericoporto.github.io/agsModuleList any package not on cache that it's listed in the agsget-lock.json.
  • Finally, all packages listed in agsget-lock.json gets imported in the Game.agf project and shows in the editor.

Removing a package will remove the package from agsget-manifest.json, and the agsget-lock.json will be recreated, with the previous lock saved as agsget-lock.json.removal. Packages that are listed on agsget-lock.json.removal, but not on agsget-lock.json get removed.

Where do we go from here

This package manager implementation is created more as a proof of concept that it can be done. It appears that two things are actually the most iminent needs:

- A place to list all Script Modules including their metadata (specially to allow creating modules that depends from other modules).
- A place to store these Script Module that everyone can trust (this is a much harder problem to figure out).

Once the above is properly structured, creating the package manager to work with above is a much minor problem.

I created this Editor plug-in cutting a lot of corners to make it live in the shortest amount of time, the main idea was that people could experiment with it and talk about what they think of this idea, assessing if there's need from the community for a package manager or a central database... It's important to note that this tool is supposed to work along with the forum since packages don't work without communities. Mostly of the ideas in making this package manager were taken from this article from Sam Boyer, which has a presented design inspired by the package manager npm. Additionally, the general design of the package repository is based on early iterations of the vcpkg, see it's ports directory.


Troubleshooting

If you downloaded this plugin from the internet and AGS complained something about policies when loading, the file is locked. You need to right click it, click on properties and then click Unblock, see image below.


Edit: I've since include more modules in the website and index accessible through the plugin, if someone is missing a particular module, just comment on it and I will add it.
#84
I need a C# programmer to work together to build an Editor plug-in. This person needs to read the READMEs in this project below and the issues to familiarize with it. Questions can be asked by opening issues in the project and contribution can be made by opening Pull Requests. The person must be familiar with the basics of git.

https://github.com/ericoporto/agsModuleList

I built a small command line tool and need help giving it an editor plug-in interface. If someone is interested please answer here, technical questions can be directly asked by opening issues or commenting on the existing ones in the GitHub. I will not be paying for these services.

Some specifics of the Editor plug-in requirements is written here:
https://github.com/ericoporto/agsModuleList/issues/4
#85
Hey, I decided to give a shot at trying to build another Winsetup alternative.


Spoiler
[close]

The code is written in C++ and it's available on GitHub github.com/ericoporto/agsconfig.

Binaries are provided on the releases page here, under assets, for Windows, MacOS and Linux. I have successfully run the Linux binary, but I have not yet tested the other binaries provided by the Continuous Integration system but they may work.

Remember that you will need to set permissions for MacOS and Linux after download (chmod +x agsconfig).

Instructions

Place agsconfig executable at the same location of your game entry-point (mygame) or binary (mygame.exe). On MacOS, place it at the side of the AGS Engine binary.

Create on the same directory a text file named .config, and add the following content to it:

Code: ini
[game]
executable = mygame


On MacOS, set the value of the executable to the binary of the AGS Engine and certify the AGS Engine is finding your game on execution. If you are using on Windows your executable should be mygame.exe.

Better instructions will be written over time on the README in the GitHub page. Oh the above GUI should be compatible with any gamepad too.

Note: The intent of this tool is to be shipped by the game developer along with their game when releasing in a non-windows platform. It can also be shipped with the Windows build if there's intent on customizing it and provide good consistency accross OSes. The source code should be fairly readable.

Development

If you wish to develop with agsconfig, all the logic is in a library in a directory called core and you can use just it if you wish to implement your own interface. The entirety of the interface with comments is the 400 lines of code agsconfig.cpp file. There's no ui code in the core.
#86
Advanced Technical Forum / Parsing JSON in AGS
Sun 12/01/2020 03:12:51
So, I kinda was curious about parsing JSON on AGS, I had a need for reading and not writing. Still, I decided to go on and look on Google, and found this c library called JSMN: https://github.com/zserge/jsmn/blob/master/jsmn.h

The code looked simple enough, so I decided to try to port it to AGS Script...

jsmn.ash
Spoiler
Code: ags

// new module header

/**
 * JSON type identifier. Basic types are:
 * 	o Object
 * 	o Array
 * 	o String
 * 	o Other primitive: number, boolean (true/false) or null
 */
 enum jsmntype {
  eJSMN_UNDEFINED = 0,
  eJSMN_OBJECT = 1,
  eJSMN_ARRAY = 2,
  eJSMN_STRING = 3,
  eJSMN_PRIMITIVE = 4
};

enum jsmnerr {
  /* Not enough tokens were provided */
  eJSMN_ERROR_NOMEM = -1,
  /* Invalid character inside JSON string */
  eJSMN_ERROR_INVAL = -2,
  /* The string is not a full JSON packet, more bytes expected */
  eJSMN_ERROR_PART = -3
};

/**
 * JSON token description.
 * type		type (object, array, string etc.)
 * start	start position in JSON data string
 * end		end position in JSON data string
 */
managed struct jsmntok {
  jsmntype type;
  int start;
  int end;
  int size;
  int parent;
};

/**
 * JSON parser. Contains an array of token blocks available. Also stores
 * the string being parsed now and current position in that string.
 */
managed struct jsmn_parser{
  int pos;     /* offset in the JSON string */
  int toknext; /* next token to allocate */
  int toksuper;         /* superior token node, e.g. parent object or array */
};

/**
 * Create JSON parser over an array of tokens
 */
import void jsmn_init(jsmn_parser *parser);

/**
 * Run JSON parser. It parses a JSON data string into and array of tokens, each
 * describing
 * a single JSON object.
 */
import int jsmn_parse(jsmn_parser *parser, String js, int len, jsmntok *tokens[], int num_tokens);
[close]


jsmn.asc
Spoiler
Code: ags

// new module script

/**
 * Allocates a fresh unused token from the token pool.
 */
jsmntok *jsmn_alloc_token(jsmn_parser *parser, jsmntok *tokens[], int num_tokens) {
  jsmntok *tok;
  if (parser.toknext >= num_tokens) {
    return null;
  }
  parser.toknext = parser.toknext+1;
  tok = tokens[parser.toknext];
  tok.end = -1;
  tok.start = -1;
  tok.size = 0;
  tok.parent = -1;
  return tok;
}

/**
 * Fills token type and boundaries.
 */
void jsmn_fill_token(jsmntok *token, jsmntype type, int start, int end) {
  token.type = type;
  token.start = start;
  token.end = end;
  token.size = 0;
}


int _parse_found(int start, jsmn_parser *parser, String js, int len, jsmntok *tokens[], int num_tokens){
  jsmntok *token;
  if (tokens == null) {
    parser.pos--;
    return 0;
  }
  token = jsmn_alloc_token(parser, tokens, num_tokens);
  if (token == null) {
    parser.pos = start;
    return eJSMN_ERROR_NOMEM;
  }
  jsmn_fill_token(token, eJSMN_PRIMITIVE, start, parser.pos);

  token.parent = parser.toksuper;
  parser.pos--;
  return 0;
}

/**
 * Fills next available token with JSON primitive.
 */
int jsmn_parse_primitive(jsmn_parser *parser, String js, int len, jsmntok *tokens[], int num_tokens) {
  jsmntok *token;
  int start;

  start = parser.pos;

  for (; parser.pos < len && js.Chars[parser.pos] != 0; parser.pos++) {
    switch (js.Chars[parser.pos]) {

    /* In strict mode primitive must be followed by "," or "}" or "]" */
    case ':':
    case 20: /* '\t' */
    case 18: /* '\r' */
    case 14: /* '\n' */
    case ' ':
    case ',':
    case ']':
    case '}':
      return _parse_found(start, parser, js, len, tokens, num_tokens);
    default:
                   /* to quiet a warning from gcc*/
      break;
    }
    if (js.Chars[parser.pos] < 32 || js.Chars[parser.pos] >= 127) {
      parser.pos = start;
      return eJSMN_ERROR_INVAL;
    }
  }
  
  /* In strict mode primitive must be followed by a comma/object/array */
  parser.pos = start;
  return eJSMN_ERROR_PART;
}

/**
 * Fills next token with JSON string.
 */
int jsmn_parse_string(jsmn_parser *parser, String js, int len, jsmntok *tokens[], int num_tokens) {
  jsmntok *token;

  int start = parser.pos;

  parser.pos++;

  /* Skip starting quote */
  for (; parser.pos < len && js.Chars[parser.pos] != 0; parser.pos++) {
    char c = js.Chars[parser.pos];

    /* Quote: end of string */
    if (c == '"') {
      if (tokens == null) {
        return 0;
      }
      token = jsmn_alloc_token(parser, tokens, num_tokens);
      if (token == null) {
        parser.pos = start;
        return eJSMN_ERROR_NOMEM;
      }
      jsmn_fill_token(token, eJSMN_STRING, start + 1, parser.pos);
      token.parent = parser.toksuper;
      return 0;
    }

    /* Backslash: Quoted symbol expected */
    if (c == 92 && parser.pos + 1 < len) {
      int i;
      parser.pos++;
      switch (js.Chars[parser.pos]) {
      /* Allowed escaped symbols */
      case '"':
      case '/':
      case 92:
      case 'b':
      case 'f':
      case 'r':
      case 'n':
      case 't':
        break;
      /* Allows escaped symbol \uXXXX */
      case 'u':
        parser.pos++;
        for (i = 0; i < 4 && parser.pos < len && js.Chars[parser.pos] != 0; i++) {
          /* If it isn't a hex character we have an error */
          if (!((js.Chars[parser.pos] >= 48 && js.Chars[parser.pos] <= 57) ||   /* 0-9 */
                (js.Chars[parser.pos] >= 65 && js.Chars[parser.pos] <= 70) ||   /* A-F */
                (js.Chars[parser.pos] >= 97 && js.Chars[parser.pos] <= 102))) { /* a-f */
            parser.pos = start;
            return eJSMN_ERROR_INVAL;
          }
          parser.pos++;
        }
        parser.pos--;
        break;
      /* Unexpected symbol */
      default:
        parser.pos = start;
        return eJSMN_ERROR_INVAL;
      }
    }
  }
  parser.pos = start;
  return eJSMN_ERROR_PART;
}


/**
 * Parse JSON string and fill tokens.
 */
int jsmn_parse(jsmn_parser *parser, String js, int len, jsmntok *tokens[], int num_tokens) {
  int r;
  int i;
  jsmntok *t;
  jsmntok *token;
  int count = parser.toknext;
  
  for (; parser.pos < len && js.Chars[parser.pos] != 0; parser.pos++) {
    char c;
    jsmntype type;
    
    c = js.Chars[parser.pos];
    
    switch (c) {
    case '{':
    case '[':
      count++;
      if (tokens == null) {
        break;
      }
      token = jsmn_alloc_token(parser, tokens, num_tokens);
      if (token == null) {
        return eJSMN_ERROR_NOMEM;
      }
      if (parser.toksuper != -1) {
        t = tokens[parser.toksuper];

        /* In strict mode an object or array can't become a key */
        if (t.type == eJSMN_OBJECT) {
          return eJSMN_ERROR_INVAL;
        }

        t.size++;
        token.parent = parser.toksuper;
      }
      if(c == '{') token.type = eJSMN_OBJECT;
      else token.type = eJSMN_ARRAY;
      token.start = parser.pos;
      parser.toksuper = parser.toknext - 1;
      break;
    case '}':
    case ']':
      if (tokens == null) {
        break;
      }
      
      if(c == '}') type = eJSMN_OBJECT;
      else type = eJSMN_ARRAY;

      if (parser.toknext < 1) {
        return eJSMN_ERROR_INVAL;
      }
      token = tokens[parser.toknext - 1];
      for (;;) {
        if (token.start != -1 && token.end == -1) {
          if (token.type != type) {
            return eJSMN_ERROR_INVAL;
          }
          token.end = parser.pos + 1;
          parser.toksuper = token.parent;
          break;
        }
        if (token.parent == -1) {
          if (token.type != type || parser.toksuper == -1) {
            return eJSMN_ERROR_INVAL;
          }
          break;
        }
        token = tokens[token.parent];
      }
      break;
    case '"': /* '\"' */
      r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
      if (r < 0) {
        return r;
      }
      count++;
      if (parser.toksuper != -1 && tokens != null) {
        tokens[parser.toksuper].size++;
      }
      break;
    case 20: /* '\t' */
    case 18: /* '\r' */
    case 14: /* '\n' */
    case ' ':
      break;
    case ':':
      parser.toksuper = parser.toknext - 1;
      break;
    case ',':
      if (tokens != null && parser.toksuper != -1 &&
          tokens[parser.toksuper].type != eJSMN_ARRAY &&
          tokens[parser.toksuper].type != eJSMN_OBJECT) {

        parser.toksuper = tokens[parser.toksuper].parent;


      }
      break;
      
    /* In strict mode primitives are: numbers and booleans */
    case '-':
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
    case 't':
    case 'f':
    case 'n':
      /* And they must not be keys of the object */
      if (tokens != null && parser.toksuper != -1) {
        t = null;
        t = tokens[parser.toksuper];
        if (t.type == eJSMN_OBJECT ||
            (t.type == eJSMN_STRING && t.size != 0)) {
          return eJSMN_ERROR_INVAL;
        }
      }

      r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
      if (r < 0) {
        return r;
      }
      count++;
      if (parser.toksuper != -1 && tokens != null) {
        tokens[parser.toksuper].size++;
      }
      break;

    /* Unexpected char in strict mode */
    default:
      return eJSMN_ERROR_INVAL;
    }
    
  }
  
}

/**
 * Creates a new parser based over a given buffer with an array of tokens
 * available.
 */
void jsmn_init(jsmn_parser *parser) {
  parser.pos = 0;
  parser.toknext = 0;
  parser.toksuper = -1;
}

[close]

Now things appear to compile, so I decided to try it, I created a new room and tried to follow the example in the library here: https://github.com/zserge/jsmn/blob/master/example/simple.c

Room1.asc
Spoiler
Code: ags

// room script file

int jsoneq(String json, jsmntok *tok, String s) {
  if (tok.type == eJSMN_STRING && s.Length == (tok.end - tok.start) &&
      s == json.Substring(tok.start, tok.end-tok.start) ) {
    return 0;
  }
  return -1;
}

#define MAX_TOKENS 256

function room_AfterFadeIn()
{
  String simple_json = "{ \"name\":\"John\", \"age\":30, \"car\":null }";
  
  jsmn_parser* parser = new jsmn_parser;
  jsmn_init(parser);
  jsmntok* t[] = new jsmntok[MAX_TOKENS];
  for(int i=0; i<MAX_TOKENS; i++) t[i] = new jsmntok;
  int r;
  
  r = jsmn_parse(parser, simple_json, simple_json.Length, t, MAX_TOKENS);
  
  if (r < 0) {
    Display("Failed to parse JSON: %d\n", r);
    return 1;
  }
  
    /* Assume the top-level element is an object */
  if (r < 1 || t[0].type != eJSMN_OBJECT) {
    Display("Object expected\n");
    return 1;
  }
  
  
  //jsmn_
}
[close]

But I kinda couldn't advance much, I think I don't really understand how it's supposed to be used to parse things. :/

Does it expected usage makes sense to anybody?

The project can be downloaded here.
#87
agsimgui  version 0.3.0



Get Latest Release agsimgui.dll | libagsimgui.so | libagsimgui.dylib | GitHub Repo | Demo Windows | Demo Linux

Dear ImGui plugin for Adventure Game Studio



Supports Directx9 and Software renderer. Using OpenGL will default to Software renderer for ImGui only (it has it's own renderer), but OpenGL support will be provided in the future.

A more complete documentation is available here because AGS Forums have a 30000 character limit per posting.

Usage example

Run a simple demo window to see what's possible with Dear ImGui.
Code: AGS

// use function room_RepExec() when in Room Script and link it throught the editor
void repeatedly_execute(){
  AgsImGui.NewFrame(); //let's begin a new frame, we end it with a Render
  AgsImGui.ShowDemoWindow(); //Shows a demo of everything possible
  AgsImGui.Render(); // This will generate drawing instructions. 
  // AGS will actually draw this on screen later on, on Post Screen Draw stage.
}

Some of what is shown on Demo Window is not yet exposed in the AgsImGui Script API.

Let's do a simple example now.
Code: AGS

bool is_button_clicked;

// use function room_RepExec() when in Room Script and link it throught the editor
void repeatedly_execute(){
  AgsImGui.NewFrame(); //let's begin a new frame, we end it with a Render

AgsImGui.BeginWindow("My first window");    
  ViewFrame* vf = Game.GetViewFrame(player.View, player.Loop, player.Frame);
  is_button_clicked = AgsImGui.ImageButton(vf.Graphic); 
  if(AgsImGui.IsItemHovered()) AgsImGui.SetTooltip(String.Format("frame %d",player.Frame));
  player.x = AgsImGui.DragInt("player.x", player.x);
  player.y = AgsImGui.DragInt("player.y", player.y);
  AgsImGui.EndWindow();

AgsImGui.Render(); // This will generate drawing instructions. 
  // AGS will actually draw this on screen later on, on Post Screen Draw stage.
}

Note ImGui will save a imgui.ini file to allow window positions and some more data to persist between sections when no guidance is given.



AGS Script API
Spoiler

Main

  • AgsImGui.NewFrame

    Code: ags
    static void AgsImGui.NewFrame()


    Call this before calling any AgsImGui commands.


  • AgsImGui.EndFrame

    Code: ags
    static void AgsImGui.EndFrame()


    We don't need this if we are using Render, since it will automatically call AgsImGui.EndFrame() too.


  • AgsImGui.Render

    Code: ags
    static void AgsImGui.Render()


    This will EndFrame and proceed to generate drawing instructions.


  • AgsImGui.GetStyle

    Code: ags
    static ImGuiStyle* AgsImGui.GetStyle()


    Gets the Style customization use in AgsImGui. Check ImGuiStyle for more information.


  • AgsImGui.SetStyle

    Code: ags
    static void AgsImGui.SetStyle(ImGuiStyle* imGuiStyle)


    Sets the Style customization use in AgsImGui. Check ImGuiStyle for more information.

    Code: AGS
    
    ImGuiStyle* style = AgsImGui.GetStyle();
    style.Alpha = 0.5
    style.Colors[eImGuiCol_PopupBg] = ImVec4.Create(1.00, 1.00, 1.00, 1.00); //white
    AgsImGui.SetStyle(style);
    




    Demo, Debug, Information

  • AgsImGui.GetVersion

    Code: ags
    static String AgsImGui.GetVersion()


  • AgsImGui.ShowDemoWindow

    Code: ags
    static void AgsImGui.ShowDemoWindow()


  • AgsImGui.ShowAboutWindow

    Code: ags
    static void AgsImGui.ShowAboutWindow()


  • AgsImGui.ShowMetricsWindow

    Code: ags
    static void AgsImGui.ShowMetricsWindow()


  • AgsImGui.ShowUserGuide

    Code: ags
    static void AgsImGui.ShowUserGuide()





    Windows

  • AgsImGui.BeginWindow

    Code: ags
    static AgsImGui.ImGuiBeginWindow BeginWindow(String name, bool has_close_button = 0, ImGuiWindowFlags flags = 0)


  • AgsImGui.EndWindow

    Code: ags
    static void EndWindow()





    Child Windows

  • AgsImGui.BeginChild

    Code: ags
    static bool BeginChild(String str_id, int width = 0, int height = 0, bool border = false, ImGuiWindowFlags flags = 0)


    Child Windows. Always call a matching EndChild() for each BeginChild() call, regardless of its return value. Child windows can embed their own child.

  • AgsImGui.EndChild

    Code: ags
    static void EndChild()


    Pop child window from the stack.




    Item/Widgets Utilities

    Most of the functions are referring to the last/previous item we submitted.

  • AgsImGui.IsItemHovered

    Code: ags
    static bool AgsImGui.IsItemHovered(ImGuiHoveredFlags flags = 0)


    Is the last item hovered? (and usable, aka not blocked by a popup, etc.). You can specify a flag for more options.

    static bool IsItemHovered(ImGuiHoveredFlags flags = 0)

  • AgsImGui.IsItemActive

    Code: ags
    static bool AgsImGui.IsItemActive()


    Is the last item active? (e.g. button being held, text field being edited. This will continuously return true while holding mouse button on an item.)

  • AgsImGui.IsItemFocused

    Code: ags
    static bool AgsImGui.IsItemFocused()


    Is the last item focused for keyboard navigation?

  • AgsImGui.IsItemVisible

    Code: ags
    static bool AgsImGui.IsItemVisible()


    Is the last item visible? (items may be out of sight because of clipping/scrolling)

  • AgsImGui.IsItemEdited

    Code: ags
    static bool AgsImGui.IsItemEdited()


    Did the last item modify its underlying value this frame? or was pressed? This is generally the same as the bool return value of many widgets.

  • AgsImGui.IsItemActivated

    Code: ags
    static bool AgsImGui.IsItemActivated()


    Was the last item just made active (item was previously inactive).

  • AgsImGui.IsItemDeactivated

    Code: ags
    static bool AgsImGui.IsItemDeactivated()


    Was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that requires continuous editing.

  • AgsImGui.IsItemDeactivatedAfterEdit

    Code: ags
    static bool AgsImGui.IsItemDeactivatedAfterEdit()


    Was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that requires continuous editing.

  • AgsImGui.IsItemToggledOpen

    Code: ags
    static bool AgsImGui.IsItemToggledOpen()


    Was the last item open state toggled? set by TreeNode().

  • AgsImGui.IsAnyItemHovered

    Code: ags
    static bool AgsImGui.IsAnyItemHovered()


    is any item hovered?

  • AgsImGui.IsAnyItemActive

    Code: ags
    static bool AgsImGui.IsAnyItemActive()


    is any item active?

  • AgsImGui.IsAnyItemFocused

    Code: ags
    static bool AgsImGui.IsAnyItemFocused()


    is any item focused?

  • AgsImGui.IsWindowAppearing

    Code: ags
    static bool AgsImGui.IsWindowAppearing()


    'current window' = the window we are appending into while inside a Begin()/End() block.

  • AgsImGui.IsWindowCollapsed

    Code: ags
    static bool AgsImGui.IsWindowCollapsed()


    return true when window is collapsed. Use this between Begin and End of a window.

  • AgsImGui.IsWindowFocused

    Code: ags
    static bool AgsImGui.IsWindowFocused(ImGuiFocusedFlags flags=0)


    is current window focused? or its root/child, depending on flags. see flags for options. Use this between Begin and End of a window.

  • AgsImGui.IsWindowHovered

    Code: ags
    static bool AgsImGui.IsWindowHovered(ImGuiHoveredFlags flags=0)


    is current window hovered (and typically: not blocked by a popup/modal)? see flags for options. Use this between Begin and End of a window.




    Widgets: Text

  • AgsImGui.Text

    Code: ags
    static void AgsImGui.Text(String text)


    Draws a string of text.

  • AgsImGui.TextColored

    Code: ags
    static void AgsImGui.TextColored(int ags_color, String text)


    shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor();

  • AgsImGui.TextDisabled

    Code: ags
    static void AgsImGui.TextDisabled(String text)


    shortcut for PushStyleColor(ImGuiCol[cur]Text, style.Colors[ImGuiCol[/cur]TextDisabled]); Text(fmt, ...); PopStyleColor();

  • AgsImGui.TextWrapped

    Code: ags
    static void AgsImGui.TextWrapped(String text)


    shortcut for PushTextWrapPos(0.0f); Text(fmt, ...); PopTextWrapPos();.

  • AgsImGui.LabelText

    Code: ags
    static void AgsImGui.LabelText(String label, String text)


    Display text+label aligned the same way as value+label widgets .

  • AgsImGui.Bullet

    Code: ags
    static void AgsImGui.Bullet(String text)


    Draws a bullet and a string of text. Shortcut for Bullet()+Text().




    Widgets: Main

    Most widgets return true when the value has been changed or when pressed/selected.

    You may also use one of the many IsItemXXX functions (e.g. IsItemActive, IsItemHovered, etc.) to query widget state.

  • AgsImGui.Button

    Code: ags
    static bool AgsImGui.Button(String label, int width = 0, int height = 0)


    Creates a button. Returns true while button is pressed.

  • AgsImGui.SmallButton

    Code: ags
    static bool AgsImGui.SmallButton(String label)


    Creates a button with no padding to be easier to embed within text. Returns true while button is pressed.

  • AgsImGui.Image

    Code: ags
    static void AgsImGui.Image(int sprite_id)


    Create an image with passed sprite ID.

  • AgsImGui.ImageButton

    Code: ags
    static bool AgsImGui.ImageButton(int sprite_id)


    Create a button with an image with passed sprite ID. Returns true while button is pressed.

  • AgsImGui.ArrowButton

    Code: ags
    static bool AgsImGui.ArrowButton(String label, ImGuiDir dir)


    Creates a button with an arrow shape. Returns true while button is pressed.

  • AgsImGui.Checkbox

    Code: ags
    static bool AgsImGui.Checkbox(String label, bool initial_value)


    Creates a checkbox button. Returns true when button is marked.

  • AgsImGui.RadioButton

    Code: ags
    static bool AgsImGui.RadioButton(String label, bool active)


    Creates a radio button. Returns true while button is marked.

  • AgsImGui.Bullet

    Code: ags
    static void AgsImGui.Bullet()


    Draw a small circle and keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses .




    Widgets: Selectables

    A selectable highlights when hovered, and can display another color when selected.

  • AgsImGui.Selectable

    Code: ags
    static bool AgsImGui.Selectable(String label, bool selected = false, ImGuiSelectableFlags flags = 0, int width = 0, int height = 0)


    bool selected carry the selection state (read-only). When Selectable() is clicked it returns true so you can modify your selection state.




    Widgets: Drag

    CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped and can go off-bounds.

  • AgsImGui.DragFloat

    Code: ags
    static float AgsImGui.DragFloat(String label, float value, float min_value = 0, float max_value = 0, float speed = 0, String format = 0)


    Returns the current value of the drag box. Format string uses regular
    Code: ags
    "%f"
    specifiers, so you can use to define how many decimals you want.

  • AgsImGui.DragInt

    Code: ags
    static int AgsImGui.DragInt(String label, int value, int min_value = 0, int max_value = 0, float speed = 0, String format = 0)


    Returns the current value of the drag box. Format string uses regular
    Code: ags
    "%d"
    , you can use it to specify left most zeroes.




    Widgets: Slider

    CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped and can go off-bounds.

  • AgsImGui.SliderFloat

    Code: ags
    static float AgsImGui.SliderFloat(String label, float value, float min_value = 0, float max_value = 0, String format = 0)


    Returns the current value of the slider. Format string uses regular
    Code: ags
    "%f"
    specifiers, so you can use to define how many decimals you want.

  • AgsImGui.SliderInt

    Code: ags
    static int AgsImGui.SliderInt(String label, int value, int min_value = 0, int max_value = 0, String format = 0)


    Returns the current value of the slider. Format string uses regular
    Code: ags
    "%d"
    , you can use it to specify left most zeroes.




    Widgets: Input with Keyboard

  • AgsImGui.InputText

    Code: ags
    static String AgsImGui.InputText(String label, String text_buffer, int buffer_size, ImGuiInputTextFlags flags =0)


    Pass a string to identify the input field as label, this label is going to be used for ID and can't be empty.

    Remember that we don't save state between frames, so you have to do this yourself by reapplying the output to input as needed.

    This function returns null when the input hasn't changed, and returns a string with all the text, when it has been modified.

    This widget supports Ctrl+Z for undo, Ctrl+X for Cut, Ctrl+C for copy, Ctrl+V for pasting, mouse selection and cursor navigation with arrows.
    Behavior can be modified by passing flags, see the enums section. Flags can be combined with bitwise operations.

    Example:
    Code: AGS
    
    String input_string;
    function room_RepExec() {
      AgsImGui.NewFrame();
        // buff
      String out_string = AgsImGui.InputText("type here!",input_string, 128);
      if(out_string!=null) input_string = out_string;
    
      AgsImGui.Render();
    }
    
    
    function room_Load() {
      input_string = "Hello String!";
    }


  • AgsImGui.InputTextMultiline

    Code: ags
    static String AgsImGui.InputTextMultiline(String label, String text_buffer, int buffer_size, int width=0, int height=0, ImGuiInputTextFlags flags = 0)


    Same as InputText, but allows controlling the input size and supports multiline text.

  • AgsImGui.InputTextWithHint

    Code: ags
    static String AgsImGui.InputTextWithHint(String label, String hint, String text_buffer, int buffer_size, ImGuiInputTextFlags flags = 0)


    Same as InputText, but supports passing an input hint text.




    Widgets: Combobox commands

  • AgsImGui.BeginCombo

    Code: ags
    static bool AgsImGui.BeginCombo(String label, String preview_value, ImGuiComboFlags flags = 0);


    The BeginCombo()/EndCombo() allows to manage your contents and selection state however you want it, by creating e.g. Selectable() items.

    Example:
    Code: AGS
    
    bool option1;
    bool option2;
    if(AgsImGui.BeginCombo("My combo","click me!")){
      option1 = AgsImGui.Selectable("Option 1");
      option2 = AgsImGui.Selectable("Option 2");
      AgsImGui.EndCombo();
    }
    


  • AgsImGui.EndCombo

    Code: ags
    static void EndCombo()


    Only call EndCombo() if BeginCombo() returns true!




    Widgets: List Boxes commands

  • AgsImGui.BeginListBox

    Code: ags
    static bool AgsImGui.BeginListBox(String label, int items_count, int height_in_items = -1);


    The BeginListBox()/EndListBox() allows to manage your contents and selection state however you want it, by creating e.g. Selectable() items.

    Example:
    Code: AGS
    
    bool option1;
    bool option2;
    if(AgsImGui.BeginListBox("My ListBox",2)){
      option1 = AgsImGui.Selectable("Option 1");
      option2 = AgsImGui.Selectable("Option 2");
      AgsImGui.EndListBox();
    }
    


  • AgsImGui.EndListBox

    Code: ags
    static void AgsImGui.EndListBox()


    Only call EndListBox() if BeginListBox() returns true!




    Widgets: Trees

    TreeNode functions return true when the node is open, in which case you need to also call TreePop() when you are finished displaying the tree node contents.

  • AgsImGui.TreeNode

    Code: ags
    static bool AgsImGui.TreeNode(String label)


    Show the contents and call TreePop() at the end, if this returns true.

  • AgsImGui.TreeNodeWithID

    Code: ags
    static bool AgsImGui.TreeNodeWithID(String str_id, String text)


    Show the contents and call TreePop() at the end, if this returns true.

  • AgsImGui.TreeNodeV

    Code: ags
    static bool AgsImGui.TreeNodeV(String str_id, String text)


    Show the contents and call TreePop() at the end, if this returns true.

  • AgsImGui.TreeNodeEx

    Code: ags
    static bool AgsImGui.TreeNodeEx(String label, ImGuiTreeNodeFlags flags = 0)


    Show the contents and call TreePop() at the end, if this returns true.

  • AgsImGui.TreeNodeExWithID

    Code: ags
    static bool AgsImGui.TreeNodeExWithID(String str_id, ImGuiTreeNodeFlags flags, String text)


    Show the contents and call TreePop() at the end, if this returns true.

  • AgsImGui.TreeNodeExV

    Code: ags
    static bool AgsImGui.TreeNodeExV(String str_id, ImGuiTreeNodeFlags flags, String text)


    Show the contents and call TreePop() at the end, if this returns true.

  • AgsImGui.TreePush

    Code: ags
    static void AgsImGui.TreePush(String str_id)


    ~ Indent()+PushId(). Already called by TreeNode() when returning true, but you can call TreePush/TreePop yourself if desired.

  • AgsImGui.TreePop

    Code: ags
    static void AgsImGuiTreePop()


    ~ Unindent()+PopId().

  • AgsImGui.GetTreeNodeToLabelSpacing

    Code: ags
    static float AgsImGui.GetTreeNodeToLabelSpacing()


    Horizontal distance preceding label when using TreeNode*() or Bullet() == (g.FontSize + style.FramePadding.x*2) for a regular unframed TreeNode.

  • AgsImGui.CollapsingHeader

    Code: ags
    static bool AgsImGui.CollapsingHeader(String label, ImGuiTreeNodeFlags flags = 0)


    If returning 'true' the header is open. doesn't indent nor push on ID stack. user doesn't have to call TreePop().




    Tooltips

    Tooltip are windows following the mouse which do not take focus away.

    Remember we can only have one active tooltip at all times, and the last one called is the active one.

  • AgsImGui.BeginTooltip

    Code: ags
    static void AgsImGui.BeginTooltip()


    Begin/append a tooltip window. to create full-featured tooltip (with any kind of items). Doesn't return nothing.

  • AgsImGui.EndTooltip

    Code: ags
    static void AgsImGui.EndTooltip()


    Always call after a BeginTooltip block!

  • AgsImGui.SetTooltip

    Code: ags
    static void AgsImGui.SetTooltip(String text)


    Set a text-only tooltip, typically use with AgsImGui.IsItemHovered(). Override any previous call to SetTooltip().

    Example:
    Code: AGS
    
    AgsImGui.Button("Clicks for nothing!");
    if(AgsImGui.IsItemHovered()) AgsImGui.SetTooltip("Button is hovered!");
    
    [big]
    [/big]

    Popups and Modals

  • AgsImGui.OpenPopup

    Code: ags
    static void AgsImGui.OpenPopup(String str_id)


    call to mark popup as open (don't call every frame!). Popups are closed when user click outside, or if CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.

    Popup identifiers are relative to the current ID-stack.

    Example:
    Code: AGS
    
    // If the button is in the Window, this code goes inside the window.
    if(AgsImGui.BeginPopup("my_popup")){
      AgsImGui.Text("This is a popup");
      AgsImGui.EndPopup(); 
    }
    if(AgsImGui.Button("open popup"))
    {
      AgsImGui.OpenPopup("my_popup");
    }
    


  • AgsImGui.BeginPopup

    Code: ags
    static bool AgsImGui.BeginPopup(String str_id, ImGuiWindowFlags flags = 0)


    Return true if the popup is open, and you can start outputting to it.
    Only call EndPopup() if BeginPopup() returns true!

    Generally you will want to run this on every frame, and it will return true once the popup has been made open,
    and return false again once it's closed.

  • AgsImGui.BeginPopupModal

    Code: ags
    static bool AgsImGui.BeginPopupModal(String name, bool has_close_button = 0, ImGuiWindowFlags flags = 0)


    Modal dialog, a regular window with title bar, block interactions behind the modal window, and you can't close the
    modal window by clicking outside.

  • AgsImGui.EndPopup

    Code: ags
    static void AgsImGui.EndPopup()


    Only call EndPopup() if BeginPopupXXX() returns true!

  • AgsImGui.IsPopupOpen

    Code: ags
    static bool AgsImGui.IsPopupOpen(String str_id)


    Return true if the popup is open at the current begin-ed level of the popup stack.

  • AgsImGui.CloseCurrentPopup

    Code: ags
    static void AgsImGui.CloseCurrentPopup()


    Close the popup we have begin-ed into. clicking on a MenuItem or Selectable automatically close the current popup.




    Tab Bars, Tabs

  • AgsImGui.BeginTabBar

    Code: ags
    static bool AgsImGui.BeginTabBar(String str_id, ImGuiTabBarFlags flags = 0)


    create and append into a TabBar

  • AgsImGui.EndTabBar

    Code: ags
    static void AgsImGui.EndTabBar()


    only call EndTabBar() if BeginTabBar() returns true!

  • AgsImGui.BeginTabItem

    Code: ags
    static bool AgsImGui.BeginTabItem(String label, bool has_close_button = 0, ImGuiTabItemFlags flags = 0)


    create a Tab. Returns true if the Tab is selected.

  • AgsImGui.EndTabItem

    Code: ags
    static void AgsImGui.EndTabItem()


    only call EndTabItem() if BeginTabItem() returns true!

  • AgsImGui.SetTabItemClosed

    Code: ags
    static void AgsImGui.SetTabItemClosed(String tab_or_docked_window_label)


    notify TabBar or Docking system of a closed tab/window ahead (useful to reduce visual flicker on reorderable tab bars). For tab-bar: call after BeginTabBar() and before Tab submissions. Otherwise call with a window name.




    Menus

  • AgsImGui.BeginMenuBar

    Code: ags
    static bool AgsImGui.BeginMenuBar()


    Append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window).

  • AgsImGui.EndMenuBar

    Code: ags
    static void AgsImGui.EndMenuBar()


    Only call EndMenuBar() if BeginMenuBar() returns true!

  • AgsImGui.BeginMainMenuBar

    Code: ags
    static bool AgsImGui.BeginMainMenuBar()


    Create and append to a full screen menu-bar.

  • AgsImGui.EndMainMenuBar

    Code: ags
    static void AgsImGui.EndMainMenuBar()


    Only call EndMainMenuBar() if BeginMainMenuBar() returns true!

  • AgsImGui.BeginMenu

    Code: ags
    static bool AgsImGui.BeginMenu(String label, bool enabled = true)


    Create a sub-menu entry. Only call EndMenu() if this returns true!

  • AgsImGui.EndMenu

    Code: ags
    static void AgsImGui.EndMenu()


    Only call EndMenu() if BeginMenu() returns true!

  • AgsImGui.MenuItem

    Code: ags
    static bool AgsImGui.MenuItem(String label, String shortcut, bool selected = false, bool enabled = true)


    return true when activated. shortcuts are displayed for convenience but not processed by ImGui at the moment!




    General Helpers

  • AgsImGuiHelper.SetClipboarText

    Code: ags
    static void AgsImGuiHelper.SetClipboarText(String text)


  • AgsImGuiHelper.GetClipboarText

    Code: ags
    static String AgsImGuiHelper.GetClipboarText()






    ImVec2

    This is a format used to describe either x,y points or zero origin rectangles and other things that can be described as vectors.
    It's used more in the attributes of a ImGuiStyle object.


    ImVec4

    This is a format used to describe either x,y,w,z points or any origin rectangles and other things that can be described as vectors with 4 coordinates.
    It's the preferable format for ImGui Colors, and used a lot for this! It's used more in the attributes of a ImGuiStyle object.


    ImGuiStyle

    A big object used to describe the theme in ImGui.


    Enums

    Code: ags
    ImGuiCond



    • eImGuiCond_Always, used when condition should always trigger, is usually the default when the AgImGui API requires
      conditions.
    • eImGuiCond_Once, Set the variable once per runtime session (only the first call with succeed).
    • eImGuiCond_FirstUseEver, Set the variable if the object/window has no persistently saved data (no entry in .ini file).
    • eImGuiCond_Appearing, Set the variable if the object/window is appearing after being hidden/inactive (or the first time).
    Code: ags
    ImGuiDir



    • eImGuiDir_Left, left direction.
    • eImGuiDir_Right, right direction.
    • eImGuiDir_Up, up direction.
    • eImGuiDir_Down, down direction.
    Code: ags
    ImGuiBeginWindow


    obs: This is exclusive to AgsImGui implementation because in AGS we don't have pointers to primitive types.

    In any cases below, you still have to call AgsImGui.EndWindow().


    • eImGuiBeginWindow_OK, the window is open.
    • eImGuiBeginWindow_Collapsed, the window is collapsed (using the arrow at top left).
    • eImGuiBeginWindow_OK_Closed the window is closed. This value is only returned at mouse release frame, after clicking on close button!
    • eImGuiBeginWindow_Collapsed_Closed the window is both collapsed and closed, rare but theoretically possible.
    Code: ags
    ImGuiWindowFlags



    • eImGuiWindowFlags_None (= 0), Default.
    • eImGuiWindowFlags_NoTitleBar, Disable title-bar
    • eImGuiWindowFlags_NoResize, Disable user resizing with the lower-right grip
      ...
    • eImGuiWindowFlags_NoInputs, Disable inputs.
    Check the README on GitHub for the complete enum listing.

    Code: ags
    enum ImGuiInputTextFlags


    The enums of this type can be combined with bitwise operators.

    Check the README on GitHub for the complete enum listing.

    Code: ags
    enum ImGuiCol_


    The enums of this type can be combined with bitwise operators.

    Used for selecting a specific color from the ImGuiStyle.Colors[] property. An example usage would be:

    Code: AGS
    
    ImGuiStyle* style = ImGui.GetStyle();
    style.Colors[eImGuiCol_WindowBg] = ImVec4.Create(1.0, 0.0, 0.0, 0.0);
    ImGui.SetStyle(style);
    


    This would use the enum eImGuiCol_WindowBg to access the desired color you want to modify,
    which in the example sets to a red color. Check the README on GitHub for the complete enum listing.

[close]
FAQ

How do I make mouse wheel work?

First, make sure you have mouse wheel support enabled in Global Settings.

Then, in some script, add the code below. Note: If the script is a Room script, it will only work in that room.
Code: AGS Script

void on_mouse_click (int btn){
  if(btn == eMouseWheelNorth){
    AgsImGui.DoMouseWheel(eImGuiDir_Down);
  } else if(btn == eMouseWheelSouth){
    AgsImGui.DoMouseWheel(eImGuiDir_Up);    
  }  
}

For now, mouse wheel events are not exposed in the AGS Plugin API, so you have to do it through script. In the future, if this changes, it will be integrated in the plugin.


License and Author

AgsImGui is made by eri0o provided with MIT LICENSE.

Using ocornut Dear ImGui, and also using software renderer with modifications from LAK132, and using other changes to ensure AGS compatibility, provided with MIT License.

Additionally using David Cappelo clip library for providing cross platform clipboard integration, also provided with MIT License.
#88
Has anyone ever written a Immediate GUI module for AGS?

From Wikipedia

Quote
Immediate mode GUI in computer graphics is a GUI implemented using an immediate mode pattern, where the event processing is directly controlled by the application.

There are two major pattern of API design in graphics libraries - retained mode and immediate mode.[1] In the retained mode, the rendering primitives are managed by the GUI system/library, often hidden from the application code. In the immediate mode, the application code is holding on its own rendering primitives.

I have thought about it, and while I realized it would be theoretically possible using Overlays and reading mouse and keyboard status, I was too overwhelmed and decided to just at first write a plug-in.

Now, this, as always, isn't really in a working state. I mean, it runs and draws things on screen, but it doesn't handle input yet.

I present agsimgui. The GitHub repository is here.



This GUI is more oriented to building in game development tools for the person testing systems and stuff in game rather than actually being used by the final user. Because of my limitations what eventually will be the API, will be a smaller subset of actual Dear ImGui capabilities.
#89
This is just to start a topic to see if someone has looked into testing with Adventure Game Studio Scripts before.

I have recently been experimenting with the idea of a test game.

Here is an example output on an Continuous Integration system called AppVeyor: ci.appveyor.com/project/ericoporto/ags-test-game/builds/29713758.

And here is the code I used, I added a README explaining the module built in the game project here: github.com/ericoporto/ags_test_game

The idea is to create a game to help testing AGS meanwhile writing standard output with the Test Anything Protocol Producer Script Module. This idea is experimental.

Ah, searching the forums for the word Testing or Tests produce an infinite number of results that aren't exactly this kinda of Testing, so I am going to say some keywords here like Unit Testing and Test Driven Development just so if this topic falls into forgotten someone can dig it by throwing related keywords.
#90
Advanced Technical Forum / Tiled and AGS
Tue 03/12/2019 22:13:35
Hey, just curious if someone ever looked into this.

Tiled is a map editor that is free and open source, and available here: thorbjorn.itch.io/tiled

The only hack I know is using a tileset like this.
mask_blocks.tsx
Code: xml

<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.2" tiledversion="1.2.1" name="mask_blocks" tilewidth="16" tileheight="16" tilecount="5" columns="5">
 <image source="mask_tileset.png" width="80" height="16"/>
</tileset>


mask_tileset.png


And then loading it on Tiled and using it to draw the walkable areas in your map (click to expand image)



And later exporting the layers as images and importing the masks drawn there (in the example, walkable areas)

Tiled though has some other useful stuff, like drawing polygons and creating points that can have special means - these are particularly useful.

Anyway curious if someone has ever looked into using Tiled and AGS and maybe even done some stuff with it.
#91


Recently I've been inclined to learn the math that enables doing 3D stuff. In the long past I guess people have looked into it (I found this thread from 2005!), and more recently a very impressive demo was put here...

So I decided to look into what could be done in similar manner, as a module, to help with such endeavors. From a different unreleased, unfinished, project, I cobbled up some code here in this GitHub repository. The README is barely a sketch, I will try to figure out the concepts and put them there as I learn.

You can get math3d.scm here. You can experiment with it.

I could really use some help understanding this stuff, if you have better ideas for the API and things to have, fire up. If you find a bug, please PR on GitHub if you know how to fix.

I intend to provide a triplet vector (which may have a fourth element), quaternions, matrix and helpful transformation functions.

Also I like discussing this and will try to update this thread as I learn. The gif above links to a video showing me playing with a virtual camera.
#92
Edit: Figured out a way to split a string on needles or tokens...

Code: ags

int CountToken(this String*, String token){
  String sub = this.Copy();
  int count = 0;
  
  while(sub.Length > 0){ 
    if(sub.IndexOf(token)==-1){
      return count;  
    }
  
    sub = sub.Substring(sub.IndexOf(token)+token.Length, sub.Length);
    count++;
  }  
  return count;  
}

String[] Split(this String*, String token){
  int count = this.CountToken(token);
  
  if(count<=0){
    String r[] = new String[1];
    r[0] = null;
    return r;  
  }
  
  String r[] = new String[count+2];
  String sub = this.Copy();
  
  int i = 0;
  int cur = 0;
  
  while(i < count){     
    cur = sub.IndexOf(token);
    if(cur==-1) cur=sub.Length;

    r[i] = sub.Substring(0, cur);
    
    sub = sub.Substring(sub.IndexOf(token)+token.Length, sub.Length);
     
    i++;
  }
  r[i] = sub.Substring(0, sub.Length);
  i++;
  r[i] = null;
  return  r;
}


So what I would like is for "22, 33, 44, 55", a split on ", ", would return four strings.


  • "22"
  • "33"
  • "44"
  • "55"

So these strings wouldn't have the token strings in it. Lastly it returns a null string, so you can check you got the latest string by testing for null.

Edit:
Figured, fixed! It's working!

Here's a simple test of it in a room:

Code: ags
// room script file
function room_AfterFadeIn() {
  String mstr = "1, 232, 9, 55, 744";
  
  String b[] = mstr.Split(", ");
  int i=0;
  while(b[i]!=null){
    Display("%s[[%s", mstr, b[i]);
    i++;  
  }
  Display("the end");
}
#93


link: youtube.com/watch?v=MBRoCdtZOYg
slides: naming_is_hard_lets_do_better__kate_gregory__cppcon_2019.pdf

Hey, I really liked this talk, even though it's c++ related, there is a lot of useful things related to naming things in code that I think can be applied to other vaguely similar languages (like AGS Script, with gotchas there).

I am reaaaally terrible at naming things and I freeze sometimes trying to figure out the right name for variables to provide the correct semantic meaning, so for me it was interesting to watch.
#94
rellax version 0.4.0

Get Latest Release rellax.scm | GitHub Repo | Demo Windows | Demo Linux | Download project .zip

Rellax while the camera tracks with cool parallax



This module uses the camera and Viewport API from Adventure Game Studio 3.5.0.

Demo game uses keyboard arrows control, up arrow jumps. WASD should also work.


Usage
Before starting, you must create the following Custom Properties in AGS Editor, for usage with Objects.
Just click on Properties [...] and on the Edit Custom Properties screen, click on Edit Schema ... button, and add the two properties below:

PxPos:
  • Name: PxPos
  • Description: Object's horizontal parallax
  • Type: Number
  • Default Value: 0

PyPos:
  • Name: PyPos
  • Description: Object's vertical parallax
  • Type: Number
  • Default Value: 0

The number defined on Px or Py will be divided by 100 and used to increase the scrolling.
An object with Px and Py 0 is scrolled normally, an object with Px and Py 100 will be fixed on the screen despite camera movement.
Objects with negative Px and Py are usually at the front, and positive values are usually at the back.


Script API

static attribute Character* TargetCharacter
The character being tracked by the Game.Camera.

static attribute bool EnableParallax
Gets/sets whether Parallax is on or off.

static attribute bool EnableSmoothCam
Gets/sets whether Smooth Camera tracking is on or off.

static attribute bool AutomaticallySetupOnRoomLoad
Gets/sets whether to automatically setup on room load. It defaults to yes (true).
Leave as this unless you really need it.

static void SetupRoomManually()
You should not call this, unless AutomaticallySetupOnRoomLoad is false.
Then call this at the end of your room_Load, after you are done setting things up.

static attribute bool AdjustCameraOnRoomLoad
Gets/sets whether to instantly adjust camera to target on room before fade in, when Smooth Camera is on. Default is true.

static attribute RellaxTweenEasingType EasingType
gets/sets the camera tween type to use when the character is stopped.

static attribute float TweenDuration
gets/sets the camera tween duration once the character is stopped.

static attribute int CameraOffsetX
Gets/sets the camera horizontal offset. It's applied without smoothing.

static attribute int CameraOffsetY
Gets/sets the camera vertical offset. It's applied without smoothing.

static attribute int CameraLookAheadX
Gets/sets the camera horizontal lookahead offset. This is an additional offset that is added in the direction the target character is facing (only 4 direction support now).

static attribute int CameraLookAheadY
Gets/sets the camera vertical lookahead offset. This is an additional offset that is added in the direction the target character is facing (only 4 direction support now).

static attribute int CameraLerpFactorX
Gets/sets the factor the camera should use when interpolating in the X axis.

static attribute int CameraLerpFactorY
Gets/sets the factor the camera should use when interpolating in the Y axis.

static attribute int CameraWindowWidth
Gets/sets the camera window width that is centered on the target lookahead point, when the target is outside of the window, the camera moves to keep it inside.

static attribute int CameraWindowHeight
Gets/sets the camera window height that is centered on the target lookahead point, when the target is outside of the window, the camera moves to keep it inside.


License
This module is created by eri0o is provided with MIT License, see LICENSE for more details.
The code on this module is based on the code of Smooth Scrolling + Parallax Module from Alasdair Beckett, which bases on code from Steve McCrea.
It uses easing code based on Edmundo Ruiz and Robert Penner's, works, which are MIT and BSD licensed, respectively (and included in the module script).
The demo game uses CC0 (Public Domain) art provided by jetrel.
#95
agsbox2d  version 0.5.0

Get Latest Release agsbox2d.dll | libagsbox2d.so | libagsbox2d.dylib | GitHub Repo | Demo Windows | Demo Linux



AgsBox2D is a physics plugin for Adventure Game Studio that gives access to the Box2D library created by Erin Catto.
Because I never used Box2D directly before, I tried to make the API similar to Love physics module.



AgsBox2D is still in early development.

In development warning

AgsBox2D is in development. Still, if you want to experiment with it and report your findings, this post should prove itself useful and I appreciate any help in making this plugin work well with AGS.

Usage example

Below we will do a very simple example that creates a ground, and adds a box and a ball. The ball is controlled by keyboard input. The game is supposed 320x200 in this example.

Code: ags
// room script file
World* world;
Overlay* ov;

struct Physical {
  Body* body;
  Shape* shape;
  Fixture* fixture;
};

Physical ground;
Physical ball;

function room_Load()
{
  if(world == null){
    AgsBox2D.SetMeter(32.0);
    world = AgsBox2D.CreateWorld(0.0, 9.8*AgsBox2D.GetMeter());

    ground.body = AgsBox2D.CreateBody(world, 160.0, 160.0, eBodyStatic);
    ground.shape = AgsBox2D.CreateRectangleShape(320.0, 40.0);
    ground.fixture = AgsBox2D.CreateFixture(ground.body, ground.shape);

    ball.body = AgsBox2D.CreateBody(world, 160.0, 40.0, eBodyDynamic);
    ball.shape = AgsBox2D.CreateCircleShape(20.0);
    ball.fixture = AgsBox2D.CreateFixture(ball.body, ball.shape, 1.0);
    ball.fixture.Restitution = 0.5;

    AgsBox2D.CreateFixture(AgsBox2D.CreateBody(world, 80.0, 60.0, eBodyDynamic),
                           AgsBox2D.CreateRectangleShape(30.0, 20.0), 5.0);
  }
}

function room_RepExec()
{
  if(IsKeyPressed(eKeyLeftArrow)) ball.body.ApplyForce(-500.0, 0.0);
  if(IsKeyPressed(eKeyRightArrow)) ball.body.ApplyForce(500.0, 0.0);
  if(IsKeyPressed(eKeyUpArrow) && ball.body.IsTouching(ground.body)){
    ball.body.ApplyForce(0.0, -6000.0);
    ball.body.SetLinearVelocity(0.0, 0.0);
  }

  if(ov!=null && ov.Valid) ov.Remove();
  ov = Overlay.CreateGraphical(0, 0, world.GetDebugSprite(), true);

  world.Step(1.0/IntToFloat(GetGameSpeed()), 8, 3);
}


There's a breakdown of this code here.

Script API

Spoiler
AgsBox2D


  • void AgsBox2D.SetMeter(float meter)
    Sets how many pixels equals to a meter. Default is 32 pixels per meter.

    Do this only once, before using any other functions, it doesn't apply retroactively

    You want the size of your moving objects roughly between 0.1 and 10 meters.

    For the default 32px this enables objects between 3 and 320 pixels, so this usually needs to scale along with your game resolution and character size.

    Internally, Box2D uses Meter, Kilograms and Seconds as it's main units.
  • float AgsBox2D.GetMeter()
    Get's previously passed meter in pixels.

  • World* AgsBox2D.CreateWorld(float gravityX, float gravityY)
    Creates a World object, this should be done before creating bodies.

    A positive gravityY is directed to ground, and a negative gravityY is directed upwards.

    Similarly, a positive gravityX is directed to right and a negative gravityX is directed to left.

  • Body* AgsBox2D.CreateBody(World* world,  float x, float y, BodyType bodytype)
    Creates a body object in the world, at specified x and y positions.
    These positions correspond to the center of the body.

    The bodytype can be any of the types below:

    - eBodyStatic : An object that does not move under simulation, usually the
    ground in a platformer is a static body. It doesn't collide with other static
    or kinematic bodies. A static body has zero velocity.

    - eBodyDynamic : A fully simulated body, can collide with all body type,
    this body moves according to forces. It always has finite non-zero mass.

    - eBodyKinematic : A kinematic body moves according it's velocity, it doesn't
    move according to forces. A Kinematic body behaves as if it has infinite mass.
    It doesn't collide with other Kinematic bodies or with static bodies.

    Any bodytype can be moved by user input, but you have to specific code the behavior in AGS.

    You don't need to keep the returned pointer if you aren't going to need to access this body anymore, since the world will hold it, but you will be unable to destroy it unless the world is destroyed.

    The specifics on a body form and mass are defined by using a Shape and Fixture.

  • void AgsBox2D.DestroyBody(World* world,  Body* body)
    Removes a body from the world, and marks it with the property IsDestroyed true.

  • Shape* AgsBox2D.CreateRectangleShape(float w,  float h,  float x=0, float y=0)
    Creates a RectangleShape with Width w and Height h, and returns a Shape object.

    You can also change it's relative center which will be mapped to the body center.
    An x of 0.0 and y of 0.0, which are defaults, maps to the shape center.

  • Shape* AgsBox2D.CreateCircleShape(float radius,  float x=0, float y=0)
    Creates a Circle shape, and similar to RectangleShape, you can also translate it's center.

  • Fixture* AgsBox2D.CreateFixture(Body* body, Shape* shape, float density=0)
    Creates a Fixture, and attachs a body a shape, and specifies a density.

    You should always pass finite non-zero densities for dynamic bodies.

    You don't need to keep the pointer to the shape attached to a body through a fixture, since the body will hold a copy of the shape.
    Similarly, you also don't need to keep a pointer to the fixture, because the body will hold it too.

  • Joint* AgsBox2D.CreateDistanceJoint(Body* bodyA, Body* bodyB, float a_x, float a_y, float b_x, float b_y, bool collideConnected = 0)

    Create Distance Joint, pass anchors on bodies A and B using world coordinates. The two bodies are assumed to be in place when this joint is created.

    This joint constrains the distance between two points on two bodies to be constant. The first anchor point is connected to the first body and the second to the second body, and the points define the length of the distance joint.

  • Joint* AgsBox2D.CreateMotorJoint(Body* bodyA, Body* bodyB, float correction_factor,  bool collideConnected = 0)

    Create Motor Joint. This is a joint between two bodies which controls the relative motion between them.

    Position and rotation offsets can be specified once the MotorJoint has been created, as well as the maximum motor force and torque that will be be applied to reach the target offsets.

  • Joint* AgsBox2D.CreateMouseJoint(Body* bodyA, float x, float y)

    Create Mouse Joint between body and a target point in the world. To make it follow the mouse, the fixed point must be updated every time-step.

    The advantage of using a MouseJoint instead of just changing a body position directly is that collisions and reactions to other joints are handled by the physics engine.

  • Joint* AgsBox2D.CreatePulleyJoint(Body* bodyA, Body* bodyB, PointF* groundAnchorA, PointF* groundAnchorB, PointF* localAnchorA, PointF* localAnchorB, float ratio, bool collideConnected = 0)

    Creates a PulleyJoint to join two bodies to each other and the ground.

    The pulley joint simulates a pulley with an optional block and tackle. If the ratio parameter has a value different from one, then the simulated rope extends faster on one side than the other. In a pulley joint the total length of the simulated rope is the constant length1 + ratio * length2, which is set when the pulley joint is created.

    Pulley joints can behave unpredictably if one side is fully extended. It is recommended that the method setMaxLengths  be used to constrain the maximum lengths each side can attain.

  • void AgsBox2D.DestroyJoint(World* world,  Joint* body)

    Removes a joint from the world, it should no longer return true to isValid.



PointF


  • PointF* PointF.Create(float x, float y)
    Creates a PointF object with x and y values.

  • float PointF.X
    The X coordinate property of a PointF.

  • float PointF.Y
    The Y coordinate property of a PointF.

  • float PointF.Length()
    Returns distance from point (X,Y) coordinates to origin (0,0).

  • float PointF.SquaredLength()
    Returns  squared distance from point (X,Y) coordinates to origin (0,0). Slightly faster than Length.

  • PointF* PointF.Add(PointF* pointF)
    Returns a new point with the sum of this with pointF.

  • PointF* PointF.Sub(PointF* pointF)
    Returns a new point with the subtraction of pointF from this.

  • PointF* PointF.Scale(float scale)
    Returns a new point that is a copy of this point multiplied by a scalar.

  • PointF* PointF.Rotate(float angle, float pivot_x = 0, float pivot_y = 0)
    Returns a new point with this point treated as a vector to a pivot point, rotated by an angle in radians. If you don't specify, pivot is origin (0,0).

  • Point* PointF.ToPoint()
    Rounds this point as integer and returns a standard AGS Point object.


Body


  • int Body.X
    The X position property of a body as integer.

    Avoid setting this property directly. Bodies coordinates are actually float values in the simulation, this is provided as convenience.

  • int Body.Y
    The Y position property of a body as integer.

    Avoid setting this property directly. Bodies coordinates are actually float values in the simulation, this is provided as convenience.

  • float Body.fX
    The X position property of a body as float.

  • float Body.fY
    The Y position property of a body as float.

  • float Body.Angle
    The Body Angle property. AGS can't easily rotate Sprites so avoid using angles with bodies that you expect to map directly in someway to screen sprites.

  • bool Body.FixedRotation
    By default, bodies are created with FixedRotation set to true.

    A body with FixedRotation set to true does not rotate, causing it's rotational inertia and it's inverse to be set to zero.

  • bool Body.Bullet
    By default, bodies are created with Bullet set to false.

    Set bullet to true when the body has a small shape and moves really fast, this will prevent the body from having wrong collisions with thin bodies.

  • readonly bool Body.IsDestroyed
    Returns true if it's destroyed by AgsBox2D.DestroyBody().

  • float Body.LinearDamping
    The LinearDamping property of a body. Damping occurs independently from contact and is different than friction.
    Normally  the value for damping is between 0.0 and 0.1.

  • float Body.AngularDamping
    The AngularDamping property of a body, the angular drag, also happens
    independently from contact.

  • float Body.AngularVelocity
    The AngularVelocity property of a body.

  • float Body.Inertia
    Rotational Inertia, body's resistance to changes in angular velocity.

  • readonly float Body.LinearVelocityX
    Gets the X vector from the body's Linear Velocity.

  • readonly float Body.LinearVelocityY
    Gets the Y vector from the body's Linear Velocity.

  • void Body.SetLinearVelocity(float fx, float fy)
    Set's the body LinearVelocity vector.

  • void Body.ApplyForce(float fx, float fy)
    Applies a force on a body from it's center 0.0, 0.0 to the specified fx, fy
    direction.

  • void Body.ApplyAngularImpulse(float impulseIntensity)
    Applies an angular impulse on the body.

  • void Body.ApplyLinearImpulse(float intensity_x, float intensity_y)
    Applies an impulse from the body center with the specified vector.

  • void Body.ApplyTorque(float torque)
    Applies a torque on the body. Positive values are counter clockwise.

  • bool Body.IsTouching(Body* otherBody)
    Returns true when a body is in contact (being touched) by other body.
    This function only evaluates at the current time, so prefer using it for resting states.

World

The world holds all the information needed for the physics simulation.
Once a world is destroyed, the previous pointers (Bodies, Fixtures, ...) will be of no use and you will need to recreate any objects you need in the new world.


  • void World.Step(float dt, int velocityIteractions = 8, int positionIteractions = 3)
    Advances a step in the World physics simulation of dt seconds.

    Because AGS uses fixed game steps, a good value is  dt = 1.0/IntToFloat(GetGameSpeed()).

    velocityIteractions and positionIteractions relates to how Box2D simulates the world, so for information on these values I recommend looking into Box2D own documentation.

  • int World.GetDebugSprite(int camera_x = 0, int camera_y = 0)
    Returns a sprite of the size of the screen with the objects in the world drawn on it.

    A common usage is to create a GUI of the size of the screen and set the background graphic of it with the sprite this function outputs. Set this GUI transparency between 1 and 99.

    You can pass a camera x and y value to scroll the camera on the world.

  • int FixtureArray*  World.BoundingBoxQuery(float lower_x, float lower_y, float upper_x, float upper_y)
    Returns array of fixtures which their bounding boxes are overlapped by the supplied box.

    A fixture bounding box is the non rotated smallest rectangle that contains it's shape, this means a rotate rectangle or a circle, a empty area is part of the bounding box.
    This is usually good enough for a first stage of a detection, but may require additional steps.

  • int RaycastResult* World.Raycast(float x0, float y0, float x1, float y1, RaycastType rc_type = 0, FixtureArray* stopping_fixtures = 0)
    Returns RaycastResult with fixtures hit by a line, along with the hit normals.
    The raycast goes through all fixtures on the line if you supply eRaycastPassthrough (this is the default).

    You can use eRaycastUntilHit for it to stop at the first fixture hit, or additionally supply an array of target fixtures so that the raycast only stops if hit any fixture on the array.

  • readonly int World.ContactCount
    How many contacts are available. Use it to know the range to access World.Contacts[].

  • readonly Contact* World.Contacts[]
    Gets the contacts in the world by index. These only contain fixtures in contact right now.


Shape



  • ShapeRectangle* Shape.AsRectangle
    If the shape is a ShapeRectangle, it returns it. Otherwise, it returns null.
    You should not hold pointers to it, and instead access it directly like myshape.AsRectangle.Width as needed.

  • ShapeCircle* Shape.AsCircle
    If the shape is a ShapeCircle, it returns it. Otherwise, it returns null.
    You should not hold pointers to it, and instead access it directly like myshape.AsCircle.Radius as needed.


Fixture
Fixtures are used when linking a shape to an object and assigning it's density.



  • float Fixture.Density
    Density is used to compute the mass of the linked body. It's preferable to use similar densities to all your fixtures, because this will improve the simulation.

  • float Fixture.Friction
    Friction is used to make objects slide along each other realistically.

    It's usually a value between 0.0 and 1.0, but can be any non-negative value.

    Box2D uses the square root of the multiplication of two contacting fixtures to calculate the contact friction. This means if one fixture has 0.0 friction, the contact will have no friction.

  • float Fixture.Restitution
    Restitution is used to make objects bounce, and is usually a value between 0.0 and 1.0. A value of 0.0 means the object won't bounce, and a value of 1.0 means the object velocity will be exactly reflected.

  • readonly Body* Fixture.Body
    Returns Body if it's defined for this fixture, otherwise null.

  • int Fixture.GroupIndex
    Group the fixture belongs to, from -32768 to 32767. Fixtures with the same group will always collide if group is positive or never collide if it's negative.
    Zero means no group, and is default.

  • int Fixture.CategoryBits
    Category of this fixture, from 16 possible categories encoded as 16-bit integer (1, 2, 4, 8, ... 32768). 65535 means all categories.

  • int Fixture.MaskBits
    Mask of this fixture, encoded as 16-bit integer. Categories selected will collide with this fixture (ex: 5, means category 1 and 4 will collide). Default is 65535 - collide with all categories.

  • bool Fixture.TestPoint(float x, float y)`
    Returns true if a point is inside the shape of the fixture.

  • bool Fixture.IsSensor
    Whether this fixture is a sensor. Sensors do not cause collision responses, but generate begin-contact and end-contact events.



Joint
Joints are used to link a body to another to create relative movements.



  • JointDistance* Joint.AsDistance
    If this joint is a distance joint, returns the JointDistance interface; otherwise null.

  • JointMotor* Joint.AsMotor
    If this joint is a motor joint, returns the JointMotor interface; otherwise null.

  • JointMouse* Joint.AsMouse
    If this joint is a mouse joint, returns the JointMouse interface; otherwise null.

  • JointPulley* Joint.AsPulley
    If this joint is a pulley joint, returns the JointPulley interface; otherwise null.

  • bool Joint.IsValid
    If this joint is valid, returns true.

  • bool Joint.IsActive
    If this joint is active, returns true.

  • Body* Joint.BodyA
    Returns Body A if it's defined, otherwise null, for this joint.

  • Body* Joint.BodyB
    Returns Body B if it's defined, otherwise null, for this joint.

  • JointType Joint.Type
    Returns this joint type.


JointDistance



  • float JointDistance.Length
    The equilibrium distance between the two Bodies.

  • float JointDistance.DampingRatio
    The damping ratio, typically between 0 and 1. At 1, the damping is critical.

  • float JointDistance.Frequency
    The frequency of a harmonic oscillator. Should be smaller than half the frame rate.



JointMotor



  • void JointMotor.SetLinearOffset(float fx, float fy)
    Sets the target linear offset between the two bodies the joint is attached to.

  • float JointMotor.LinearOffsetX
    The target linear offset X axis between the two bodies the joint is attached to.

  • float JointMotor.LinearOffsetY
    The target linear offset Y axis between the two bodies the joint is attached to.

  • float JointMotor.AngularOffset
    The target angular offset between the two bodies the joint is attached to.

  • float JointMotor.MaxForce
    The Maximum Force applied to reach target position.

  • float JointMotor.MaxTorque
    The Maximum Torque applied to reach target rotation.



JointMouse



  • void JointMouse.SetTarget(float fx, float fy)
    Sets the target point.

  • float JointMouse.TargetX
    The target point X axis.

  • float JointMouse.TargetY
    The target point Y axis.

  • float JointMouse.DampingRatio
    The damping ratio, typically between 0 and 1. At 1, the damping is critical.

  • float JointMouse.Frequency
    The frequency of a harmonic oscillator. Should be smaller than half the frame rate.

  • float JointMouse.MaxForce
    The Maximum Force applied to reach target position.



JointPulley



  • float JointPulley.LengthA
    The current length of the segment attached to the first body.

  • float JointPulley.LengthB
    The current length of the segment attached to the second body.

  • float JointPulley.Ratio
    The pulley ratio.



Contact
Contact are returned to inform a contact is happening, between two fixtures. They also include additional useful information.



  • readonly bool Contact.IsValid
    Whether the Contact is still valid.

  • readonly PointF* Contact.Normal
    The normal vector between two shapes that are in contact.

  • readonly PointF* Contact.Positions[]
    The contact points of the two colliding fixture, use PositionsCount to find out how many. Index starts at 0.

  • readonly int Contact.PositionsCount
    How many position of contact are available.

  • readonly Fixture* Contact.FixtureA
    One of the Fixtures that hold the shapes in contact.

  • readonly Fixture* Contact.FixtureB
    The other of the Fixtures that hold the shapes in contact.

  • bool Contact.Enabled
    Whether the contact is enabled.

  • float Contact.Restitution
    The restitution between two shapes that are in contact.

  • float Contact.Friction
    The friction between two shapes that are in contact.

[close]

Download AgsBox2D

Spoiler
This plugin is available as agsbox2d.dll under assets, in the latest release, for usage with Windows and the AGS Editor. You can also find it there built for Linux as libagsbox2d.so and for MacOS as libagsbox2d.dylib.
[close]

Building agsbox2d

Spoiler

AgsBox2D both Makefile and the VS Solution file, expects to find Adventure Game Studio source code in a folder ../ags/. After you do this, you need to clone this source code.

Code: ags
  ~/git/ags/
  ~/git/agsbox2d/

Navigate then to the directory where you cloned this repository.

On Windows, you can load the solution on the root of it's directory and load it on Visual Studio. It should work with VS 2015, 2017 and 2019. You will need v140 tools (VS should promptly warn you to install if you don't have it).
The dll provided by Release Win32 builds is the one you should build to use with an AGS Game at the time of this writing.

On Linux and MacOS, navigate to agsbox2d/ inside the directory and type make.
[close]

License and Author

AgsBox2D is made by eri0o provided with Z-Lib LICENSE.

Box2D itself is made by Erin Catto and is provided with a Z-Lib LICENSE too.
#96
Controlz  0.4.0

controlz.scm | controlz_demo_windows.zip | GitHub repo

Move your character with keyboard or joystick controlz for Adventure Game Studio.


This code was originally made by Dualnames for Strangeland and I eri0o got his permission to open source and wrapped in this function to be easier for consumption.

Usage example

Call it on repeatedly execute, in your Global Script or in the script that you deal with player input, passing a character and some way to check for directional keys, once for each direction (down, left, right, up).

In the example below, WASD will be used to control the player character, and arrow keys will be used to control a second character named cEgo2.

Code: ags
// called on every game cycle, except when the game is blocked
function repeatedly_execute() 
{
  Controlz(player, 
    IsKeyPressed(eKeyDownArrow),
    IsKeyPressed(eKeyLeftArrow), 
    IsKeyPressed(eKeyRightArrow),
    IsKeyPressed(eKeyUpArrow));

  Controlz(cEgo2, 
    IsKeyPressed(eKeyS),  IsKeyPressed(eKeyA), 
    IsKeyPressed(eKeyD),  IsKeyPressed(eKeyW));
}

  • Since 0.3.0, solid characters collide with solid characters.
  • Since 0.4.0, a character will keep walking in place if a button is pressed.

script API

Controlz only has a single function

Controlz(Character* c, bool down, bool left, bool right, bool up)

Call it on your repeatedly execute or repeatedly execute always, passing a character and which keys are pressed at that time. If you need to control more characters, just call Controlz again, passing the new character and the buttons that are mapped to move it.

You can check for multiple keys or inputs too.

Code: ags
function repeatedly_execute() 
{
  Controlz(player, 
    IsKeyPressed(eKeyDownArrow) || IsKeyPressed(eKeyS),
    IsKeyPressed(eKeyLeftArrow) || IsKeyPressed(eKeyA), 
    IsKeyPressed(eKeyRightArrow) || IsKeyPressed(eKeyD),
    IsKeyPressed(eKeyUpArrow) || IsKeyPressed(eKeyW));
}

License

This code is licensed with MIT LICENSE.
#97
I did a plugin that gives sqlite capabilities for ags. GitHub repo is here and already includes the dll and so for download under Releases->Assets.

I wonder if anyone has any suggestions regarding reading the resulting SQL requests when searching the database, right now it will return pure text with the results.

Probably no one will answer this right now, but someday in the future when someone browses around for sqlite and AGS I will get an answer. :)

You can create a table using

Code: ags

AgsSQLite* db = AgsSQLite.Open("test.db");
  
  String sql = "";
  
  sql = sql.Append("CREATE TABLE CHARACTERS(");
  sql = sql.Append( "ID INT PRIMARY KEY     NOT NULL,");
  sql = sql.Append("NAME           TEXT    NOT NULL,");
  sql = sql.Append("ROOM            INT     NOT NULL,");
  sql = sql.Append("X               INT     NOT NULL,");
  sql = sql.Append("Y               INT     NOT NULL);");
  
  SQLiteQueryStatus rc = db.ExecuteQuery(sql);
    
  Display(db.GetQueryStatusText());  


Apparently I need to conceive some sort of table object so that the result of a query is easier to traverse. I imagine the simplest object would be something like:

Code: ags

managed struct Table {
  import attribute String Cell[];
  int RowCount;
  int ColumnCount;
};


And it would be accessible with something like:

Code: ags

Table* result = db.GetQueryResult();
Display(result.Cell[x+y*result.RowCount])


Still need to think a little more.
#98
Hey guys,

I need help with people to test this and give suggestions, even for naming things.

AGS Pathfinder Plugin version 0.1.0

Get the plugin: agspathfinder.dll, libagspathfinder.solibagspathfinder.dylib 
GitHub Repo | Demo Windows | Demo Linux



This plugin has copy of the actual pathfinder used by Adventure Game Studio, and exposes an interface to interact directly with it.

It's in experimental state because I believe that once the API for it is figured out, it's facilities would be provided by AGS Engine itself and then this Plugin would not make sense anymore.

AGS Pathfinder usage

Pathfinding is provided in three steps.

1. Set the map, Pathfinder will generate the nodes. Don't do this every frame.

2. Get the path as a vector by passing an origin and a destination.
   If your node map is not too gigantic, this should be very fast.
   If start and end of the vector doesn't match your origin and destination, no
   path was found. Ideally the vector returned should be empty.

3. Consume the returned vector and do something with it.

Code: ags

// 1 Pass sprite 1 to be used as map
AgsPathfinder.SetGridFromSprite(1);

// 2 Calculates the path and returns a vector containing all the nodes
PathNodeArray* pathNodeArr = AgsPathfinder.GetPathFromTo(origin_X, origin_Y, destination_X, destination_Y);

// 3 Consume your path node by node
while(!pathNodeArr.Empty()){
  PathNode* pathNode = pathNodeArr.Pop();
  // do something with pathNode.X and pathNode.Y
}


AGS Pathfinder Script API


void AgsPathfinder.SetGridFromSprite(int sprite)

Pass a sprite to be used as the walkable map. Real black (000000) identifies walls.

PathNodeArray* GetPathFromTo(int origin_x, int origin_y, int destination_x, int destination_y)

Get a PathNodeArray containing each Node of the path for the provided origin and destination.
#99
I recently built a very experimental plugin to test some ideas, the AGS Fast Wave Function Collapse Plugin.

First some images to help explain the possibilities.





The plugin right now has only a simple interface like this:

Code: ags
bool AgsFastWFC.Overlapping(
                  int destination, int sprite,
                  int seed,
                  bool periodic_input, bool periodic_output,
                  int N=3, int ground=0)



  • int destination The Graphic property of a Dynamic Sprite you have created to draw the result.
  • int sprite The source sprite you want to feed FastWFC Overlapping algorithm.
  • int seed An integer number, will provide the randomness of the output.
  • bool periodic_input Should be true if the input is periodic.
  • bool periodic_output Should be true if the desired output is periodic.
  • int N A NxN pattern of pixels in the output should occur at least once in the input. Default is 3x3 pixels.
  • int ground Default is 0.

The algorithm can in some cases fail, depending on the configurations, this is why it returns either a true, for success, or false, for failure.

A GitHub Repository is up to share the code and I added more information there too. You can also download the code as a zip. If you want to try the demo game, you can also download it for windows or linux (64-bit only).

I did only the ags parts and picked the interesting algorithm from a very interesting implementation I found online, which is better explained in the GitHub repo. It's reasonably fast as you can notice when interacting with the demo.
#100
ItemCount gives me the total items in a ListBox, RowCount gives me total number of rows in a ListBox. But ListBoxes can be scrolled. Is there a way to get the number of items actually shown in a ListBox ?
SMF spam blocked by CleanTalk