Author Topic: TEMPLATE: Tumbleweed Verbs 1.2 Last update: 4th April 2019  (Read 13864 times)

abstauber

  • Cavefish
  • still mowing the lawn
    • abstauber worked on one or more games that won an AGS Award!
    •  
    • abstauber worked on one or more games that was nominated for an AGS Award!
@Babar: Sorry about the broken link, I've just fixed it.

@Monsieur OUXX: Your changes seem to make a lot of sense, but unfortunately your pull request does not compile (yet)
Quote
GlobalScript.asc(54): Error (line 54): '.AdjustLanguage' is not a public member of 'Verbs'. Are you sure you spelt it correctly (remember, capital letters are important)?

Monsieur OUXX

  • Mittens Half Initiate
    • I can help with proof reading
    • I can help with translating
    • I can help with voice acting
    • Monsieur OUXX worked on one or more games that won an AGS Award!
    •  
    • Monsieur OUXX worked on one or more games that was nominated for an AGS Award!
your pull request does not compile (yet)

Weird. I made sure I never committed without compiling and testing first.
How do pull requests work? Do you see the state of the code at the time I did the pull request, or do you see the current state of the cloned respository and the branch it contains?
 

abstauber

  • Cavefish
  • still mowing the lawn
    • abstauber worked on one or more games that won an AGS Award!
    •  
    • abstauber worked on one or more games that was nominated for an AGS Award!
I downloaded your last commit from Aug 18th (dedfd488392cf46dad0af3dbe2ddde97eb44c1d f) and tried it to compile it with AGS. Since then I still didn't find the time review all the changes and probably find a fix for the remaining bugs.

That also raises the question: do I need merge this borked pull request and fix the bugs in the master branch or do I have to work in your repo to make it happen?

That also raises the question: do I need merge this borked pull request and fix the bugs in the master branch or do I have to work in your repo to make it happen?

Sorry for barging in the discussion; technically both ways are viable, usually this is rather an organizational question.
Also, a while ago Github has allowed such feature as letting the owner of target repository to add commits directly to pull request branch, if the PR creator has ticked this option. Personally I cannot tell if it's the case not having necessary permissions there.

BTW this reminds me, Monsieur OUXX, have you noticed my comment to your pull request?
https://github.com/dkrey/ags_tumbleweed/pull/1#issuecomment-411395083

abstauber

  • Cavefish
  • still mowing the lawn
    • abstauber worked on one or more games that won an AGS Award!
    •  
    • abstauber worked on one or more games that was nominated for an AGS Award!
Quote
Sorry for barging in the discussion
I was actually hoping you would do that :D
Thanks for the clarification, I guess I just need to wait until Mathieu replies. Maybe in the meantime I find time to do a full code review, since this pull request is quite a biggie.
In any case the #error command will find its way into the template (nod)

Monsieur OUXX

  • Mittens Half Initiate
    • I can help with proof reading
    • I can help with translating
    • I can help with voice acting
    • Monsieur OUXX worked on one or more games that won an AGS Award!
    •  
    • Monsieur OUXX worked on one or more games that was nominated for an AGS Award!
Sorry guys I had a crazy month (I think the other people in my game project have probably hired bounty hunters to get me by now). I'll check out my code and the state of the pull, as well as CW's comment asap.
 

abstauber

  • Cavefish
  • still mowing the lawn
    • abstauber worked on one or more games that won an AGS Award!
    •  
    • abstauber worked on one or more games that was nominated for an AGS Award!
Quote
I'll check out my code and the state of the pull, as well as CW's comment asap.
(wtf)

Hehe, no worries. I was able to find some time fixing your bugs and to merge your changes. Thanks for the tedious work of refactoring all this stuff (nod)

Anyone dares to test this right from the repo as well?
« Last Edit: 06 Nov 2018, 16:13 by abstauber »

abstauber

  • Cavefish
  • still mowing the lawn
    • abstauber worked on one or more games that won an AGS Award!
    •  
    • abstauber worked on one or more games that was nominated for an AGS Award!
Here's the new beta, compiled as a template for your convenience :)
I've been testing it for a bit now, but I couldn't find anything odd yet. But since so much has changed code wise, it would be great if anyone else could have a look.

Changes (mostly by Monsieur Ouxx)
- cleaned up the global script (moved the remaining GUI actions to the options menu module)
- Localisation of the GUI elements is handled a bit different internally
- It's possible to change the GUIs being used by the template
- Some more refactoring, the template throws errors if a module is missing.

Here it is:
http://shatten.sonores.de/wp-content/uploads/2018/11/tumbleweed_12_beta.zip

Monsieur OUXX

  • Mittens Half Initiate
    • I can help with proof reading
    • I can help with translating
    • I can help with voice acting
    • Monsieur OUXX worked on one or more games that won an AGS Award!
    •  
    • Monsieur OUXX worked on one or more games that was nominated for an AGS Award!
So I'm not forgetting that I need to review the pull request, but in the meantime I'm having a weird issue. thanks to Khris it's been narrowed down to AGS apparently not being very good at having a sprite with alpha AND a transparency > 0 used as a GUI background.

I'm trying to set the custom dialogs GUI background to a black background with a slight transparency. Everything below failed :

- I tried with a fully black image (no alpha) and eDialogGui_bg_img_transparency set to 20, the background does not appear at all.
- I tried with a sprite imported from a PNG containing an all-black image with alpha (the black is 20% transparent) and  and eDialogGui_bg_img_transparency set to 0, now it's the font outline that goes cuckoo.
- I tried with the same semi-transparent PNG sprite and eDialogGui_bg_img_transparency set to -1. Now it's the transparency rendering that's all over the place (some pixels become fully transparent while some others are fully opaque).

What is the proper way?
 

Snarky

  • Global Moderator
  • Private Insultant
    • Best Innovation Award Winner 2018, for his numerous additions to the AGS open source ecosystem including the new Awards Ceremony client and modules
    • Snarky worked on one or more games that won an AGS Award!
    •  
    • Snarky worked on one or more games that was nominated for an AGS Award!
There is no proper way. There's a bug in AGS: rendering is broken for GUIs with alpha-transparent backgrounds and content (including text) with alpha-transparency.

The workaround is to use two GUIs; one for the background and one on top (with the background set to COLOR_TRANSPARENT) for the content.

