Show Posts

You can view here all posts made by this member. Note that you can only see posts made in areas to which you currently have access.

Topics - Khris

Pages: [1] 2 3
Advanced Technical Forum / [Tool] WFN creator (outlines!)
« on: 06 Nov 2018, 12:52 »
Not sure where to put this, so I'll put it here.

Have you created a game with a relatively high resolution and are unsatisfied with the puny black outline AGS draws around your letters?
Look no further. Just go to and create your own set of bitmap font + outline font, based on a TTF or OTF.

It's quite straightforward. Just select a font file and check the preview. Then adjust the values, best from top to bottom. The size is in pixels, but that's "font pixels", not necessarily pixel pixels. Check the x1 zoom preview to see the font at its actual size. Alpha threshold needs to be lowered if the thin lines in your letters are broken up, just experiment with the setting until it looks good. The final three settings apply to the outline.

I recommend using the short preview while playing with the settings. Then switch to the full letter sets to check for issues.
When you're satisfied, just click the download button beneath either font to grab them.

To use a font in AGS, open your game, then add a font in the Project tree (right-click the Font node). After AGS has created the font file, overwrite it with the downloaded one (renaming it in the process, obviously). If AGS created a ttf, just use its number but keep the *.wfn ending, then delete the .ttf with the same number.
To set up the outline font, import both fonts as just described, then set the base font's OutlineType to "UseOutlineFont" and enter the other font's number.
You'll probably also want to make sure "Fonts designed for high resolution" in General Settings / Text output is active.

DISCLAIMER: the usual WFN limitations apply: no anti-aliasing, no special characters unless you add them to the font and use substitutes in your strings. If in doubt, don't use this. It's a beta version anyway; if you encounter any errors or issues, please contact me and state what you did, which browser you're using and whether are any errors in the browser's console (press F12 to view it).

In-game preview:
Spoiler: ShowHide

Comic Sans with heavy outline

PS: there's no pixel-perfect editing. While you can do that with Radiant's FontEdit, you'd have to replicate the edit for the outline font. Thus I might add the possibility to generate the outline based on an existing WFN font at some time in the future.

General Discussion / Pro- or Anti-Antivirus?
« on: 07 Apr 2017, 10:08 »
I'm still running Win 7 on my main machine, and afaik, using AV software is recommended in that case. You don't need it for Win 10 though. (I read this in an article by security experts complaining about various AV programs messing up Chrome's internal security, but I'm too lazy to go looking for it now.)

The best defense against viruses is between your ears anyway  :-D


To use the module, put a button on a GUI, then create a new chat instance:
Code: [Select]
  globalHandleInt = Chat.Create(btnChat, eFontWestwood [, bgSprite, width, height]);bgSprite is a sprite slot for the background, it gets auto-tiled if it's smaller than the button. You can also state width and height, in that case the button is resized to those dimensions.

Optionally add sound:
Code: [Select]
  Chat.SetSound(globalHandleInt, aBlip);Pass null to disable sound.

To add a message, just call:
Code: [Select]
  Chat.Add(globalHandleInt, "The message", "sender" [, hour, minute]);If you put an asterisk at the start of the sender, like "*Alice", the message appears on the right. You can optionally state a timestamp, or omit for user's local time.

Adding a message makes the chat scroll down automatically. You can also scroll using the mousewheel, while hovering over the chat.

The chat is automatically drawn and scrolled if the GUI and button are visible.

It's also possible to prepare multiple messages, just call Chat.Prepare() instead. Show them one by one using Chat.Advance(globalHandleInt); or all at once using Chat.ShowAllPrepared(globalHandleInt);.
(Calling .Add() will simply call .Prepare(), then .Advance(), so make sure you have advanced over all prepared messages before using .Add(), or you will get unexpected results ;) )

It is recommended to call Chat.CleanUp() upon quitting the game, this will delete all the DynamicSprites, avoiding a warnings log in the game folder.

Given that most people will probably want to customize the look of the chat, I've moved the function that draws a message to the very top of the module script.
It gets passed all relevant parameters:
Code: [Select]
DynamicSprite *CustomMessage(int chatWidth, Alignment align, String author, int hour, int minute, String text, FontType font) and must return a new DynamicSprite containing the message.

Preview of default look:
Spoiler: ShowHide

My example room script:

