MODULE: TypedText 1.0.0

Started by Crimson Wizard, Sun 12/02/2017 22:53:14

Previous topic - Next topic

Phemar

This is really cool, Crimson! Glad to see a more fleshed out version of the typewriter module. Awesome work.

Aaron Best

Hi apologies with the delayed response...

No there's certainly no [ in the text - however adding a space after the final word has seemed to resolve the issue.

Thank you for your assistance.

Crimson Wizard

Quote from: Except Pigs on Wed 27/12/2017 22:21:26
No there's certainly no [ in the text - however adding a space after the final word has seemed to resolve the issue.

Could you give an example of text that caused this? Does this happen with any text or particular one? Depends on font? This information would be very useful to prevent this error in the future.

Cassiebsg

Uhm, I think it happens with all text in the module, maybe?
I remember adding the space on my lines, cause I was experiencing the same problem with the line breaking (which is why I suggested the space at the end).
I didn't thought much about it at the time. I was using ttf font if that makes a difference.
There are those who believe that life here began out there...

eri0o

Hey, two minor things

  • in the first example, it should be SayBackground, with the S upper case
  • in the module code, the alignment centre is now center in 3.6.0, so there's I think three lines that need to be updated in the code - I think it's the renderer code

Used the module for the first time, it's really good!

Crimson Wizard

#45
Updated TypedText modules to 1.0.0

After several years of not touching this module, I fixed couple of known bugs, added fixes for changed script commands to ease usage in AGS 3.5.0 and later, and replaced download links from bitbucket to github (where my script modules now reside).

* Fixed that if text does not end with any of the "split" characters (any punctuation by default), then the last word will be unnecessarily wrapped onto the next line.
* Fixed if drawing surface or control's height is too small, there will be no text typed at all, which may confuse users.
* Provided compat fixes for compiling with AGS 3.5.0 and higher.

Download module pack: https://github.com/ivan-mogilko/ags-script-modules/releases/download/typedtext-1.0.0/TypedTextPack.zip
Download demo game: https://github.com/ivan-mogilko/ags-script-modules/releases/download/typedtext-1.0.0/TypedTextDemo_1.0.0.zip

On another topic, the original documentation for this module is probably overcomplicated (as most of my attempts to document my work). A while ago I did an effort to write better docs for my DragDrop module, perhaps if I manage to find spare time I could do same for this module.

Also, this module maybe should be rewritten for AGS 4.0, where scripts have more features, and many things could be scripted easier and in a more elegant way.

TypewriterTextLover

Hello! I am complete noob and this is my first post! I have been messing around with the TypedText1.00 demo and module for a few days and struggling to figure out a few things.

I have been able to successfully get words to type on a object (dynamic sprite that looks like a piece of paper) just like in the demo. However the typewriter always seems "active" and ttdraw is never in a "waiting for reader" or "idle" stage. I can't figure out how to get it to stop! I understand it's being repeatedly executed to create the typing effect with ttdraw.tick.

I have tried using ClearTypewriter (usually get error messages with this since it deletes the sprite), ttdraw.Clear (which does clear the text but doesn't allow time for the ttdraw.Start(String) to finish. I have tried using timers to get around this with no success. I have also tried messing with ttdraw.TextReadTime which doesn't seem to have any effect.

I basically want to :

- Look at an hotspot or object.
- Typewriter starts typing word description on paper.
- If the words are too many for the text box: stop, let user click when done reading, user clicks, text clears, and typewriter continues writing from top.
- Click on paper to skip to the next text section.

I am sticking with TypedTextDrawing since it's the only feature that supports caret sprites. I have it working near perfectly, but just need to figure out this one final part. Any help is greatly appreciated!!! Here is my code. This code does not produce any errors, but doesn't do what I want either :) It's mostly the same from the demo. I have tried incorporating some of the PhylactereTypewriter code (since I believe it also uses dynamic sprites) which maybe has the solution to the problem there, but I couldn't figure it out. I think getting the typewriter to idle is the first step.

Code: ags
// room script file

// Compatibility macros
#ifndef SCRIPT_API_v350
  #define eAlignCenter eAlignCentre
#endif

TypedTextDrawing ttdraw;
DynamicSprite *papersprite;
int paper_original_pos;