abstauber

  • Cavefish
  • still mowing the lawn
    • abstauber worked on one or more games that won an AGS Award!
    •  
    • abstauber worked on one or more games that was nominated for an AGS Award!
Maybe I don't get it, but if it's about a GUI background with different alpha levels... that works and it's already implemented in the template.
Code: Adventure Game Studio
  1.   // Background
  2.   // set bg_img_transparency to -1 if you're using 32-bit graphics and
  3.   // want to preserve the alpha channel  
  4.   CustomDialogGui.DialogGuiOptions[eDialogGui_bg_img]                = 122;
  5.   CustomDialogGui.DialogGuiOptions[eDialogGui_bg_img_scaling]        = 0;
  6.   CustomDialogGui.DialogGuiOptions[eDialogGui_bg_img_transparency]   = -1;
  7.   CustomDialogGui.DialogGuiOptions[eDialogGui_bg_color]              = 0;
  8.  

When you work with an alpha transparent PNG, you actually don't want AGS to interfere with it, you do it all in the image file.

Quote
- I tried with a sprite imported from a PNG containing an all-black image with alpha (the black is 20% transparent) and  and eDialogGui_bg_img_transparency set to 0, now it's the font outline that goes cuckoo
Just set eDialogGui_bg_img_transparency to -1 and you should be fine.

There is no proper way. There's a bug in AGS: rendering is broken for GUIs with alpha-transparent backgrounds and content (including text) with alpha-transparency.

This was supposed to be fixed long ago! If it's still not working please do bug report, otherwise I won't know about it.

abstauber

  • Cavefish
  • still mowing the lawn
    • abstauber worked on one or more games that won an AGS Award!
    •  
    • abstauber worked on one or more games that was nominated for an AGS Award!
There is no proper way. There's a bug in AGS: rendering is broken for GUIs with alpha-transparent backgrounds and content (including text) with alpha-transparency.

This was supposed to be fixed long ago! If it's still not working please do bug report, otherwise I won't know about it.

Here's proof that it works  8-) The frame is 100% opaque, the background is semi transparent, the scroll arrows render correctly as does the font outline.


Monsieur OUXX

  • Mittens Half Initiate
    • I can help with proof reading
    • I can help with translating
    • I can help with voice acting
    • Monsieur OUXX worked on one or more games that won an AGS Award!
    •  
    • Monsieur OUXX worked on one or more games that was nominated for an AGS Award!

  CustomDialogGui.DialogGuiOptions[eDialogGui_bg_img_transparency]   = -1;

OK. I don't know what I'm doing wrong. When importing the PNG, should I say "yes" to "use alpha?" and should I select "leave transparency as-is"?
 

abstauber

  • Cavefish
  • still mowing the lawn
    • abstauber worked on one or more games that won an AGS Award!
    •  
    • abstauber worked on one or more games that was nominated for an AGS Award!

  CustomDialogGui.DialogGuiOptions[eDialogGui_bg_img_transparency]   = -1;

OK. I don't know what I'm doing wrong. When importing the PNG, should I say "yes" to "use alpha?" and should I select "leave transparency as-is"?

Yep.
Also make sure to check the option "GUI alpha rendering style" in the general settings. It has to be set to Proper Alpha Blending".

Here's the source image from the template, if you import this one, you can be sure that it works.


https://raw.githubusercontent.com/dkrey/ags_tumbleweed/master/assets/gui/dialog_background.png

tzachs

  • Parking Goat- games that goats like!
    • I can help with translating
    • tzachs worked on one or more games that won an AGS Award!
    •  
    • tzachs worked on one or more games that was nominated for an AGS Award!
Maybe you're using an older version of AGS (from before the fix)?

Monsieur OUXX

  • Mittens Half Initiate
    • I can help with proof reading
    • I can help with translating
    • I can help with voice acting
    • Monsieur OUXX worked on one or more games that won an AGS Award!
    •  
    • Monsieur OUXX worked on one or more games that was nominated for an AGS Award!
PROBLEM SOLVED.

From the start I was focusing on "GUI alpha rendering style", but it was actually "Sprite alpha rendering style" that caused the issue.
I'm not sure if it was because of the background sprite I was using OR if it was because of the font I'm using, that contains semi-transparent pixels, causing the outline to go crazy when the blending failed (unfortunately it went so crazy that I failed to visually identify it as only the outline)

Anyways now that everything is set to "proper alpha blending", everything seems to work as expected.

Thanks!!!
This is the thing that took me the most time on the short game I'm making :D *sigh of relief*
 

Monsieur OUXX

  • Mittens Half Initiate
    • I can help with proof reading
    • I can help with translating
    • I can help with voice acting
    • Monsieur OUXX worked on one or more games that won an AGS Award!
    •  
    • Monsieur OUXX worked on one or more games that was nominated for an AGS Award!
I have a new problem, which is the font issue that I've mentionned previously. It's not caused by the module itself however I don't understand what differs between my own game files and the demo game.

Abstauber, unfortunately the best way to show you my issue is to share the game files but I don't want to do it publicly, so I'm sending them to you in a Private Message.

For everyone, here is my issue's description:

CONTEXT
- I have a game based on the Tumbleweed module.
- It contains the "gAction" GUI, which itself contains "lblAction" -- same as Tumbleweed demo game
- lblAction uses the "eFontTumbleTextOut" font.  -- same as Tumbleweed demo game
- "TumbleTextOut" was imported from assets/xpaider2.ttf  -- same as Tumbleweed demo game
- TumbleTextOut was imported as 9pts and has outline "automatic" -- same as Tumbleweed demo game
- the game is set to "proper alpha blending" for both GUI and fonts -- same as Tumbleweed demo game

ISSUE
- In the demo game, when i open gAction, the label appears with the font without its outline (the outline is rendered only in-game)
- In my own game, when I open gAction, the label appears with a crappy 1-pixel outline the same color as the font. It's like the font is white with a white outline.

Screenshot:


I've tried tried overwriting xpaider2.ttf over itself, by re-importing it into the Tumbleweed demo game (without changing anything else), to see if it would do the same as in my game. That is, to see if the import action was broken in my current AGS version (3.4.1.13) but the label is still rendered properly in the Editor in the demo game.
I've tried re-importing /xpaider2.ttf over itself into my own game, but the issue is still there : it has this unwanted white outline.