Hints & Tips / Feng Shui TV
« on: 07 Jul 2015, 11:34 »
Spoiler: ShowHide
What do I do with the key?
And any hint on how to arrange the tree ornaments and curio items?

I just found out about this more or less by accident: if you call ClaimEvent() in a situation where there wasn't a key press or mouse click, AGS will crash with the message: ClaimEvent(): no event to claim

This is pretty much a non-issue, but it means if you want to simulate a key press by calling something like on_key_press(eKeyT); and there's a ClaimEvent(), AGS will crash.
You can easily work around this of course by using an additional function, but should the engine really crash in this case? Instead of silently continuing?

(Didn't want to put this in the tracker since it's not really a bug.)

Completed Game Announcements / Floaty Rog' - Arcade Game
« on: 13 Feb 2014, 01:08 »
Well, somebody was eventually going to do it I guess, so I thought "why not me?" :-D

Floaty Rog'


Graphics based on this: click
Gameplay based on Flappy Bird.

Click the mouse to give the shuttle an upwards thrust and avoid crashing into the cups.
If you get to 20 or more points, you can submit your score to this: Highscore Table
Just enter your name, then press enter or click the rankings button.
Press Play to try again, Escape or Q to quit anytime.
Press H to view highscores in game.

General Discussion / Fraud on Indiegogo, they don't care
« on: 24 Sep 2013, 17:05 »
Take a look at this:

Some asshole tricked 286 suckers into giving him a total of $18,064, for something that cannot work (an energy generating(!) perpetual motion machine).

It was pointed out to Indiegogo that this was fraud, but they sent back a generic "we'll look into it" reply.

This module handles moving multiple inventory items to a secondary inventory window and combining them into a new item.

In order to use it:
1. Get the module here: DOWNLOAD  (DEMO VIDEO)

2. Import it into AGS by right clicking the Script node in the Project tree, selecting "Import script..." and choosing the file.

3. Add an InvWindow to a GUI in a way that the player can click it with an active InventoryItem as mouse cursor, then in its properties, name it something sensible like "InvCrafting". The easiest way to accomplish this is by adding it to the existing inventory GUI (gInventory).
Now add a dummy character to the game (or use an existing NPC) and set its ID as the InvWindow's CharacterID. (Don't use the player character!)

4. Add two buttons to a GUI. One is supposed to try and combine the selected items, the other is supposed to clear the selection. The buttons should have proper names, choose something like "btnCraft" and "btnCraftClear" or similar.

5. Create the click handler functions for the two buttons, either by double-clicking them or via their event pane.

6. In Globalscript.asc, find the two handler functions and link them to the module so the whole thing looks similar to this:
Code: Adventure Game Studio
  1. function btnCraftClear_OnClick(GUIControl *control, MouseButton button) {
  2.   if (button == eMouseLeft) Crafting.Clear(false);   // <- false is crucial here
  3. }
  5. function btnCraft_OnClick(GUIControl *control, MouseButton button) {
  6.   if (button == eMouseLeft) Crafting.Craft();
  7. }
The latter one can of course contain additional code, for instance if the player is currently unable to craft items, the game could provide a message instead. Calling Crafting.Craft(); attempts to craft an item in the pool, regardless of other circumstances.

The following steps are all about adding commands to the game_start() function in GlobalScript.asc.

7. Some more preparation for the module is necessary; tell the module the names of the InvWindow, Craft and Clear Button:
Code: Adventure Game Studio
  1.   Crafting.SetGUIElements(InvCrafting, btnCraft, btnCraftClear);
(The buttons are disabled automatically if the crafting window is empty.)

8. Optionally, you can
a) allow the selection of multiple instances of the same item. Say building a box takes five pieces of wood, among other things. In order to not confuse the player, you should activate the following option in that case: General settings -> Inventory -> Display multiple icons for multiple items
Code: Adventure Game Studio
  1.   Crafting.SetMultipleAllowed(true);
b) change the default messages.
They are "You successfully craft a %s." and "You fail to craft anything."
Just call
Code: Adventure Game Studio
  1.   Crafting.SetMessages("success message here, include item name using %s", "failure message here");

9. Now the most important part: adding item recipes.
Start by adding the resulting InventoryItem:
Code: Adventure Game Studio
  1.   int box = Crafting.AddItem(iBox);