void ClearTypewriter()
{
if (papersprite != null)
   papersprite.Delete();
  papersprite = null;
  ttdraw.Clear();
  oPaper.Y = paper_original_pos;
 }

void StartTypewriter()
{ oPaper.Visible=true;
 ClearTypewriter();
  
  papersprite = DynamicSprite.CreateFromExistingSprite(369);
  oPaper.Graphic = papersprite.Graphic;
 
  ttdraw.X = 2;
  ttdraw.Y = 4;
  ttdraw.LineSpacing = 5;
  ttdraw.Width = papersprite.Width;
  ttdraw.Height = papersprite.Height;
  ttdraw.Font = eFontPressStart2P;
  ttdraw.BkgColor = Game.GetColorFromRGB(226,175,143);
  ttdraw.TextColor = Game.GetColorFromRGB(0, 0, 0);
  ttdraw.TextAlign = eAlignLeft;
  ttdraw.CaretSprite = 79;
  ttdraw.TypeDelayMin = 2;
  ttdraw.TypeDelayMax = 4;
  ttdraw.TypeDelayStyle = eTypedDelay_Mixed;
  ttdraw.CaretFlashOnTime = 9;
  ttdraw.CaretFlashOffTime = 9;
  ttdraw.CaretStyle = eTypedCaret_Explicit;
  
  AudioClip *type_clips[] = new AudioClip[3];
  type_clips[0] = aType;
  type_clips[1] = aType2;
  type_clips[2] = aType3;
  ttdraw.SetRandomTypeSounds(type_clips, 3);
  ttdraw.CaretSound = aCaret;
}

void UpdateTypewriter()
{
  if (ttdraw.IsActive)
  {
    ttdraw.Tick();
    DrawingSurface *ds = papersprite.GetDrawingSurface();
    ttdraw.Draw(ds);
    ds.Release();
    // oPaper.Y = paper_original_pos -  * (ttdraw.LineHeight + ttdraw.LineSpacing);
    oPaper.Graphic = papersprite.Graphic;
  }
  }

void PaperLines()
{
gGuiLine1.Visible=true;
gGuiLine2.Visible=true;
}

function room_Load()
{
  paper_original_pos = oPaper.Y;
  gGuiIntro.Visible=false;
  Verbs.HideGui();
  StartTypewriter();
  ttdraw.Start("Your sister's kids are here to celebrate your birthday!");
  aHaunted_House.Play();
}

function oPaper_AnyClick()
{
  if (ttdraw.IsActive && !ttdraw.IsIdle)
  {
    ttdraw.Skip();
    // oPaper.Y = paper_original_pos - ttdraw.LinesDisplayed * (ttdraw.LineHeight + ttdraw.LineSpacing);
  }
  else
  {
    oPaper.Visible=false;
      gGuiLine1.Visible=false;
    gGuiLine2.Visible=false;
    Verbs.ShowGui();
  }
}

function room_RepExec()
{
  UpdateTypewriter();
}

function hHotspotBabyEmmie_Look(Hotspot *theHotspot, CursorMode mode)
{
Verbs.HideGui();
  PaperLines();
  StartTypewriter();
ttdraw.Start("Look it's Baby Emmie! Awwww! How cute! Blah blah blah blah blah blah blah blah blah blah blah");
}




Crimson Wizard

#47
Quote from: TypewriterTextLover on Thu 05/09/2024 03:32:53- If the words are too many for the text box: stop, let user click when done reading, user clicks, text clears, and typewriter continues writing from top.
- Click on paper to skip to the next text section.

This module itself does not split text into "sections" or "pages", it has no such functionality. It only splits into lines according to the defined "width" of a text space.
If you like to display a longer text split into pages, where a text space is filled to max, waits, and then starts typing next page, then you should script this yourself on top of the typewriter object. You need some custom system that:
- accepts a input text
- calculates how much text lines can fit into the typewriter's space at once (see functions like GetTextWidth, GetTextHeight, etc)
- splits this input text into substrings, each representing a "page", and stores these in a String array
- remembers which page is currently being printed
- lets to order next page, in which case it stops current typing and starts it again with the next string from array.
- lets to know if this is the last page.