Note that the font appears as expected in the Editor's "font" panel. It's only messed up in the label in gAction.

I'm 100% sure it's caused by transparency and some font pixels not being 100% transparent, hence being rendered as 100% opaque. But I just can't understand why it doesn't do the same in my game and in the demo game.

==========

unrelated : how do you manage to have all the GUIs appear with the proper Z order in your demo game, without defining any custom z-order? I had to do it in my own game.
« Last Edit: 30 Jan 2019, 13:36 by Monsieur OUXX »
 

abstauber

  • Cavefish
  • still mowing the lawn
    • abstauber worked on one or more games that won an AGS Award!
    •  
    • abstauber worked on one or more games that was nominated for an AGS Award!
Hmm... it seems like you somehow triggered an editor bug. The font is rendered correctly in the game itself and it is also rendered correctly if you chose a button instead of a label.

It is just the label, that doesn't like the Xpaider font. Changing other labels to the mentioned font (your debug GUI for example) worked perfectly fine. But creating a new GUI with a new label resulted in a messy font again.

Like you described, I wasn't able to replicate this outside of your game project.

I'd say you also show your sources to Tzachs and Crimson Wizard, maybe they have a clue about what going on.

Monsieur OUXX

  • Mittens Half Initiate
    • I can help with proof reading
    • I can help with translating
    • I can help with voice acting
    • Monsieur OUXX worked on one or more games that won an AGS Award!
    •  
    • Monsieur OUXX worked on one or more games that was nominated for an AGS Award!
I wasn't able to replicate this outside of your game project.

Thanks a lot for such a quick answer. I'll send the link to CW and Tzachs.

============

EDIT: so the issue was that "Anti-alias TTF fonts" was set to "true" in the general settings. Now everything works perfectly.

============

EDIT 2 :

Here is a module that generates all the action buttons automatically, with an outline, shadow, inner gradient, and is meant to fit nicely with Tumbleweed. It also manages the horizontal stretching of some buttons in the languages that allow it (mostly, English)
It's meant to save the hassle of having to redraw and reimport a billion action buttons in every language. In our case we didn't like the blue theme but it would have taken an eternity to change it to red.

It's called "AutoButtons" (CODE BELOW)

Spoiler: ShowHide



Module header :
Code: Adventure Game Studio
  1. /*
  2.  
  3. //All values below defined in Tumbleweed module
  4. enum Action {
  5.   eGA_LookAt = 0, //Starting at zero helps avoiding human mistakes when iterating on the enum
  6.   eGA_TalkTo,
  7.   eGA_GiveTo,
  8.   eGA_PickUp,
  9.   eGA_Use,
  10.   eGA_Open,
  11.   eGA_Close,
  12.   eGA_Push,
  13.   eGA_Pull,
  14.   eGA_UseInv,
  15.   eGA_Default,
  16.   eGA_WalkTo
  17. };
  18.  
  19. enum eLanguage {
  20.   eLangEN = 0, //Starting at zero helps avoiding human mistakes when iterating on the enum
  21.   eLangDE,
  22.   eLangES,
  23.   eLangIT,
  24.   eLangFR,
  25.   eLangPT,
  26.   eLangNL
  27. };
  28.  
  29. */
  30.  
  31. enum AutoButtonStates {
  32.     eAutoButton_off = 0,
  33.     eAutoButton_on = 1,
  34.     eAutoButton_hover = 2
  35. };
  36.  
  37.  
  38. #define THEMESIZE 20 //this must be bigger than the number of values in AutoButtonsThemeSettings
  39.  
  40. enum AutoButtonsThemeSettings {
  41.   eAutoBSetting_color_off,  
  42.   eAutoBSetting_color_off_gradienttop,  
  43.   eAutoBSetting_color_off_gradientbottom,  
  44.   eAutoBSetting_color_on,  
  45.   eAutoBSetting_color_on_gradienttop,  
  46.   eAutoBSetting_color_on_gradientbottom,  
  47.   eAutoBSetting_color_hover,  
  48.   eAutoBSetting_color_hover_gradienttop,  
  49.   eAutoBSetting_color_hover_gradientbottom,  
  50.   eAutoBSetting_color_outline,  
  51.   eAutoBSetting_color_shadow
  52. };
  53.  
  54. enum AutoButtonThemes {
  55.     eAutoButtonTheme_Red = 0,
  56.     eAutoButtonTheme_Blue = 1
  57.    
  58. };
  59.  
  60. struct AutoButtons
  61. {
  62.     import static int[] GetTheme(AutoButtonThemes theme); //Get the settings values from any of the built-in themes
  63.     import static void SetTheme(AutoButtonThemes theme,  int themeSettings[]); //Overwrite an existing theme from your game, without requiring to temper with the module's script (if needed)
  64.     import static int GenerateAll(int themeSettings[]);
  65.     import static DynamicSprite* GetSprite(eLanguage lan,  Action action,  AutoButtonStates state);
  66.     import static int GetSpriteID(eLanguage lan,  Action action,  AutoButtonStates state);
  67.    
  68.    
  69. };
  70.  