The function returns an index number that's used by the module to identify items. I've chosen this route because there might be multiple recipes producing the same item. (Store the returned integer in a global variable in order to be able to change or deactivate the recipe later in the game.*)
Using the value returned by the function, you can now add parts. Optionally, state the number of required parts, if it is greater than one:
Code: Adventure Game Studio
  1.   Crafting.AddPart(box, iGlue);
  2.   Crafting.AddPart(box, iWood, 5);
  3.   Crafting.AddPart(box, iHandle);
You can use that function to change the recipe later in the game, just call it with a different number as third parameter or 0 to remove the ingredient from the list of requirements altogether.

*To remove an item from the pool of possible items, use this function:
Code: Adventure Game Studio
  1.   Crafting.SetItem(box, null);
This will effectively remove the possibility to create the box from the game. You can of course also use this to change the resulting item; simply pass it to the function as second parameter.

Note: The player can move single items back to the main inventory by right-clicking them.

You can also check the requirement for an item in-game. If debug mode is activated, type !, then enter the number of the item. (The module's number, starting at 0, not AGS's inventory ID!)

ProTip: If you have lots of recipes, your game_start might get crowded. To avoid this:
Spoiler: ShowHide
Create a function above game_start containing all the crafting setup commands and call it in game_start, like this:
Code: Adventure Game Studio
  1. void prepare_crafting() {
  2.   Crafting.SetGUIElements(InvCraft, btnCraft, btnCraftClear);
  3.   Crafting.SetMultipleAllowed(true);
  5.   int box = Crafting.AddItem(iBox);
  6.   Crafting.AddPart(box, iGlue);
  7.   Crafting.AddPart(box, iWood, 5);
  8.   Crafting.AddPart(box, iHandle);
  9.   ...
  10. }
  12. function game_start() {
  13.   ...
  14.   ...
  15.   prepare_crafting();
  16. }

I'd be grateful if people tested this and provided some feedback. This is pretty much a beta-version, so use at your own risk ;-D
The module can be altered freely, credit is welcome but not required.

Also, here's a short video demonstrating the module:

Hints & Tips / Patchwork
« on: 13 Aug 2012, 16:49 »
Got relatively far in this little gem, but now I'm stuck:

Spoiler: ShowHide
I have everything except harmony, what do I do with the crystals?

Alright, I'm adding some stuff to a website and all of a sudden I get like 30 or 40 spaces in between my divs that screw up the layout:

Here's the piece of code for the main page:

Code: [Select]
                            <div id='paper'>
include 'archiv/index.php';

The first lines of the included file:
Code: [Select]
<div style='position: relative;'>
       <div style='position: absolute; left: -110px; width: 100px; padding: 0; margin: 0;'>
              <div class='archive' style='text-align: center;'>

When I look at the source in chrome, there's lots of white space after <div id='paper'>.
Naturally, I squished everything together as far as possible (although I had never to do that in the past) and what used to be
"                                                                     " ends up being "".
Still, it's treated as a line of text and Chrome even shows it as text element.

I can't get rid of it and the baffling thing is that this is by far not the only include and everywhere else, the source code lines up perfectly in Chrome's source code view.

Has anybody encountered something similar?
Btw, all my source files are encoded in UTF-8, so I'm pretty sure that conflicting encoding can be excluded as the source of the problem.

General Discussion / 20th, 21th & 22th May
« on: 20 May 2011, 17:05 »
This is going to be an awesome weekend, in case you have missed what three events are going down today and the two days after:

20th May:
The second Draw Mohammed Day.
People are killed over cartoons, so let's teach Islam a lesson about tolerance:

21th May:
At 6 PM, a global earthquake will herald Armageddon. Harold Camping didn't want to nail down the time zone, but 6 PM is pretty much when it's going to go down. So according to the blabbering, senile old fool, apparently the earthquake will start in the Pacific and then run around the globe, slowing down or getting faster according to how wide the time zones are.
This guy has gotten media coverage without end and has thousands of followers. And, this time, he's absolutely 100% positive it's going to happen.

22th May:

(click for bigger version)

Have fun :D

Hints & Tips / Night of the Testicle
« on: 12 May 2011, 18:21 »
I'm stuck pretty much at the beginning,

Spoiler: ShowHide
 I'm downstairs in the 24-7 with the huge red sphere and can't do anything.

I'm already tearing at my pubic hear over this game, please help. :=

Spent a few hours today whipping this up; I tried to test it thoroughly but as you know, some bugs always seep through the cracks. I'd welcome some extensive testing :D

