[solved] How do I tint a character portrait?

Started by eri0o, Fri 01/06/2018 15:05:35

Previous topic - Next topic

eri0o

I need to tint a character portrait - single frame speech view, and no limp sync and no blinking.

I have thought about, but I am not sure how to do it. :(

Crimson Wizard

Clone portrait into dynamic sprite, then DynamicSprite.Tint, then set ViewFrame.Graphic to one of dynamic sprite.

eri0o

#2
So something like below?

Code: ags

int originalGraphic;
DynamicSprite * dyn_spr; 
 
void tintPortrait(Character * chr, int r, int g, int b, int s,  int l){
  ViewFrame * chr_vf = Game.GetViewFrame(chr.SpeechView, 0, 0);
  originalGraphic = chr_vf.Graphic;
  dyn_spr = DynamicSprite.CreateFromExistingSprite(originalGraphic, true);
  dyn_spr.Tint(r, g, b, s, l);
  chr_vf.Graphic = dyn_spr.Graphic;
}
 
void untintPortrait(Character * chr){
  ViewFrame * chr_vf = Game.GetViewFrame(chr.SpeechView, 0, 0);
  
  chr_vf.Graphic = originalGraphic;
  dyn_spr.Delete();
}


But now I need to track the original graphic since tint is definitive and clean the dyn_spr before exiting game to avoid warning logs. But since it's only at some very isolated points, it will be ok.

Crimson Wizard

Quote from: eri0o on Fri 01/06/2018 15:54:22
But now I need to track the original graphic since tint is definitive and clean the dyn_spr before exiting game to avoid warning logs.

warning.log is only created in Debug build, that's notification for developer only.
There is no 100% guaranteed way to clean dynamic sprites  on exit as far as I know, because user may hit alt+X or click on the close window button, which cannot be detected in script.

eri0o

Ah ok, since it's only on Debug build it's alright :]

eri0o

Spoiler
Code: ags

int originalGraphic;
DynamicSprite * dyn_spr; 
 
void tintPortrait(Character * chr, int r, int g, int b, int s,  int l){
  ViewFrame * chr_vf = Game.GetViewFrame(chr.SpeechView, 0, 0);
  if(originalGraphic==0){
    originalGraphic = chr_vf.Graphic;
  }
  dyn_spr = DynamicSprite.CreateFromExistingSprite(originalGraphic, true);
  dyn_spr.Tint(r, g, b, s, l);
  chr_vf.Graphic = dyn_spr.Graphic;
}
 
void untintPortrait(Character * chr){
  ViewFrame * chr_vf = Game.GetViewFrame(chr.SpeechView, 0, 0);
  
  chr_vf.Graphic = originalGraphic;
  dyn_spr.Delete();
}

void repeatedly_execute_always(){
  if(player.Speaking) tintPortrait(player, Random(255), Random(255), Random(255), 50, 100);  
}
[close]

Of course, let me try to add repeatedly_execute_always to the mix. I have a problem that it doesn't work, the graphics for the portrait is updated in a different rate then the rest of the screen, and after 3 seconds it doesn't update anymore.

I created a minimum game to reproduce this bug, download it here. :X

Crimson Wizard

Did you check if player.Speaking is still true when it stops updating?
It looks like it returns false when speech is waiting for the skip key.

eri0o

I removed the if player.Speaking and the same thing happens...

Crimson Wizard

#8
You are modifying frames in SpeechView, but these frames need to be copied into portrait overlay by AGS.
It might simply be that AGS does not update the portrait overlay after speech waiting time has finished. That will also explain why it updates not every frame.

E: I found you may hack this by setting game.messagetime to something greater than 1, but if you do that, it also stops skipping speech by time.

eri0o

#9
I only skip on Mouse click, so no problem not skipping with Timer... But... I placed game.messagetime = 65535 on game_start at the global script and it didn't work. :/

Edit: If the message is longer, this time is indeed longer, and if the message is short, this time is shorter... So updating it indeed has something to do with speaking the text...

Crimson Wizard

#10
Quote from: eri0o on Fri 01/06/2018 20:46:37
I only skip on Mouse click, so no problem not skipping with Timer... But... I placed game.messagetime = 65535 on game_start at the global script and it didn't work. :/

Ok, I was not clear enough.
game.messagetime is a dynamically changed value. You need to keep setting it in rep_exec. I set it to 2, and it updates tint very fast.

Code: ags

void repeatedly_execute_always(){
  if(player.Speaking)
  {
      tintPortrait(player, Random(255), Random(255), Random(255), 50, 100);  
      game.messagetime = 2;
  }
}


I think messagetime is related to how fast speech is animated and portrait updated.
Therefore you may choose different values to control the speed of tinting.

eri0o

whoa! That's magic! Damn! It worked! :-D

Thank you! This is amazing! (laugh)

SMF spam blocked by CleanTalk