Module body :
Code: Adventure Game Studio
  1. #define NBACTIONS 12
  2. #define NBLANGUAGES 7
  3. #define MAXAUTOBUTTONS 84 //12*7
  4.  
  5. #define BUTT_WIDTH 56
  6. #define BUTT_HEIGHT 12
  7.  
  8. #define MAXTEXTWIDTH 200 //Some bullshit width to be passed to GetTextHeight. Should be bigger than the biggest possible width
  9.  
  10. int defaultTheme;
  11. GUI* guiMain; //The gui that has the action buttons
  12.  
  13. struct AutoButtonData {
  14.     DynamicSprite* s[3]; //one for each state
  15.     eLanguage lan;
  16.     Action action;
  17.     String text;
  18.     int widenFactor; //how much the text should be stretched horizontally (not all the same depending on the language and the action)
  19.     int width; //what should be the width of the buttons (they don't have the same width depending on the language and the action)
  20. };
  21. AutoButtonData buttons[MAXAUTOBUTTONS];
  22.  
  23. #define MAXTHEMES 3
  24.  
  25. struct Constants {
  26.     int width;
  27.     int height;    
  28. };
  29. Constants constants;
  30.  
  31. struct Theme {
  32.     //all values below initialized in game_start or if you call SetTheme
  33.     int settings[THEMESIZE];
  34.    
  35.     //You can add any field you want. For example String themeName;
  36.    
  37. };
  38. Theme themes[MAXTHEMES];
  39. //int nbThemes;
  40.  
  41.  
  42. //Unfortunately the Verbs module does not provide the bare actions, only with their prepositions and complement, like "Look at %s"
  43. String TranslateAction(Action a, eLanguage lan) {
  44.     switch(lan) {
  45.         case eLangEN:
  46.             switch(a) {
  47.                 case eGA_WalkTo: return "Go to";
  48.                 case eGA_LookAt: return "Look At";
  49.                 case eGA_TalkTo: return "Talk To";
  50.                 case eGA_GiveTo: return "Give";
  51.                 case eGA_PickUp: return "Pick Up";
  52.                 case eGA_Use: return "Use";
  53.                 case eGA_Open: return "Open";
  54.                 case eGA_Close: return "Close";
  55.                 case eGA_Push: return "Push";
  56.                 case eGA_Pull: return "Pull";
  57.                 default :
  58.                     return "ERROR";
  59.                     break;
  60.             }
  61.             break;
  62.         case eLangDE :
  63.             switch(a) {
  64.                 case eGA_WalkTo: return "Gehe zu";
  65.                 case eGA_LookAt: return "Schau";
  66.                 case eGA_TalkTo: return "Rede";
  67.                 case eGA_GiveTo: return "Gebe";
  68.                 case eGA_PickUp: return "Nehme";
  69.                 case eGA_Use: return "Nutze";
  70.                 case eGA_Open: return "Öffne";
  71.                 case eGA_Close: return "Schließe";
  72.                 case eGA_Push: return "Drücke";
  73.                 case eGA_Pull: return "Ziehe";
  74.                 default :
  75.                     return "ERROR";
  76.                     break;
  77.             }
  78.             break;
  79.         case eLangES :
  80.             switch(a) {
  81.                 case eGA_WalkTo: return "Ir a";
  82.                 case eGA_LookAt: return "Mirar";
  83.                 case eGA_TalkTo: return "Hablar";
  84.                 case eGA_GiveTo: return "Dar";
  85.                 case eGA_PickUp: return "Coger";
  86.                 case eGA_Use: return "Usar";
  87.                 case eGA_Open: return "Abrir";
  88.                 case eGA_Close: return "Cerrar";
  89.                 case eGA_Push: return "Empujar";
  90.                 case eGA_Pull: return "Tirar";
  91.                 default :
  92.                     return "ERROR";
  93.                     break;
  94.             }
  95.             break;
  96.         case eLangFR :
  97.             switch(a) {
  98.                 case eGA_WalkTo: return "Aller";
  99.                 case eGA_LookAt: return "Regarder";
  100.                 case eGA_TalkTo: return "Parler";
  101.                 case eGA_GiveTo: return "Donner";
  102.                 case eGA_PickUp: return "Prendre";
  103.                 case eGA_Use: return "Utiliser";
  104.                 case eGA_Open: return "Ouvrir";
  105.                 case eGA_Close: return "Fermer";
  106.                 case eGA_Push: return "Pousser";
  107.                 case eGA_Pull: return "Tirer";
  108.                 default :
  109.                     return "ERROR";
  110.                     break;
  111.             }
  112.             break;
  113.         case eLangIT :
  114.             switch(a) {
  115.                
  116.                 case eGA_WalkTo: return "Vai a";
  117.                 case eGA_LookAt: return "Esamina";
  118.                 case eGA_TalkTo: return "Parla";
  119.                 case eGA_GiveTo: return "Dai";
  120.                 case eGA_PickUp: return "Raccogli";
  121.                 case eGA_Use: return "Usa";
  122.                 case eGA_Open: return "Apri";
  123.                 case eGA_Close: return "Ferma";
  124.                 case eGA_Push: return "Premi";
  125.                 case eGA_Pull: return "Tira";
  126.                 default :
  127.                     return "ERROR";
  128.                     break;
  129.             }
  130.             break;
  131.         case eLangPT :
  132.             switch(a) {  
  133.                
  134.                 case eGA_WalkTo: return "Ir para";
  135.                 case eGA_LookAt: return "Olhar";
  136.                 case eGA_TalkTo: return "Falar";
  137.                 case eGA_GiveTo: return "Dar";
  138.                 case eGA_PickUp: return "Apanhar";
  139.                 case eGA_Use: return "Usar";
  140.                 case eGA_Open: return "Abrir";
  141.                 case eGA_Close: return "Fechar";
  142.                 case eGA_Push: return "Empurrar";
  143.                 case eGA_Pull: return "Puxar";
  144.                 default :
  145.                     return "ERROR";
  146.                     break;
  147.             }
  148.             break;
  149.         case eLangNL :
  150.             switch(a) {
  151.    
  152.                 case eGA_WalkTo: return "Ga naar";
  153.                 case eGA_LookAt: return "Bekijk";
  154.                 case eGA_TalkTo: return "Praat";
  155.                 case eGA_GiveTo: return "Geef";
  156.                 case eGA_PickUp: return "Pak";
  157.                 case eGA_Use: return "Gebruik";
  158.                 case eGA_Open: return "Open";
  159.                 case eGA_Close: return "Sluit";
  160.                 case eGA_Push: return "Duw";
  161.                 case eGA_Pull: return "Trek";
  162.                 default :
  163.                     return "ERROR";
  164.                     break;
  165.             }
  166.             break;    
  167.         default :
  168.                 return "ERROR2";
  169.                 break;
  170.     }
  171. }
  172.  
  173. DynamicSprite* DrawString_Widened(int font, String text, int color,  int widenFactor )
  174. {
  175.     int kerning = 0;
  176.     int wordSpacing = 2; //arbitrary number of pixels for the 'space' character, to make the text more compact
  177.     int maxWidth = GetTextWidth(text, font)*3;
  178.     int height = GetTextHeight(text,  font,  MAXTEXTWIDTH);
  179.    
  180.     //The width is a bit hard to calculate so we'll start by drawing onto a temporary surface
  181.     DynamicSprite* temp_s = DynamicSprite.Create(maxWidth,  height, true);
  182.     DrawingSurface* temp_ds = temp_s.GetDrawingSurface();
  183.     temp_ds.Clear();
  184.     int offset = 0;
  185.     temp_ds.DrawingColor = color;
  186.     //we draw each letter one by one
  187.     for (int i=0; i<text.Length; i++) {
  188.         if (text.Chars[i] == ' ') {
  189.            offset+=wordSpacing;
  190.            
  191.         } else {
  192.             String c = String.Format("%c", text.Chars[i]);
  193.             int letterWidth = GetTextWidth(c, font);
  194.             //the easiest way to widen a letter is to draw it several times
  195.             for (int j=0; j < widenFactor; j++) {
  196.                 temp_ds.DrawString(offset+j, 0, font, c);
  197.             }
  198.             offset+=(letterWidth+widenFactor);
  199.            
  200.             //if it's not the last letter we add a white space after the letter
  201.             if (i<text.Length-1)
  202.                 offset+=kerning;
  203.         }
  204.     }
  205.    
  206.     //now that we know the width we copy the temp sprite to a final sprite    
  207.     DynamicSprite* s = DynamicSprite.Create(offset,  height, true);
  208.     DrawingSurface* ds = s.GetDrawingSurface();
  209.     ds.DrawSurface(temp_ds);
  210.    
  211.     temp_ds.Release();
  212.    
  213.     return s;
  214.    
  215. }
  216.  
  217. //Draws 'graphic' 9 times onto ds, in an "outlined" manner (i.e. with offsets of -1 to +1)
  218. void DrawOutline(DrawingSurface*ds,  int x,  int y,  int graphic)
  219. {
  220.    ds.DrawImage(x-1, y-1, graphic);
  221.    ds.DrawImage(x-0, y-1, graphic);
  222.    ds.DrawImage(x+1, y-1, graphic);
  223.    ds.DrawImage(x-1, y-0, graphic);
  224.    ds.DrawImage(x-0, y-0, graphic);
  225.    ds.DrawImage(x+1, y-0, graphic);
  226.    ds.DrawImage(x-1, y+1, graphic);
  227.    ds.DrawImage(x-0, y+1, graphic);
  228.    ds.DrawImage(x+1, y+1, graphic);
  229. }
  230.  
  231. //See more at https://www.adventuregamestudio.co.uk/forums/index.php?topic=42449.0
  232. int[] GetRGBFromColor(int color)
  233. {
  234.     int rgb[] = new int[3];
  235.     bool highBit = true; //or false, you decide
  236.  
  237.     if (color > 65535) color -= 65536;
  238.     rgb[0] = ((color >> 11) & 31) << 3;
  239.     rgb[1] = ((color >> 6) & 31) << 3;
  240.     rgb[2] = (color & 31) << 3;
  241.     if (highBit)
  242.     {
  243.         rgb[0] = rgb[0] | 7;
  244.         rgb[1] = rgb[1] | 3;
  245.         rgb[2] = rgb[2] | 7;
  246.     }
  247.  
  248.     return rgb;
  249. }
  250.  
  251. //from top to bottom, every color of 'ds' that has color 'color' will be replaced by a gradient
  252. void noloopcheck ApplyGradient(DrawingSurface* ds, int color, int color_gradient_top, int color_gradient_bottom)
  253. {
  254.     int rgb_top[] = GetRGBFromColor(color_gradient_top);
  255.     int rgb_bottom[] = GetRGBFromColor(color_gradient_bottom);
  256.     float height_f = IntToFloat(ds.Height);
  257.    
  258.     float r_top = IntToFloat(rgb_top[0]); float r_bottom = IntToFloat(rgb_bottom[0]); float step_r = (r_bottom-r_top)/height_f;
  259.     float g_top = IntToFloat(rgb_top[1]); float g_bottom = IntToFloat(rgb_bottom[1]); float step_g = (g_bottom-g_top)/height_f;
  260.     float b_top = IntToFloat(rgb_top[2]); float b_bottom = IntToFloat(rgb_bottom[2]); float step_b = (b_bottom-b_top)/height_f;
  261.    
  262.     float r = r_top; float g = g_top; float b = b_top; //start values
  263.     for (int j=0; j< ds.Height; j++) {
  264.         ds.DrawingColor = Game.GetColorFromRGB(FloatToInt(r), FloatToInt(g),  FloatToInt(b));
  265.        
  266.         for (int i=0; i<ds.Width; i++) {
  267.             if (ds.GetPixel(i, j)==color)
  268.                 ds.DrawPixel(i, j);
  269.         }
  270.        
  271.         r+=step_r; g+=step_g; b+=step_b;
  272.     }
  273. }
  274.  
  275. DynamicSprite* GenerateButton(String text,  int width,  AutoButtonStates state,  int widenFactor,  int themeSettings[])
  276. {
  277.    
  278.    int font = eFontTumbleText;
  279.    
  280.    //int COLOR_BLACK = Game.GetColorFromRGB(5, 5, 5);
  281.    int COLOR_RED = Game.GetColorFromRGB(255, 0, 0);
  282.    int color = COLOR_RED; //we give it a default value for satefy
  283.    int color_gradient_top = COLOR_RED; //we give it a default value for satefy
  284.    int color_gradient_bottom = COLOR_RED; //we give it a default value for satefy
  285.    switch(state) {
  286.        case eAutoButton_off :
  287.         color = themeSettings[eAutoBSetting_color_off];
  288.         color_gradient_top = themeSettings[eAutoBSetting_color_off_gradienttop];
  289.         color_gradient_bottom = themeSettings[eAutoBSetting_color_off_gradientbottom];
  290.         break;
  291.        case eAutoButton_on :
  292.         color = themeSettings[eAutoBSetting_color_on];
  293.         color_gradient_top = themeSettings[eAutoBSetting_color_on_gradienttop];
  294.         color_gradient_bottom = themeSettings[eAutoBSetting_color_on_gradientbottom];
  295.         break;
  296.        case eAutoButton_hover :
  297.         color = themeSettings[eAutoBSetting_color_hover];
  298.         color_gradient_top = themeSettings[eAutoBSetting_color_hover_gradienttop];
  299.         color_gradient_bottom = themeSettings[eAutoBSetting_color_hover_gradientbottom];
  300.         break;
  301.        
  302.        default:
  303.         AbortGame("Unknown state");
  304.    }
  305.    //int width = constants.width;
  306.    int height = constants.height;
  307.    DynamicSprite* text_s = null;
  308.    
  309.    DynamicSprite* s = DynamicSprite.Create(width, height, true);  
  310.    DrawingSurface* ds = s.GetDrawingSurface();
  311.    //ds.DrawingColor = COLOR_TRANSPARENT;
  312.    ds.Clear();
  313.    
  314.    //int textWidth = GetTextWidth(text, font); //we actually need the widened width
  315.    int textHeight = GetTextHeight(text, font, MAXTEXTWIDTH);
  316.    
  317.    //(optional) colored rectangular background
  318.    //ds.DrawingColor = Game.GetColorFromRGB(100, 100, 100);
  319.    //ds.DrawRectangle(0, 0, width, height);
  320.    
  321.    //shadow
  322.    text_s = DrawString_Widened(font, text, themeSettings[eAutoBSetting_color_shadow], widenFactor );
  323.    int textWidth = text_s.Width;
  324.    int offset_x = (width - textWidth)/2; if (offset_x < 0) offset_x = 0;
  325.    int offset_y = (height - textHeight)/2;  if (offset_y < 0) offset_y = 0;
  326.    DrawOutline(ds, offset_x+widenFactor, offset_y, text_s.Graphic);
  327.    
  328.    //outline (the cheapest way to do an outline is to draw the text 9 times with and offset of -1 or +1 all around
  329.    text_s = DrawString_Widened(font, text, themeSettings[eAutoBSetting_color_outline], widenFactor );
  330.     DrawOutline(ds, offset_x, offset_y, text_s.Graphic);
  331.    //widened text. It returns a temporary sprite that we immediately copy onto our drawing surface
  332.    text_s = DrawString_Widened(font, text, color, widenFactor );
  333.    ds.DrawImage(offset_x,  offset_y, text_s.Graphic);
  334.    
  335.    //gradient
  336.    ApplyGradient(ds, color,  color_gradient_top,  color_gradient_bottom);
  337.    
  338.    //finish up
  339.    text_s.Delete();
  340.    ds.Release();
  341.    
  342.    return s;
  343. }
  344.  
  345. //this is purely built-in
  346. //how much the text should be stretched horizontally (not all the same depending on the language and the action)
  347. int GetWidenFactor(eLanguage lan,  Action a)
  348. {
  349.     switch(lan) {
  350.         case eLangDE :
  351.                     if (a==eGA_Close) //In German, "SchlieBe"'s font is narrower then the other buttons fonts
  352.                         return 1;
  353.                     else
  354.                         return 2;
  355.                     break;
  356.         case eLangEN : return 2; break;
  357.         case eLangES : return 1; break;
  358.         case eLangFR : return 1; break;
  359.         case eLangIT : return 1; break;
  360.         case eLangNL : return 1; break;
  361.         case eLangPT : return 1; break;
  362.  
  363.         default : AbortGame("Unknown language");
  364.     }
  365. }
  366.  
  367.  
  368. //Utility : Returns the button bound to a given action. Unfortunately this function is not provided by Verbs
  369. Button*  GetActionButton(Action a)
  370. {
  371.     //we scan every control
  372.     for (int i=0; i<guiMain.ControlCount; i++) {
  373.       GUIControl* c = guiMain.Controls[i];
  374.       Button* b = c.AsButton;
  375.       if (b!=null) {
  376.           if (Verbs.GetButtonAction(b) == a)
  377.             return b;
  378.       }
  379.     }
  380.    
  381.     //AbortGame("Couldn't find the button bound to action '%d'", a);
  382.     return null; //Not all actions have a button. E.g. "walk to"
  383.    
  384.     //If it was implemented in Verbs we would just use this :
  385.     //return Verbs.GetActionButton(a);
  386. }
  387.  
  388.  
  389. int GetButtonWidth(Action a) {
  390.     /*
  391.     //Somehow the automated code below doesn't work. buttons return goofy values, like b.Width == 200. I have no idea why.
  392.     Button* b = GetActionButton(a);
  393.     if (b!= null) {
  394.         //Display("Action %d return button.ID=%d, which has width %d x height %d. It belongs to GUI %d. Name is '%s'", a,  b.ID,  b.Width, b.Height,  b.OwningGUI.ID,  b.Text);
  395.         return b.Width;
  396.     } else {
  397.         return constants.width; //this action doesn't seem to have a button. We roll back to the default buttons width
  398.     }
  399.     */
  400.    
  401.     //Manual version. TODO : find why code above does not work.
  402.     switch(a) {
  403.         case eGA_LookAt :  return 56; //larger buttons in the central column
  404.         case eGA_TalkTo :  return 56; //larger buttons in the central column
  405.         case eGA_PickUp :  return 56;  //larger buttons in the central column
  406.         default: return 50; //narrower buttons in th eleft and right column
  407.     }
  408. }
  409.  
  410. static int AutoButtons::GenerateAll(int themeSettings[])
  411. {
  412.     int NBSPRITES = NBACTIONS*NBLANGUAGES;
  413.    
  414.     //safety
  415.     if (NBSPRITES > MAXAUTOBUTTONS)
  416.         AbortGame("Did something change?");
  417.    
  418.     //Some languages have shorter words so we can make the text wider up to a factor of 3
  419.     int widenFactor = 1;
  420.    
  421.     for (int i=0; i < NBLANGUAGES; i++) {
  422.        
  423.        
  424.         for (int a = 0; a<NBACTIONS; a++) {
  425.              widenFactor = GetWidenFactor(i,  a);
  426.             int width = GetButtonWidth(a);
  427.            
  428.             buttons[i*NBACTIONS+a].widenFactor = widenFactor;
  429.             buttons[i*NBACTIONS+a].width = width;
  430.            
  431.             String text = TranslateAction(a, i);
  432.            
  433.             buttons[i*NBACTIONS+a].lan = i;
  434.             buttons[i*NBACTIONS+a].action = a;
  435.             buttons[i*NBACTIONS+a].s[eAutoButton_off] = GenerateButton(text, width, eAutoButton_off, widenFactor, themeSettings);
  436.             buttons[i*NBACTIONS+a].s[eAutoButton_on] = GenerateButton(text, width,  eAutoButton_on, widenFactor, themeSettings);
  437.             buttons[i*NBACTIONS+a].s[eAutoButton_hover] = GenerateButton(text, width, eAutoButton_hover, widenFactor, themeSettings);
  438.             buttons[i*NBACTIONS+a].text = text;
  439.            
  440.         }
  441.     }
  442.    
  443. }
  444.  
  445.  
  446. static DynamicSprite* AutoButtons::GetSprite(eLanguage lan,  Action action,  AutoButtonStates state)
  447. {
  448.     return buttons[lan*NBACTIONS+action].s[state];
  449. }
  450.  
  451. static int AutoButtons::GetSpriteID(eLanguage lan,  Action action,  AutoButtonStates state)
  452. {
  453.     return buttons[lan*NBACTIONS+action].s[state].Graphic;
  454. }
  455.  
  456. float min(float a,  float b) { if (a < b) return a; return b; }
  457. float max(float a,  float b) { if (a > b) return a; return b; }
  458. float bound(float a,  float roof,  float floor) { return min(roof, max(a, floor)); }
  459.  
  460. //this function returns a darker or brighter version of color 'color'.
  461. // 'factor' between 0.0 and 2.0.
  462. // - If factor is 0.0, the function returns black (because the color has been darkened to the max).
  463. // - If it's 1.0, the color doesn't change
  464. // - If factor is 2.0, the function probably returns white, or at least 'color" is now twice brighter
  465. int ChangeBrightness(int color,  float factor)
  466. {
  467.     int rgb[] = GetRGBFromColor(color);
  468.     float r = IntToFloat(rgb[0]); float g = IntToFloat(rgb[1]); float b = IntToFloat(rgb[2]);
  469.     rgb = null;
  470.     r = bound(r*factor, 255.0,  0.0); g = bound(g*factor, 255.0,  0.0); b = bound(b*factor, 255.0,  0.0);
  471.     return Game.GetColorFromRGB(FloatToInt(r), FloatToInt(g),FloatToInt(b));
  472. }
  473.  
  474. int[] GetTheme_BuiltIn(AutoButtonThemes theme)
  475. {
  476.  
  477.     //useful values
  478.     int COLOR_RED = Game.GetColorFromRGB(255, 99, 99);
  479.     int COLOR_BROWN = Game.GetColorFromRGB(50, 0, 0);
  480.     int COLOR_YELLOW = Game.GetColorFromRGB(255, 249, 72);
  481.     int COLOR_GRAYISHRED = Game.GetColorFromRGB(116, 63, 63);
  482.     int COLOR_DARKGRAYISHRED = Game.GetColorFromRGB(52, 32, 32);
  483.     int COLOR_BLACK = Game.GetColorFromRGB(5, 5, 5);
  484.    
  485.     int themeSettings[] = new int[THEMESIZE];
  486.    
  487.     switch (theme) {
  488.         case eAutoButtonTheme_Red :
  489.  
  490.    
  491.             themeSettings[eAutoBSetting_color_off] = COLOR_RED;
  492.             themeSettings[eAutoBSetting_color_on] = COLOR_YELLOW;
  493.             themeSettings[eAutoBSetting_color_hover] = COLOR_BROWN;
  494.             themeSettings[eAutoBSetting_color_outline] = COLOR_GRAYISHRED;
  495.             themeSettings[eAutoBSetting_color_shadow] = COLOR_BLACK;
  496.            
  497.             /*
  498.             //MANUAL
  499.             themeSettings[eAutoBSetting_color_off_gradienttop] =            Game.GetColorFromRGB(255, 175, 175);
  500.             themeSettings[eAutoBSetting_color_off_gradientbottom] =         Game.GetColorFromRGB(200, 0, 0);
  501.             themeSettings[eAutoBSetting_color_on_gradienttop] =             Game.GetColorFromRGB(255, 249, 125);
  502.             themeSettings[eAutoBSetting_color_on_gradientbottom] =          Game.GetColorFromRGB(200, 200, 25);
  503.             themeSettings[eAutoBSetting_color_hover_gradienttop] =          Game.GetColorFromRGB(100, 50, 50);
  504.             themeSettings[eAutoBSetting_color_hover_gradientbottom] =       Game.GetColorFromRGB(25, 0, 0);
  505.             */
  506.             //AUTOMATED
  507.             themeSettings[eAutoBSetting_color_off_gradienttop] =            ChangeBrightness(themeSettings[eAutoBSetting_color_off],  1.5);
  508.             themeSettings[eAutoBSetting_color_off_gradientbottom] =         ChangeBrightness(themeSettings[eAutoBSetting_color_off],  0.5);
  509.             themeSettings[eAutoBSetting_color_on_gradienttop] =             ChangeBrightness(themeSettings[eAutoBSetting_color_on],  1.5);
  510.             themeSettings[eAutoBSetting_color_on_gradientbottom] =          ChangeBrightness(themeSettings[eAutoBSetting_color_on],  0.5);
  511.             themeSettings[eAutoBSetting_color_hover_gradienttop] =          ChangeBrightness(themeSettings[eAutoBSetting_color_hover],  1.5);
  512.             themeSettings[eAutoBSetting_color_hover_gradientbottom] =       ChangeBrightness(themeSettings[eAutoBSetting_color_hover],  0.5);
  513.            
  514.  
  515.             break;
  516.            
  517.         case eAutoButtonTheme_Blue :
  518.             AbortGame("NOT IMPLEMENTED (suit yourself)");
  519.            
  520.             break;
  521.     }
  522.    
  523.     return themeSettings;
  524. }
  525.  
  526. static int[] AutoButtons::GetTheme(AutoButtonThemes theme)
  527. {
  528.     int themeSettings[] = new int[THEMESIZE];
  529.    
  530.     if (theme < 0 || theme >= MAXTHEMES)
  531.         AbortGame("Not a valid theme number : %d", theme);
  532.    
  533.     for (int i=0; i< THEMESIZE; i++) {
  534.         themeSettings[i] = themes[theme].settings[i];
  535.     }
  536.    
  537.     return themeSettings;
  538. }
  539.  
  540.  //Overwrite an existing theme from your game, without requiring to temper with the module's script (if needed)
  541. static void AutoButtons::SetTheme(AutoButtonThemes theme,  int themeSettings[])
  542. {
  543.     if (theme < 0 || theme >= MAXTHEMES)
  544.         AbortGame("Not a valid theme number : %d", theme);
  545.    
  546.     for (int i=0; i< THEMESIZE; i++) {
  547.         themes[theme].settings[i] = themeSettings[i];
  548.     }
  549. }
  550.  
  551.  
  552.  
  553. void game_start()
  554. {
  555.     //depends on your game
  556.     guiMain = gMain; //this is a dirty hack. we should rely only on "Verbs" methods but it doesn't offer GetActionButton.
  557.    
  558.     //nbThemes = 0;
  559.     defaultTheme = eAutoButtonTheme_Red;
  560.    
  561.     //Init buttons constant width. This is normally ignored and replaced with buttons[].width because not all buttons have the same width
  562.     constants.height = BUTT_HEIGHT;
  563.     constants.width = BUTT_WIDTH;
  564.  
  565.     //Init built-in themes
  566.     int themeSettings[] = GetTheme_BuiltIn(eAutoButtonTheme_Red);
  567.     AutoButtons.SetTheme(eAutoButtonTheme_Red,  themeSettings);
  568.     //themeSettings[] = GetTheme_BuiltIn(eAutoButtonTheme_Blue);
  569.     //AutoButtons.SetTheme(eAutoButtonTheme_Blue,  themeSettings);
  570.    
  571.     //final init
  572.     themeSettings = AutoButtons.GetTheme(defaultTheme);
  573.     AutoButtons.GenerateAll(themeSettings);
  574. }
  575.  