This module is a replacement for the one included in the default game. Be sure to remove the old one if you try this!
I wrote it from scratch but it's set up so the game_start line will still work. You might still want to change that though, the module was primarily written to be used in RPGs together with JoyToKey so ideally it should be set to pressing mode.

Note that the module completely relies on moving and animating the character manually; this allows changing the player's view and speed during movement. The module detects walkable areas pixel-perfectly, so no need to draw them 3 pixels wide any longer.

I've tried to integrate it as far as possible, the module uses several character settings (AdjustSpeedWithScaling, AnimationDelay, DiagonalLoops, MovementSpeedX, MovementSpeedY)

Update: v0.3 - added support for frame sounds (like footsteps)

General control commands:

   KeyboardMovement.SetMode(eKMMode Mode);
Mode is either eKMModePressing or eKeyboardMovement_Tapping (to maintain compatibility with Default Game Global.asc)

   int KeyboardMovement.GetMode()
Returns the current control mode.

Enables/disables the module functionality altogether.

   bool KeyboardMovement.IsEnabled();
Returns whether the module is enabled.
In tapping mode, stops the character moving and changes to standing frame. If not in tapping mode, does nothing.

Key binding commands

   KeyboardMovement.SetKey(eKMKey key, eKeyCode k);
Assigns ASCII code k to movement key key.
Example:  KeyboardMovement.SetKey(eKMKeyLeft, eKeyLeftArrow);

   KeyboardMovement.SetRunKey(eKeyCode k);
Assigns ASCII code k to run key.
Example:  KeyboardMovement.SetRunKey(eKMModKeyLeftShift);

   int KeyboardMovement.GetKey(eKMKey key);
Returns ASCII code assigned to movement key key.

   KeyboardMovement.SetMovementKeys(eKMMovementKeys mk);
Assigns scheme mk to movement keys.
The default value is eKMMovementWASD.
Example:  KeyboardMovement.SetMovementKeys(eKMMovementArrowKeys);

Settings commands

   KeyboardMovement.SetRunSpeed(int RunSpeed);
Sets running animation delay to RunSpeed. The default value is 2. Like Character.AnimationSpeed, lower values mean faster movement.

   int KeyboardMovement.GetRunSpeed();
Returns the currently set animation delay of the player's running animation.

   KeyboardMovement.SetLoopDomination(eKMLoopDomination LoopDomination);
Changes which loop the player uses when moving diagonally when there are no diagonal loops.
LoopDomination is either eKMLoopDHorizontal, eKMLoopDVertical or eKMLoopDLast, the default value.

   KeyboardMovement.SetRunView(int RunView, int RunSpeedX, int RunSpeedY);
Allows using a second view for the run-cycle. RunSpeedX and RunSpeedY specify the movement of the character per frame i.e. are determined by the pixels of the run-cycle sprites. In theory it is possible to use the player's NormalView here, however, running by playing the walk animation faster is already implemented by default and increasing the movement values is not recommended because it leads to gliding.

   int KeyboardMovement.GetRunView();
Read back the currently set RunView. If none was set, this returns 0, an invalid view number!

   KeyboardMovement.SetDiagonalFactor(float DiagonalFactor);
Use this to slow down diagonal movement. The default value is 0.707. A value of 1.0 will add horizontal and vertical movement without correction.

   KeyboardMovement.SetEdgeAnimation(bool AnimateAtEdge);
Call this with true as parameter, and the character will continue to animate after having stopped at the edge of the walkable area. It is off by default.

   KeyboardMovement.SetBlockedTurn(bool TurnIfBlocked);
Call this with false as parameter, and the character will no longer face a direction they cannot walk in. It is on by default.

Lewis has brought to my attention that the module doesn't work with IdleViews. I fixed the code and added this command:
   KeyboardMovement.SetIdleView(int view, int delay);
Call this instead of player.SetIdleView().

Status functions

   bool KeyboardMovement.Animating();
   bool KeyboardMovement.Moving();
Returns whether the player is currently animating/moving. Use this instead of player.Animating/player.Moving since they won't work with module movement. If edge animations are on, the character might animate but not move!

Phew. After you have imported the module, make sure it is above Global.asc/h. By default, the character will move with WASD and run when you hold down the left shift key.