My memories of the demo game are rather vague (it's been several years), but I think I simply hardcoded these "pages" there, they are not calculated by size. So that approach from demo game won't be enough.

TypewriterTextLover

Thanks Crimson Wizard for the quick response and thank you for creating such a great module! This module has over 700,000 views which shows you how desired this feature is to people! The # 1 most viewed module!

Is there any way to get the typewriter to be in a "Waiting for user" or "IsIdle" state? I have used Display commands to see if it ever goes in a Idle status and it does not. If the typewriter would idle, i could simply say if typewriter idle, then clear, and start typing new string.

Another possible method maybe uses the "[" character. Is there way to say "if typewriter typed "[", then clear typewriter, and type new string? I believe there is a ttdraw.LastChar parameter that maybe useful?


Crimson Wizard

#49
Quote from: TypewriterTextLover on Thu 05/09/2024 16:17:23Thanks Crimson Wizard for the quick response and thank you for creating such a great module! This module has over 700,000 views which shows you how desired this feature is to people! The # 1 most viewed module!

Well, thank you, although even though there are many views, I never had statistics about actual number of uses.


Quote from: TypewriterTextLover on Thu 05/09/2024 16:17:23Is there any way to get the typewriter to be in a "Waiting for user" or "IsIdle" state? I have used Display commands to see if it ever goes in a Idle status and it does not. If the typewriter would idle, i could simply say if typewriter idle, then clear, and start typing new string.

Indeed, that's how it is supposed to work.
If you open the Demo Game that is distributed along with the module, you may add a small test for the big typewriter machine:
- Add some kind of a "status bar" gui with a label, for example on top of the screen.
- Put this inside "void UpdateTypewriter()" function in a room script:
Code: ags
Label1.Text = String.Format("ttdraw active: %d, idle %d, wait %d", ttdraw.IsActive, ttdraw.IsIdle, ttdraw.IsWaitingForReader);
- Then run the game and click on a typewriter's keyboard.
You will notice that:
- At game start "active" is false, and "idle" is true.
- After you start it, "active" becomes true and "idle false.
- As soon as it is done typing, "idle" becomes true.

It does work in the demo game. The question then is how to use this properly in your case.


Quote from: TypewriterTextLover on Thu 05/09/2024 16:17:23Another possible method maybe uses the "[" character. Is there way to say "if typewriter typed "[", then clear typewriter, and type new string? I believe there is a ttdraw.LastChar parameter that maybe useful?

I would not recommend that, the "[" character is used to wrap lines, and may be typed multiple times during the same text.

TypewriterTextLover

I used your line of code and it does indeed idle!!! I still can't get it clear the page by using it's idle status.

I have changed the void UpdateTypewriter() line to reflect if the typewriter is idle or !idle since it's only inactive once before use which isn't really helpful.
It also never enters a "wait for reader" status, although I'm not sure if that is an issue or not.

Here is what I changed it to, although it doesn't work:

Code: ags
void UpdateTypewriter()
{
  if (!ttdraw.IsIdle)
  {
    ttdraw.Tick();
    DrawingSurface *ds = papersprite.GetDrawingSurface();
    ttdraw.Draw(ds);
    ds.Release();
    //oPaper.Y = paper_original_pos - ttdraw.LinesDisplayed * (ttdraw.LineHeight + ttdraw.LineSpacing);
    oPaper.Graphic = papersprite.Graphic;
    Label1.Text = String.Format("ttdraw active: %d, idle %d, wait %d", ttdraw.IsActive, ttdraw.IsIdle, ttdraw.IsWaitingForReader);
  }
  
  else
  {
    ttdraw.Clear();
  }
}

Doesn't seem like ttdraw.Clear isn't doing what it needs to do here? I've tried a couple of different things, but I'm stumped! This is the code for ttdraw.Clear but I can't seem to figure out if it maybe needs changing?

Code: ags
//===========================================================================
//
// TypedTextDrawing::Clear
//
//===========================================================================
void TypedTextDrawing::Clear()
{
  this._RenderClear();
  this._lineCount = 0;
}

I appreciate your time and help!

Crimson Wizard

#51
Quote from: TypewriterTextLover on Thu 05/09/2024 18:58:23It also never enters a "wait for reader" status, although I'm not sure if that is an issue or not.

This "wait for reader" state is entered if the text ended typing, but the typewriter "thinks" that player did not finish reading yet. This is achieved by roughly calculating amount of time necessary to read whole text, multiplying text's length by TextReadTime parameter. If text is typed slower than the resulting reading time, then "waiting" state will never be entered.


Quote from: TypewriterTextLover on Thu 05/09/2024 18:58:23Here is what I changed it to, although it doesn't work:

Doesn't seem like ttdraw.Clear isn't doing what it needs to do here?

What does it not doing? what is the behavior you are trying to achieve with this code?

Note that Clear() does not clear the sprite, it resets the typewriter state. If you like sprite to be cleared, that would be:
Code: ags
    DrawingSurface *ds = papersprite.GetDrawingSurface();
    ds.Clear( some color );
    ds.Release();


EDIT: Also, you have this line with label text under "if (!idle)" condition, so it will never display idle off (if that matters).

TypewriterTextLover

I was able to get the page to clear by changing the void UpdateTypewriter() by using your code:

Code: ags
void UpdateTypewriter()
{
  if (ttdraw.IsActive && !ttdraw.IsIdle)
  {
    ttdraw.Tick();
    DrawingSurface *ds = papersprite.GetDrawingSurface();
    ttdraw.Draw(ds);
    ds.Release();
    //oPaper.Y = paper_original_pos - ttdraw.LinesDisplayed * (ttdraw.LineHeight + ttdraw.LineSpacing);
    oPaper.Graphic = papersprite.Graphic;
    Label1.Text = String.Format("ttdraw active: %d, idle %d, wait %d", ttdraw.IsActive, ttdraw.IsIdle, ttdraw.IsWaitingForReader);
  }
  if (ttdraw.IsActive && ttdraw.IsIdle)
  {
    ttdraw.Tick();
    DrawingSurface *ds = papersprite.GetDrawingSurface();
    ds.Clear(65535);
    ds.Release();
    oPaper.Graphic = papersprite.Graphic;
  }
}

However now I want to be able to display two strings back to back, so it looks like two different pages of text are being typed. I tried using this code, but it just ignores the first string and prints only the second:

Code: ags

  void StartTypewriter()
{
  ClearTypewriter();
  SayCustom("Launch the big typewriter!");
  
  papersprite = DynamicSprite.CreateFromExistingSprite(28);
  oPaper.Graphic = papersprite.Graphic;
  
  //Display("paper size = %d x %d", papersprite.Width, papersprite.Height);
  ttdraw.X = 4;
  ttdraw.Y = 4;
  ttdraw.Width = papersprite.Width - 8;
  ttdraw.Height = papersprite.Height - 8;
  ttdraw.Font = eFontFont3;
  ttdraw.BkgColor = Game.GetColorFromRGB(255, 255, 255);
  ttdraw.TextColor = Game.GetColorFromRGB(66, 44, 123);
  ttdraw.TextAlign = eAlignCenter;
  ttdraw.CaretSprite = 29;
  ttdraw.TypeDelayMin = 4;
  ttdraw.TypeDelayMax = 7;
  ttdraw.TypeDelayStyle = eTypedDelay_Mixed;
  ttdraw.CaretFlashOnTime = 9;
  ttdraw.CaretFlashOffTime = 9;
  ttdraw.CaretStyle = eTypedCaret_Explicit;
  
  AudioClip *type_clips[] = new AudioClip[3];
  type_clips[0] = aType;
  type_clips[1] = aType2;
  type_clips[2] = aType3;
  ttdraw.SetRandomTypeSounds(type_clips, 3);
  ttdraw.CaretSound = aCaret;
  
  String big_string = "The TypedTextDrawing prints TypedText on the provided DrawingSurface. Therefore";
  String big_string2 = "Furthermore, you may assign sprite number to CaretSprite property, and it will be used to draw caret position.";
  
  ttdraw.Start(big_string);
  ttdraw.Start(big_string2);
}


I tried looking at string arrays and looked over footlines and guybrushlines but I can't figure it out. :(

Crimson Wizard

#53
Well, at this point this becomes a generic coding problem. You need to run two commands, but not immediately, because the second will overwrite the first one. Instead you need to run the second command after you learn that the first one completes.

Code: ags
  if (ttdraw.IsActive && ttdraw.IsIdle)
  {
    ttdraw.Clear();
    DrawingSurface *ds = papersprite.GetDrawingSurface();
    ds.Clear(65535);
    ds.Release();
    oPaper.Graphic = papersprite.Graphic;

    <------------ HERE
  }

How to do that?
Make an array of strings of certain limit.
Make a function that schedules next string.
Keep record of current string.


Following is the most basic example that I may come with. You may use this directly, or as a reference for your own code, and expand from there:
Code: ags
#define MAX_PAGES 100
String PageStrings[MAX_PAGES];
int TotalPages = 0; // remember how many pages scheduled
int CurrentPage = -1; // remember which is the current page

function ClearPages()
{
    TotalPages = 0;
    CurrentPage = -1;
}

function AddPage(String text)
{
    if (TotalPages == MAX_PAGES)
        return; // no more place to store pages
    PageStrings[TotalPages] = text;
    TotalPages++;
}

function StartTypeNextPage()
{
    if (CurrentPage < TotalPages - 1) // has more pages to display?
    {
        CurrentPage++;
        ttdraw.Start(PageStrings[CurrentPage]);
    }
}

And then in UpdateTypewriter():

Code: ags
  if (ttdraw.IsActive && ttdraw.IsIdle)
  {
    ttdraw.Clear();
    DrawingSurface *ds = papersprite.GetDrawingSurface();
    ds.Clear(65535);
    ds.Release();
    oPaper.Graphic = papersprite.Graphic;

    StartTypeNextPage();
  }


Use AddPage(text) to schedule a number of pages, then call StartTypeNextPage() to start with the first page whenever you need to.

TypewriterTextLover

IT WORKS!! I added some Waits and and a little bit of TextReaderType to make it work buttery smooth. Clicking on the typewriter now starts it. Clicking on the page will skip to the next page. This demo can print out books now!!! :)

Check it out here is the code for all that need it!!!

Code: ags
// room script file
// Compatibility macros

#ifndef SCRIPT_API_v350
  #define eAlignCenter eAlignCentre
#endif

#define MAX_PAGES 100

String PageStrings[MAX_PAGES];
int TotalPages = 0; // remember how many pages scheduled
int CurrentPage = -1; // remember which is the current page

TypedTextDrawing ttdraw;
DynamicSprite *papersprite;
int paper_original_pos;

function ClearPages()
{
    TotalPages = 0;
    CurrentPage = -1;
}

function AddPage(String text)
{
    if (TotalPages == MAX_PAGES)
        return; // no more place to store pages
    PageStrings[TotalPages] = text;
    TotalPages++;
}

function StartTypeNextPage()
{   if (CurrentPage < TotalPages - 1) // has more pages to display?
    {
        CurrentPage++;
        ttdraw.Start(PageStrings[CurrentPage]);
    }
}

void ClearTypewriter()
{
  if (papersprite != null)
    papersprite.Delete();
  papersprite = null;
  ttdraw.Clear();
  oPaper.Y = paper_original_pos;
}

void StartTypewriter()
{
  ClearTypewriter();
  SayCustom("Launch the big typewriter!");
  
  papersprite = DynamicSprite.CreateFromExistingSprite(28);
  oPaper.Graphic = papersprite.Graphic;
  
  //Display("paper size = %d x %d", papersprite.Width, papersprite.Height);
  ttdraw.X = 4;
  ttdraw.Y = 4;
  ttdraw.Width = papersprite.Width - 8;
  ttdraw.Height = papersprite.Height - 8;
  ttdraw.Font = eFontFont3;
  ttdraw.BkgColor = Game.GetColorFromRGB(255, 255, 255);
  ttdraw.TextColor = Game.GetColorFromRGB(66, 44, 123);
  ttdraw.TextAlign = eAlignCenter;
  ttdraw.CaretSprite = 29;
  ttdraw.TypeDelayMin = 4;
  ttdraw.TypeDelayMax = 7;
  ttdraw.TypeDelayStyle = eTypedDelay_Mixed;
  ttdraw.CaretFlashOnTime = 9;
  ttdraw.CaretFlashOffTime = 9;
  ttdraw.CaretStyle = eTypedCaret_Explicit;
  ttdraw.TextReadTime = 3;
  
  AudioClip *type_clips[] = new AudioClip[3];
  type_clips[0] = aType;
  type_clips[1] = aType2;
  type_clips[2] = aType3;
  ttdraw.SetRandomTypeSounds(type_clips, 3);
  ttdraw.CaretSound = aCaret;
  
  ttdraw.Start("Thank you Crimson Wizard [ !!!!!!!!!!!");
  AddPage("You are the bestest!!!! THANK YOU!!!!!!!!!!");
  AddPage("Best Module of all time! 700,000 Views Baby!!!");
}

void UpdateTypewriter()
{
  if (ttdraw.IsActive && !ttdraw.IsIdle)
  {
    ttdraw.Tick();
    DrawingSurface *ds = papersprite.GetDrawingSurface();
    ttdraw.Draw(ds);
    ds.Release();
    //oPaper.Y = paper_original_pos - ttdraw.LinesDisplayed * (ttdraw.LineHeight + ttdraw.LineSpacing);
    oPaper.Graphic = papersprite.Graphic;
    Label1.Text = String.Format("ttdraw active: %d, idle %d, wait %d", ttdraw.IsActive, ttdraw.IsIdle, ttdraw.IsWaitingForReader);
  }
  if (ttdraw.IsActive && ttdraw.IsIdle)
  {
    Wait(60);
    ttdraw.Clear();
    DrawingSurface *ds = papersprite.GetDrawingSurface();
    ds.Clear(65535);
    ds.Release();
    oPaper.Graphic = papersprite.Graphic;
     
    StartTypeNextPage();
  }
}

function room_Load()
{
  paper_original_pos = oPaper.Y;
  cGuybrush.ManualScaling = true;
  cGuybrush.Scaling = 200;
  cGuybrush.FaceLocation(cGuybrush.x + 100, cGuybrush.y, eNoBlock);
}

function oTypewriter_AnyClick()
{
    StartTypewriter();
}

function oPaper_AnyClick()
{
  if (ttdraw.IsActive && !ttdraw.IsIdle)
  {
    ttdraw.Skip();
    //oPaper.Y = paper_original_pos - ttdraw.LinesDisplayed * (ttdraw.LineHeight + ttdraw.LineSpacing);
  }
}

function room_RepExec()
{
  UpdateTypewriter();
}



I truly appreciate your help Crimson Wizard!

"Thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you, thank you."

-Kielbasa, thanking the DS-86 crew

TypewriterTextLover

#55
Hey Crimson Wizard! I'm back with another question!  :grin:

I've noticed a potential weird bug with ttdraw.Skip();

If more than ~ 50% of the text has already been typed, and then you try to skip it, the typewriter will just pause and hang instead of displaying the rest of the intended message.

I've looked over the code for ttdraw.Skip which is:

Code: ags
void TypedText::Skip()
{
  this._cur = this._full;
  this._justTyped = false;
  this._hasChanged = true;

Not sure what I could change to fix this problem. Maybe this has something to do with how it calculates split text? Any ideas? Thank you.

Crimson Wizard,

Thank you for the quick reply. Replace the room code with this code to replicate the error. It doesn't happen every time but when the text comes up that says "Best Module of all time! 700,000 Views Baby!!!"
If you try to skip by clicking on the paper between the word "all" and "time" it should hang. It sometimes hangs if you click on the paper right after it types "Crimson" on the first sentence.

Code: ags
// room script file
// Compatibility macros

#ifndef SCRIPT_API_v350
  #define eAlignCenter eAlignCentre
#endif

#define MAX_PAGES 100

String PageStrings[MAX_PAGES];
int TotalPages = 0; // remember how many pages scheduled
int CurrentPage = -1; // remember which is the current page

TypedTextDrawing ttdraw;
DynamicSprite *papersprite;
int paper_original_pos;

function ClearPages()
{
    TotalPages = 0;
    CurrentPage = -1;
}

function AddPage(String text)
{
    if (TotalPages == MAX_PAGES)
        return; // no more place to store pages
    PageStrings[TotalPages] = text;
    TotalPages++;
}

function StartTypeNextPage()
{   if (CurrentPage < TotalPages - 1) // has more pages to display?
    {
        CurrentPage++;
        ttdraw.Start(PageStrings[CurrentPage]);
    }
}

void ClearTypewriter()
{
  if (papersprite != null)
    papersprite.Delete();
  papersprite = null;
  ttdraw.Clear();
  oPaper.Y = paper_original_pos;
}

void StartTypewriter()
{
  ClearTypewriter();
  SayCustom("Launch the big typewriter!");
  
  papersprite = DynamicSprite.CreateFromExistingSprite(28);
  oPaper.Graphic = papersprite.Graphic;
  
  //Display("paper size = %d x %d", papersprite.Width, papersprite.Height);
  ttdraw.X = 4;
  ttdraw.Y = 4;
  ttdraw.Width = papersprite.Width - 8;
  ttdraw.Height = papersprite.Height - 8;
  ttdraw.Font = eFontFont3;
  ttdraw.BkgColor = Game.GetColorFromRGB(255, 255, 255);
  ttdraw.TextColor = Game.GetColorFromRGB(66, 44, 123);
  ttdraw.TextAlign = eAlignCenter;
  ttdraw.CaretSprite = 29;
  ttdraw.TypeDelayMin = 4;
  ttdraw.TypeDelayMax = 7;
  ttdraw.TypeDelayStyle = eTypedDelay_Mixed;
  ttdraw.CaretFlashOnTime = 9;
  ttdraw.CaretFlashOffTime = 9;
  ttdraw.CaretStyle = eTypedCaret_Explicit;
  ttdraw.TextReadTime = 3;
  
  AudioClip *type_clips[] = new AudioClip[3];
  type_clips[0] = aType;
  type_clips[1] = aType2;
  type_clips[2] = aType3;
  ttdraw.SetRandomTypeSounds(type_clips, 3);
  ttdraw.CaretSound = aCaret;
  
  ttdraw.Start("Thank you Crimson Wizard [ !!!!!!!!!!!");
  AddPage("You are the bestest!!!! THANK YOU!!!!!!!!!!");
  AddPage("Best Module of all time! 700,000 Views Baby!!!");
}

void UpdateTypewriter()
{
  if (ttdraw.IsActive && !ttdraw.IsIdle)
  {
    ttdraw.Tick();
    DrawingSurface *ds = papersprite.GetDrawingSurface();
    ttdraw.Draw(ds);
    ds.Release();
    //oPaper.Y = paper_original_pos - ttdraw.LinesDisplayed * (ttdraw.LineHeight + ttdraw.LineSpacing);
    oPaper.Graphic = papersprite.Graphic;
    Label1.Text = String.Format("ttdraw active: %d, idle %d, wait %d", ttdraw.IsActive, ttdraw.IsIdle, ttdraw.IsWaitingForReader);
  }
  if (ttdraw.IsActive && ttdraw.IsIdle)
  {
    Wait(60);
    ttdraw.Clear();
    DrawingSurface *ds = papersprite.GetDrawingSurface();
    ds.Clear(65535);
    ds.Release();
    oPaper.Graphic = papersprite.Graphic;
     
    StartTypeNextPage();
  }
}

function room_Load()
{
  paper_original_pos = oPaper.Y;
  cGuybrush.ManualScaling = true;
  cGuybrush.Scaling = 200;
  cGuybrush.FaceLocation(cGuybrush.x + 100, cGuybrush.y, eNoBlock);
}

function oTypewriter_AnyClick()
{
    StartTypewriter();
}

function oPaper_AnyClick()
{
  if (ttdraw.IsActive && !ttdraw.IsIdle)
  {
    ttdraw.Skip();
    //oPaper.Y = paper_original_pos - ttdraw.LinesDisplayed * (ttdraw.LineHeight + ttdraw.LineSpacing);
  }
}

function room_RepExec()
{
  UpdateTypewriter();
}




Crimson Wizard

Quote from: TypewriterTextLover on Thu 12/09/2024 04:31:44Replace the room code with this code to replicate the error. It doesn't happen every time but when the text comes up that says "Best Module of all time! 700,000 Views Baby!!!"
If you try to skip by clicking on the paper between the word "all" and "time" it should hang. It sometimes hangs if you click on the paper right after it types "Crimson" on the first sentence.

This is not the module that hangs, it is the Wait(60); command that you have in your script that makes it wait for 1 second before going to the next page.

Skipping makes full text displayed, which also results in Idle state, because it's done typing.
Your own code detects Idle state, waits for 60 ticks, and starts typing next page.

You need to modify your code to let it:
- redraw once more
- maybe wait more before going to the next page after Idle, to let player read the whole page, or wait for another player's clicks, depending on how you want it to be done.

I also recommend to not use Wait() command here, but instead set up a Timer, because Wait suspends all player's interaction.

TypewriterTextLover

Crimson Wizard,

I took the Wait(60); out of the demo and it does fix the hanging problem with the text in the demo. It still hangs in my game but only on the last page of text when you try to skip even when I take the Wait(60); out. My code has gotten rather complicated because of one issue:

The typewriter can not seem to interact with inventory items since they are called from the global script and not the room script. To get around this I have have two typewriters going now. One in the room script and one in the global script. I think they are conflicting with each other and causing the hanging problem still. I have looked up importing and exporting to try to call the inventory items from the room script with no luck. Basically what I'm trying to do is if I look at a inventory item, the typewriter will type out a description.

I also have no idea how to get the pages to stop and wait for a mouse click before displaying. I'm going to look into it further and try to mess around with timers although I'd rather have a mouse click to skip text and then wait for user mouse click input again to go to next page.

I'm also using the TUMBLEWEED template which uses a lot of repeatedly executable code which maybe is causing a conflict as well maybe.

I am making a game where it has one central area where all the text is displayed when you look, pickup, etc. All of the text is typewritten like a MacVenture style game.

I appreciate all of your help!

Crimson Wizard

#58
It sounds like you need to move all this code to the global script then.
Since it has to be displayed in the same way throughout the game, then it should not be displayed on a room object, but on something else, like a GUI, or screen overlay. Both may have a sprite assigned as a background graphic (check the manual for GUI properties or screen overlays).
I recommend GUI, because it's easier to configure and work with (imho).

Quote from: TypewriterTextLover on Thu 12/09/2024 21:23:27I also have no idea how to get the pages to stop and wait for a mouse click before displaying. I'm going to look into it further and try to mess around with timers although I'd rather have a mouse click to skip text and then wait for user mouse click input again to go to next page.

Simplify UpdateTypewriting, you do not need 2 cases, only handle IsActive case:
Code: ags
void UpdateTypewriter()
{
  if (ttdraw.IsActive)
  {
    ttdraw.Tick();
    DrawingSurface *ds = papersprite.GetDrawingSurface();
    ttdraw.Draw(ds);
    ds.Release();
    oPaper.Graphic = papersprite.Graphic;
  }
  // TEST STRING
  Label1.Text = String.Format("ttdraw active: %d, idle %d, wait %d", ttdraw.IsActive, ttdraw.IsIdle, ttdraw.IsWaitingForReader);
}

Change the Click function to do different things depending on current state:
- if still typing then skip
- if finished typing, then go to next page
Code: ags
function oPaper_AnyClick()
{
  if (ttdraw.IsActive && !ttdraw.IsIdle)
  {
    ttdraw.Skip();
  }
  else if (ttdraw.IsIdle)
  {
    DrawingSurface *ds = papersprite.GetDrawingSurface();
    ds.Clear(65535);
    ds.Release();
    oPaper.Graphic = papersprite.Graphic;
    StartTypeNextPage();
  }
}

Something like that should do it.

TypewriterTextLover

I'm having some issues getting the typewriter to work in the global script. I'm not sure what to put in the header and not sure the correct nomenclature for importing functions.
This is what I added to the global header:
Code: ags
import void StartTypewriter();
import function AddPage(String text);
import function ClearPages();
import void PaperLinesShow();
import void PaperLinesHide();

#ifndef SCRIPT_API_v350
  #define eAlignCenter eAlignCentre
#endif

#define MAX_PAGES 100

String PageStrings[MAX_PAGES];
int TotalPages = 0; // remember how many pages scheduled
int CurrentPage = -1; // remember which is the current page

TypedTextDrawing ttdraw;
DynamicSprite *papersprite;
int paper_original_pos;

The typewriter currently shows the first page as being blank. No typewriting sounds. If I click the paper, it will type out the second page.
I'm also not sure what the point of int paper_original_pos and gGuiPaper.Y = paper_original_pos; If I use this line of code it makes my GUI paper appear super high up on the screen. So I put // in front of it.

I think maybe I've written the import Start Typewriter portion wrong.

Thank you for your time Crimson. I'm struggling over here!!  (laugh)  (laugh)  (laugh)

SMF spam blocked by CleanTalk