Here is how to make it work with Tumbleweed :
(locate the code below in your Tumbleweed initialization code)
Code: Adventure Game Studio
  1.       // English - eLangEN  
  2.       /*
  3.         //WITHOUT AUTOBUTTONS : Set button graphics manually
  4.         odd_id=1407; even_id = odd_id+1;
  5.         Verbs.LocalizeActionButton(eLangEN,eGA_Open,    GetOddId(), GetEvenId(), 'q');
  6.         Verbs.LocalizeActionButton(eLangEN,eGA_Close,   GetOddId(), GetEvenId(), 'a');
  7.         Verbs.LocalizeActionButton(eLangEN,eGA_GiveTo,  GetOddId(), GetEvenId(), 'z');
  8.         Verbs.LocalizeActionButton(eLangEN,eGA_Push,    GetOddId(), GetEvenId(), 'e');
  9.         Verbs.LocalizeActionButton(eLangEN,eGA_Pull,    GetOddId(), GetEvenId(), 'd');
  10.         Verbs.LocalizeActionButton(eLangEN,eGA_Use,     GetOddId(), GetEvenId(), 'c');
  11.         Verbs.LocalizeActionButton(eLangEN,eGA_PickUp,  GetOddId(), GetEvenId(), 'w');
  12.         Verbs.LocalizeActionButton(eLangEN,eGA_LookAt,  GetOddId(), GetEvenId(), 's');
  13.         Verbs.LocalizeActionButton(eLangEN,eGA_TalkTo,  GetOddId(), GetEvenId(), 'x');
  14.       */
  15.         //WITH AUTOBUTTONS : Get the sprites IDs from the module; it generated thems for you.
  16.         eLanguage lan = eLangEN; int graphic_off; int graphic_on; Action a;
  17.         a = eGA_Open;
  18.         graphic_off = AutoButtons.GetSpriteID(lan, a, eAutoButton_off);
  19.         graphic_on = AutoButtons.GetSpriteID(lan, a, eAutoButton_on);
  20.         Verbs.LocalizeActionButton(lan, a,    graphic_off, graphic_on, 'q');
  21.        
  22.         a = eGA_Close;
  23.         graphic_off = AutoButtons.GetSpriteID(lan, a, eAutoButton_off);
  24.         graphic_on = AutoButtons.GetSpriteID(lan, a, eAutoButton_on);
  25.         Verbs.LocalizeActionButton(lan, a,    graphic_off, graphic_on, 'a');
  26.        
  27.         a = eGA_GiveTo;
  28.         graphic_off = AutoButtons.GetSpriteID(lan, a, eAutoButton_off);
  29.         graphic_on = AutoButtons.GetSpriteID(lan, a, eAutoButton_on);
  30.         Verbs.LocalizeActionButton(lan, a,    graphic_off, graphic_on, 'z');
  31.        
  32.         a = eGA_Push;
  33.         graphic_off = AutoButtons.GetSpriteID(lan, a, eAutoButton_off);
  34.         graphic_on = AutoButtons.GetSpriteID(lan, a, eAutoButton_on);
  35.         Verbs.LocalizeActionButton(lan, a,    graphic_off, graphic_on, 'e');
  36.        
  37.         a = eGA_Pull;
  38.         graphic_off = AutoButtons.GetSpriteID(lan, a, eAutoButton_off);
  39.         graphic_on = AutoButtons.GetSpriteID(lan, a, eAutoButton_on);
  40.         Verbs.LocalizeActionButton(lan, a,    graphic_off, graphic_on, 'd');
  41.        
  42.         a = eGA_Use;
  43.         graphic_off = AutoButtons.GetSpriteID(lan, a, eAutoButton_off);
  44.         graphic_on = AutoButtons.GetSpriteID(lan, a, eAutoButton_on);
  45.         Verbs.LocalizeActionButton(lan, a,    graphic_off, graphic_on, 'c');
  46.        
  47.         a = eGA_PickUp;
  48.         graphic_off = AutoButtons.GetSpriteID(lan, a, eAutoButton_off);
  49.         graphic_on = AutoButtons.GetSpriteID(lan, a, eAutoButton_on);
  50.         Verbs.LocalizeActionButton(lan, a,    graphic_off, graphic_on, 'w');
  51.        
  52.         a = eGA_LookAt;
  53.         graphic_off = AutoButtons.GetSpriteID(lan, a, eAutoButton_off);
  54.         graphic_on = AutoButtons.GetSpriteID(lan, a, eAutoButton_on);
  55.         Verbs.LocalizeActionButton(lan, a,    graphic_off, graphic_on, 's');
  56.        
  57.         a = eGA_TalkTo;
  58.         graphic_off = AutoButtons.GetSpriteID(lan, a, eAutoButton_off);
  59.         graphic_on = AutoButtons.GetSpriteID(lan, a, eAutoButton_on);
  60.         Verbs.LocalizeActionButton(lan, a,    graphic_off, graphic_on, 'x');
  61.        
  62.  
« Last Edit: 31 Jan 2019, 17:31 by Monsieur OUXX »