Critics' Lounge / Run cycle - WIP
« on: 06 Oct 2010, 01:18 »
Hey guys,

something I just whipped up:

Very WIP obviously, question is, should I start pushing pixels or is the basic animation lacking? In terms of smoothness, missing frames, etc.

Resize/rotate/warp DynamicSprites using a bilinear filter!

Available commands:

   DynamicSprite.ResizeFilter(int width, int height, optional Filter filter);
   - To keep the aspect ratio, pass 0 for either width or height.

   DynamicSprite.RotateFilter(float angle, optional Filter filter);

   DynamicSprite.ResNRotFilter(int w, int h, float angle, optional Filter filter);
   - To keep the aspect ratio, pass 0 for either width or height.

   DynamicSprite.Warp(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, optional Filter filter);
   - moves UL corner to x1;y1, UR corner to x2;y2, BL corner to x3,y3, BR corner to x4;y4

   - filter is either eFilterBilinear (the default), eFilterLinear or eFilterNearestNeighbor.

Example code:
  DynamicSprite*copy = DynamicSprite.CreateFromExistingSprite(original.Graphic);

  // resize the sprite to half its size, then rotate it clockwise by 30 degrees using the bilinear filter
  copy.ResNRotFilter(0, copy.Height/2, 30.0);

Visual examples:

   (the red boxes just illustrate the transformations, they are not drawn by the module :))

Get it here:  DOWNLOAD

Hints & Tips / Death - Episode One
« on: 22 Jun 2010, 11:27 »
I got the
Spoiler: ShowHide
letter from the emo and need to find the hideout.

I have no idea where to start.
Spoiler: ShowHide
Can I get rid of the guard somehow? The emo watched my so he's probably near the elderly home.

I can't for the life of me remember the title, thought it was something along the lines of infinite ammo but, no.

It's a shoot'em up similar to Asteroids, but you can extend your ship by adding cannons everywhere; all ships are made of squares and triangles I believe, and the bosses were huge, showering the screen with bullets and rockets, and you could take them apart shape by shape.
The graphics were green lines on black background, iirc.

I believe the bosses were generated randomly so you could literally play forever, this might be hinted at in the title, not sure though.

Does any of you know the game?

Thanks for reading!

So, as some of you may know, I'm currently in the process of trying to replicate Chrono Trigger's tile engine.
It uses four layers combined with two zPlanes and a priority mode for every tile to give the impression of having much more layers.
Basically, I have two layers of ground tiles, stored in map-sized DynamicSprites, one holding the base floor, the second one holding details or foreground objects.
Then there's two sprite layers, one for each zPlane. Those contain the character sprites, either in full or with their sprite being distributed among them tile by tile. These layers are also stored in DynamicSprites, but they are redrawn every game loop.

When it comes to drawing the screen, I loop through every tile, determine the order of the layers depending on its priority flags, then compose the final tile and draw it to a fifth DynamicSprite.
After the loop, this DynamicSprite is cropped and displayed.

Here's the relevant portions of code:

Code: [Select]

void _compose_tile(int x, int y, DrawingSurface*d1, DrawingSurface*d2, DrawingSurface*d3, DrawingSurface*d4) {
  DrawingSurface*ds = t.GetDrawingSurface();
  ds.Clear(Game.GetColorFromRGB(255, 0, 255));
  temp = DynamicSprite.CreateFromDrawingSurface(d1, x, y, tiledims, tiledims);
  ds.DrawImage(0, 0, temp.Graphic);
  temp = DynamicSprite.CreateFromDrawingSurface(d2, x, y, tiledims, tiledims);
  ds.DrawImage(0, 0, temp.Graphic);
  temp = DynamicSprite.CreateFromDrawingSurface(d3, x, y, tiledims, tiledims);
  ds.DrawImage(0, 0, temp.Graphic);
  temp = DynamicSprite.CreateFromDrawingSurface(d4, x, y, tiledims, tiledims);
  ds.DrawImage(0, 0, temp.Graphic);

void _str_vs::ReDraw(bool update_guis) {
  if (!this.maploaded) return;
  DrawingSurface*rds = Room.GetDrawingSurfaceForBackground();
  rds.DrawingColor = this.bgc;
  rds.DrawRectangle(this.x, this.y, this.x+this.w-1, this.y+this.h-1);
  DynamicSprite*map = DynamicSprite.Create(,, false); // holds final map
  DrawingSurface*ds = map.GetDrawingSurface();
  ds.DrawingColor = 15;
  t = DynamicSprite.Create(tiledims, tiledims, false);
  // update character layers Z1&Z2
  // draw all layers together
  DrawingSurface*l1 = L1.GetDrawingSurface();
  DrawingSurface*l2 = L2.GetDrawingSurface();
  DrawingSurface*z1 = Z1.GetDrawingSurface();
  DrawingSurface*z2 = Z2.GetDrawingSurface();
  int x, y, i, zp, zm;
  int xx, yy;
  while (y < mapheight) {
    x = 0;
    while (x < mapwidth) {
      i = y * mapwidth + x;
      xx = x*tiledims;
      yy = y*tiledims;
      zp = Tile[i].zplane;
      zm = Tile[i].zmode;
      if (zp == eZplaneN) {
        if (zm == 0)      _compose_tile(xx, yy, z1, l1, l2, z2); // (Z1 sprites below ground)
        else if (zm == 1) _compose_tile(xx, yy, l1, z1, z2, l2); // (high ground above all sprites)
      else if (zp == eZplane1) {
        if (zm == 0)      _compose_tile(xx, yy, l1, l2, z1, z2); // (all ground below sprites)
        else if (zm == 1) _compose_tile(xx, yy, l1, z1, l2, z2); // (high ground above Z1 sprites)
      else if (zp == eZplane2) {
        if (zm == 0)      _compose_tile(xx, yy, z1, z2, l1, l2); // (all ground above sprites)
        else if (zm == 1) _compose_tile(xx, yy, z1, l1, z2, l2); // (ground over sprite)
      else if (zp == eZplaneT) {
        if (zm == 0)      _compose_tile(xx, yy, l1, z1, l2, z2); // (all ground above sprites)
        else if (zm == 1) _compose_tile(xx, yy, z1, z2, l1, l2); // (all ground above sprites)
      ds.DrawImage(xx, yy, t.Graphic);
      if (zm) ds.DrawPixel(xx+1, yy+1);
  // canvas change to reflect viewscreen size and offset
  map.ChangeCanvasSize(this.w, this.h, this.ox, this.oy);
  rds.DrawImage(this.x, this.y, map.Graphic);

I can already think of two methods of optimizing this:

- Only draw the part of the map that's actually visible on-screen. This is part of my to-do list anyway.

- When I redraw the sprite layers, I loop through all tiles that contain part of a sprite. I could mark them, then skip unmarked ones inside _compose_tile.

The thing is, given that the screen will show around 300 tiles, I'll be in the range of over a thousand DynamicSprite.CreateFromDrawingSurface and DrawingSurface.DrawImage commands each loop, even with those optimizations.

I can't think outside of the box any longer, so I'm glad for any input on this. :)

I've actually just thought of another way immediately after writing this.
There's no need to redraw tiles without sprites, so I could skip redrawing those every loop using flags marking tiles as dirty for the whole map.

Advanced Technical Forum / [MODULE] ScrollingCounter v0.1
« on: 04 May 2010, 00:23 »
This module supports up to 99 analog style counters:



To add a counter to the game, first define a global integer variable using the Global Variables pane (or the import/export method).
Then call
  VARIABLE = CreateCounter();
The next step is setting up the counter. Add a Gui, then call:
  Counter[VARIABLE].Setup(GUI*gui_to_use, int width_in_digits, int initial_value = 0, int delay = 5);
The Gui is resized automatically, initial_value and delay are optional.
A delay value of 5 is pretty slow, use lower values to make it faster. (5 means the counter is scrolled by one pixel every five frames, -2 will scroll the counter by 3 pixels every frame).

To change the default appearance (see first example image), use
  Counter[VARIABLE].SetAppearance(FontType font, int font_color, int bg_color);
The border is drawn using the font color.

To set/scroll the counter, there are three possibilities:
  Counter[VARIABLE].SetValue(int v);
                   .ScrollBy(int by);
                   .ScrollTo(int target);
SetValue changes the counter immediately to v, ScrollBy scrolls by times, ScrollTo scrolls to target. Obviously, ScrollBy() allows for negative values. Currently, it's not possible to scroll below 0 or above 10^digits-1 though.

Possible future features:
-support putting counters on Gui buttons
-allow for negative values
-change counter array to instances
-expand appearance settings


Pages: [1] 2 3