Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Topics - eri0o

#141
Hey guys,

Recently I started a thread on TIGSource with the name Game feel and juice for old school pixel point and click adventure game. If someone wants to know more about juice, I recommend the Juice it or Lose it presentation in video. So I thought to replicate the answers that came around here in the forums, to maybe try to get some specifics in AGS - for example, Chicky already told me to use 60FPS and using GUIs as overlays, maybe this also invites him to chat more here?


  • leave trace, give stuff the player can interact that leaves visible trace in the room.
  • break the rules where makes sense. The character should be able to look things from far away if he doesn't NEED to move there, saving the walking time.
  • extra flair on the rewarding: has the player picked a new item? Flash the screen, play a cool sound.
  • clarify when you reveal new text on the first time. If your game has repeated outcomes for the same input (looking something yields the same message), clearly tell the player the first time he sees, it's something new.
  • visual feedback for interation. This is a lot of work, requiring lots of animation, but feels good.
  • tweening. Tween EVERYTHING.
  • voice acting. This one is hard because I think games work a lot better with voice acting, but this is hard if you don't live on an English speaking country.

If anyone has any ideas to contribute, AGS specific things, please do. The tween everything item I think needs to be done throughout the whole development, but some things can wait the polishing phase. If some can share input on when it's interesting to think on these things would be cool too.
#142
http://archive.is/FFx6z | https://www.polygon.com/2017/9/8/16263050/game-design-magic-tricks

Article: Game Design Magic Tricks

Hey, I read this article and was curious on which magic tricks could be used in point and click adventure games. Anyone has ideas?
#143
Hello!

I need to crossfade between two music tracks. I think this isn't so unusual, so probably someone here has already done it.

an excerpt of my audio player code is below
Code: ags

AudioChannel *acMusic;
AudioClip * currentMusic;
bool stopped;
int globalMusicVol;
AudioClip * previousmusic ;


static void MusicPlayer::play(AudioClip * musicClip,  int position){ 
  stopped = false;

  if(previousmusic==musicClip){
    //don't play music if it's already playing
    return;
  }
  
  //stop previous music
  if(previousmusic != null){
    previousmusic.Stop();
  }
  
  previousmusic = musicClip;
  
  currentMusic = musicClip;
  if(position == 0){
    acMusic = musicClip.Play(eAudioPriorityHigh, eRepeat);
  } else {
    acMusic = musicClip.PlayFrom(position,eAudioPriorityHigh, eRepeat);
    
  }
  acMusic.Volume = globalMusicVol;
  updateAllVolumes();
}

static void MusicPlayer::playFromPreviousTime(AudioClip * musicClip, float crossfadeTime){ 
  if(crossfadeTime!= 0.0){
    
  } else {
    int previousPosition = acMusic.Position;
    MusicPlayer.play(musicClip, previousPosition);
  }
}


The function I am working is static void MusicPlayer::playFromPreviousTime(AudioClip * musicClip, float crossfadeTime).

I want to enable the possibility to crossfade between two songs that have the exact same length. How to do this? I only have 1 audio channel for music, and I am not using the ambient channel, would be possible to set the audioclip type to whichever isn't the last one?

Cassiebsg told me there is the possibility to use the following function too:
SetGameOption (OPT_CROSSFADEMUSIC, x); // where x= 0 (no), 1 (slow), 2 (slowish), 3 (medium) and 4 (fast)

But... I would like to have control over the crossfade time. Below is my full audio module code, it has dependency on the Tween Module.


MusicPlayer.ash
Spoiler
Code: ags

// new module header

#define MusicPlayer_CROSSFADETIME 0


struct MusicPlayer
{
  import static void play(AudioClip * musicClip,  int position = 0);
  import static void playFromPreviousTime(AudioClip * musicClip, float crossfadeTime = MusicPlayer_CROSSFADETIME);
  import static AudioClip * getCurrentMusic();
  import static void stop(AudioClip * musicClip);
  import static void setMusicPosition(int x, int y);
  import static void removeMusicPosition();
  import static void tweenMusicPosition(int x, int y,  float timing);
  import static void setGlobalVolume(int musicVolume);
  import static int  getGlobalVolume();
  import static void setVolume(int musicVolume);
  import static void tweenVolume(int musicVolume, float timing);
};

struct SFXPlayer
{
  import static void play(AudioClip * soundClip);
  import static void stop(AudioClip * soundClip);
  import static void setGlobalVolume(int soundVolume);
  import static int  getGlobalVolume();
  import static void setVolume(int soundVolume);
};

import void SPlayerSetGameVolume(int gameVolume);
import int SPlayerGetGameVolume();
import void SPlayerSetDefaults();

[close]

MusicPlayer.asc
Spoiler
Code: ags

// MusicPlayer variables
AudioChannel *acMusic;
AudioClip * currentMusic;
bool stopped;
int globalMusicVol;
int currentMusicVolue;

// SFXPlayer variables
AudioChannel *acSound;
int gloabalSoundVol;
int orignalMusicX;
int orignalMusicY;

function updateAllVolumes(){
  int i = 0;
  AudioChannel *ac;
   
  while (i < System.AudioChannelCount)
  {
    ac = System.AudioChannels[i];
   
    if (ac.PlayingClip != currentMusic)
    {
      ac.Volume = gloabalSoundVol;
    } else {
      ac.Volume = globalMusicVol;
    }
    i++;
  }  
}


AudioClip * previousmusic ;


static AudioClip * MusicPlayer::getCurrentMusic(){
  return currentMusic;  
}

static void MusicPlayer::play(AudioClip * musicClip,  int position){ 
  stopped = false;

  if(previousmusic==musicClip){
    //don't play music if it's already playing
    return;
  }
  
  //stop previous music
  if(previousmusic != null){
    previousmusic.Stop();
  }
  
  previousmusic = musicClip;
  
  currentMusic = musicClip;
  if(position == 0){
    acMusic = musicClip.Play(eAudioPriorityHigh, eRepeat);
  } else {
    acMusic = musicClip.PlayFrom(position,eAudioPriorityHigh, eRepeat);
    
  }
  acMusic.Volume = globalMusicVol;
  updateAllVolumes();
}


static void MusicPlayer::playFromPreviousTime(AudioClip * musicClip, float crossfadeTime){ 
  if(crossfadeTime!= 0.0){
    
  } else {
    int previousPosition = acMusic.Position;
    MusicPlayer.play(musicClip, previousPosition);
  }
}

static void MusicPlayer::setMusicPosition(int x, int y){
  acMusic.SetRoomLocation(x, y);
  orignalMusicX = x;
  orignalMusicY = y;
}


static void MusicPlayer::removeMusicPosition(){
  acMusic.SetRoomLocation(0, 0);
  orignalMusicX = 0;
  orignalMusicY = 0;
}

static void MusicPlayer::tweenMusicPosition(int x, int y,  float timing){
  if(x==0 && y==0){
    MusicPlayer.removeMusicPosition();
  }
  
  if(orignalMusicX ==0 && orignalMusicY==0){
    return;  
  }
  
  acMusic.TweenRoomLocation(timing, x, y, orignalMusicX, orignalMusicY);
  orignalMusicX = x;
  orignalMusicY = y;
}


static void MusicPlayer::stop(AudioClip * musicClip){
  stopped = true;
  previousmusic = null;
  musicClip.Stop();  
}

static void MusicPlayer::setVolume(int musicVolume){
  if(acMusic != null){
    currentMusicVolue = musicVolume;
    acMusic.Volume = FloatToInt(IntToFloat(currentMusicVolue) * IntToFloat(globalMusicVol)/100.0);
  }
}

static void MusicPlayer::tweenVolume(int musicVolume, float timing){
  if(acMusic != null){
    currentMusicVolue = musicVolume;
    acMusic.TweenVolume(timing, FloatToInt(IntToFloat(musicVolume) * IntToFloat(globalMusicVol)/100.0), eEaseLinearTween, eNoBlockTween);
  }
}

static void MusicPlayer::setGlobalVolume(int musicVolume){
  globalMusicVol = musicVolume;
  Game.SetAudioTypeVolume(eAudioTypeAmbientSound, globalMusicVol, eVolExistingAndFuture);
  Game.SetAudioTypeVolume(eAudioTypeMusic, globalMusicVol, eVolExistingAndFuture);
}

static int MusicPlayer::getGlobalVolume(){
  return globalMusicVol;  
}



static void SFXPlayer::play(AudioClip * soundClip){ 
  acSound = soundClip.Play(eAudioPriorityNormal, eOnce);
  acSound.Volume = gloabalSoundVol;
  updateAllVolumes();
}

static void SFXPlayer::stop(AudioClip * soundClip){
  soundClip.Stop();  
}

static void SFXPlayer::setGlobalVolume(int soundVolume){
  gloabalSoundVol = soundVolume;
  Game.SetAudioTypeVolume(eAudioTypeSound, gloabalSoundVol, eVolExistingAndFuture);
}

static void SFXPlayer::setVolume(int soundVolume){
  if(acSound != null){
    acSound.Volume = FloatToInt(IntToFloat(soundVolume) * IntToFloat(gloabalSoundVol)/100.0);
  }
}

static int SFXPlayer::getGlobalVolume(){
  return gloabalSoundVol;  
}

void  SPlayerSetGameVolume(int gameVolume){
  System.Volume = gameVolume;
}

int SPlayerGetGameVolume(){
  return System.Volume;
}

void SPlayerSetDefaults(){
  MusicPlayer.setGlobalVolume(70);
  SFXPlayer.setGlobalVolume(70);
  SPlayerSetGameVolume(70);
}

function game_start()
{
  SPlayerSetDefaults();
  sldAudio.Value = System.Volume;
  sldTestMainVolume.Value = System.Volume;
  sldTestMusicVolume.Value = globalMusicVol;
  sldTestSoundVolume.Value = gloabalSoundVol;
}
[close]
#144
Hello, my game occasionally gives the error below at a specific point:

An exception 0xC0000005 occurred in ACWIN.EXE at EIP = 0x00000000 ; program pointer is +5997, ACI version 3.4.0.16, gtags (72,0)

AGS cannot continue, this exception was fatal. Please note down the number above, remember what you were doing at the time and post the details on the AGS Technical Forum


The part of the game is a change room where I switch the music. The error doesn't happen every time, only sometimes. How do I do diagnostic of these type of errors? It generated a dump but I don't know how to read it. I am using the version: AGS Editor .NET (Build 3.4.0.16), v3.4.0, March 2017.

Thanks
#145
Thinking about the Blue Cup as an actual item in game, or maybe some item in the background.

What uses would you guys give to it in game, or what have you seem done in a game? :)

Edit: Well, this is actually a topic for Adventure Related Talk & Chat, can't find the delete button...
#146
Hey,

I have no idea if this is possible to do, the idea would be to create a module to do a custom save game. I will start the thread with some incomplete code (I have no idea how to read and parse yet) to try to gasp what's doable. Also I am looking into the most generic as possible code so the most people can benefit from it.

My design idea would be having an object to hold all rooms information, and update it when the player visits the room. Also have other object to hold other relevant information - I think this is maybe not needed but I like to have it clear which kind of thing is available to be saved. These objects would have handler to convert then to String and also have some way to parse a text file, and find the objects and recover then.

Also it would be necessary to have a way to store global variables - this would mean that for this to work, all room variables would be global, and on room variable state would be discouraged. I have no idea how to go on this.

A plus idea would be some form of text compression before storage and decompression after reading - nothing fancy, stuff as light as text compression on SNES cartridges.

PortableSaveGame.asc
Spoiler
Code: ags

// new module script
#define MAX_REGIONS 15
#define MAX_HOTSPOTS 49
#define MAX_POSSIBLE_ROOMS 100
#define MAX_POSSIBLE_CHARACTERS 100

struct RoomSaveData{
  bool exists;
  bool load_from_save;
  int ObjectCount;
  bool object_isnull[MAX_ROOM_OBJECTS];
  int object_X[MAX_ROOM_OBJECTS];
  int object_Y[MAX_ROOM_OBJECTS];
  bool object_Visible[MAX_ROOM_OBJECTS];
  bool object_Clickable[MAX_ROOM_OBJECTS];
  int object_Transparency[MAX_ROOM_OBJECTS];
  int object_Graphic[MAX_ROOM_OBJECTS];
  int object_View[MAX_ROOM_OBJECTS];
  int object_Loop[MAX_ROOM_OBJECTS];
  int object_Frame[MAX_ROOM_OBJECTS];
  bool hotspot_Enabled[MAX_HOTSPOTS];
  bool region_Enabled[MAX_REGIONS];
};

struct CharacterSaveData{
  bool exists;
  String Name;
  int Room;
  int x;
  int y;
  int z;
  int Transparency;
  bool Clickable;
  int View;
  int Frame;
  int Loop;
  int inventoryQuantity[MAX_GAME_ITEMS];
};

struct GameSaveData{
  int character_count;
  int invetoryitem_count;
  int player_ID;
};


GameSaveData save_data_game;
RoomSaveData save_data_rooms[MAX_POSSIBLE_ROOMS];
CharacterSaveData save_data_charas[MAX_POSSIBLE_CHARACTERS];

//------------------------------------------------------------------------
// PRIVATE FUNCTIONS
//-------o-----------------------------------------------------------------
void storeCharacterData(){
  int i;
  int j;
  i=0;
  save_data_game.character_count = Game.CharacterCount;
  save_data_game.invetoryitem_count = Game.InventoryItemCount+1;
  save_data_game.player_ID = player.ID;
  
  while(i<save_data_game.character_count){
    if(character[i]!=null){
      save_data_charas[i].exists=true;
      save_data_charas[i].Name = character[i].Name;
      save_data_charas[i].Room = character[i].Room;
      save_data_charas[i].x = character[i].x;
      save_data_charas[i].y = character[i].y;
      save_data_charas[i].z = character[i].z;
      save_data_charas[i].Transparency = character[i].Transparency;
      save_data_charas[i].Clickable = character[i].Clickable;
      save_data_charas[i].View = character[i].View;
      save_data_charas[i].Frame = character[i].Frame;
      save_data_charas[i].Loop = character[i].Loop;
      j=1;
      while(j<save_data_game.invetoryitem_count){
        save_data_charas[i].inventoryQuantity[j] = character[i].InventoryQuantity[j];
        j++;
      }
    } else {
      save_data_charas[i].exists=false;
    }
    
    i++;
  }
}

String CharacterSaveData_toString(){
  String rstr="{\"characters\": {[";
  int i;
  int j;
  i=0;
  while(i<save_data_game.character_count){
    if(save_data_charas[i].exists){
      rstr=rstr.Append(String.Format("  \"%d\":{[",i));
      rstr=rstr.Append(String.Format("    \"exists\": %d,[",save_data_charas[i].exists));
      rstr=rstr.Append(String.Format("    \"Name\": \"%s\",[",save_data_charas[i].Name));
      rstr=rstr.Append(String.Format("    \"Room\": %d,[",save_data_charas[i].Room));
      rstr=rstr.Append(String.Format("    \"x\": %d,[",save_data_charas[i].x));
      rstr=rstr.Append(String.Format("    \"y\": %d,[",save_data_charas[i].y));
      rstr=rstr.Append(String.Format("    \"z\": %d,[",save_data_charas[i].z));
      rstr=rstr.Append(String.Format("    \"Transparency\": %d,[",save_data_charas[i].Transparency));
      rstr=rstr.Append(String.Format("    \"Clickable\": %d,[",save_data_charas[i].Clickable));
      rstr=rstr.Append(String.Format("    \"View\": %d,[",save_data_charas[i].View));
      rstr=rstr.Append(String.Format("    \"Frame\": %d,[",save_data_charas[i].Frame));
      rstr=rstr.Append(String.Format("    \"Loop\": %d,[",save_data_charas[i].Loop));
      
      
      rstr=rstr.Append(String.Format("    \"inventoryQuantity\":{["));
      j=1;
      while(j<save_data_game.invetoryitem_count-1){
        rstr=rstr.Append(String.Format("      \"%d\": %d,[",j, save_data_charas[i].inventoryQuantity[j]));
        j++;
      }
      rstr=rstr.Append(String.Format("      \"%d\": %d[",j, save_data_charas[i].inventoryQuantity[j]));
      
      rstr=rstr.Append(String.Format("    }[  },["));
    }
    i++;
  }
  rstr=rstr.Append(String.Format("    }[  }["));
  return rstr;
}

void storeCurrentRoomData(){
  int i;
  int r = player.Room;
  save_data_rooms[r].load_from_save = false;
  save_data_rooms[r].exists = true;
  save_data_rooms[r].ObjectCount = Room.ObjectCount;
  i=0;
  while(i<save_data_rooms[r].ObjectCount){
    if(object[i] != null){
      save_data_rooms[r].object_isnull[i] = false;
      save_data_rooms[r].object_X[i] = object[i].X;
      save_data_rooms[r].object_Y[i] = object[i].Y;
      save_data_rooms[r].object_Visible[i] = object[i].Visible;
      save_data_rooms[r].object_Clickable[i] = object[i].Clickable;
      save_data_rooms[r].object_Transparency[i] = object[i].Transparency;
      save_data_rooms[r].object_Graphic[i] = object[i].Graphic;
      save_data_rooms[r].object_View[i] = object[i].View;
      save_data_rooms[r].object_Loop[i] = object[i].Loop;
      save_data_rooms[r].object_Frame[i] = object[i].Frame;
    }  else {
      save_data_rooms[r].object_isnull[i] = true;
    }
    i++;
  }
  
  i=0;
  while(i<MAX_HOTSPOTS){
    save_data_rooms[r].hotspot_Enabled[i] = hotspot[i].Enabled;
    i++;
  }
  
  i=0;
  while(i<MAX_REGIONS){
    save_data_rooms[r].region_Enabled[i] = region[i].Enabled;
    i++;
  }
}

String RoomSaveData_toString(){
  String rstr="{\"rooms\": {[";
  int i;
  int j;
  i=0;
  while(i<MAX_POSSIBLE_ROOMS){
    if(save_data_rooms[i].exists){
      rstr=rstr.Append(String.Format("  \"%d\":{[",i));
      rstr=rstr.Append(String.Format("    \"exists\": %d,[",save_data_rooms[i].exists));
      rstr=rstr.Append(String.Format("    \"load_from_save\": \"%d\",[",save_data_rooms[i].load_from_save));
      rstr=rstr.Append(String.Format("    \"ObjectCount\": \"%d\",[",save_data_rooms[i].ObjectCount));
      
      
      rstr=rstr.Append(String.Format("    \"objects\":{["));
      j=0;
      while(j<save_data_rooms[i].ObjectCount){
        rstr=rstr.Append(String.Format("      \"%d\": {",j));
        
        rstr=rstr.Append(String.Format("        \"isnull\": %d,[", save_data_rooms[i].object_isnull[j]));
        rstr=rstr.Append(String.Format("        \"X\": %d,[", save_data_rooms[i].object_X[j]));
        rstr=rstr.Append(String.Format("        \"Y\": %d,[", save_data_rooms[i].object_Y[j]));
        rstr=rstr.Append(String.Format("        \"Visible\": %d,[", save_data_rooms[i].object_Visible[j]));
        rstr=rstr.Append(String.Format("        \"Clickable\": %d,[", save_data_rooms[i].object_Clickable[j]));
        rstr=rstr.Append(String.Format("        \"Transparency\": %d,[", save_data_rooms[i].object_Transparency[j]));
        rstr=rstr.Append(String.Format("        \"Graphic\": %d,[", save_data_rooms[i].object_Graphic[j]));
        rstr=rstr.Append(String.Format("        \"View\": %d,[", save_data_rooms[i].object_View[j]));
        rstr=rstr.Append(String.Format("        \"Loop\": %d,[", save_data_rooms[i].object_Loop[j]));
        rstr=rstr.Append(String.Format("        \"Frame\": %d,[", save_data_rooms[i].object_Frame[j]));
        
        if(j<save_data_rooms[i].ObjectCount-1){
          rstr=rstr.Append(String.Format("      },"));
        } else {
          rstr=rstr.Append(String.Format("      }"));
        }
        j++;        
      }
      rstr=rstr.Append(String.Format("    },"));
      
      
      rstr=rstr.Append(String.Format("    \"regions\":{["));
      j=0;
      while(j<MAX_REGIONS){
        rstr=rstr.Append(String.Format("      \"%d\": {  \"Enabled\": %d }, [",j,save_data_rooms[i].region_Enabled[j]));
        j++;
      }
      rstr=rstr.Append(String.Format("    },"));
      
     
      rstr=rstr.Append(String.Format("    \"hotspot\":{["));
      j=0;
      while(j<MAX_HOTSPOTS){
        rstr=rstr.Append(String.Format("      \"%d\": {  \"Enabled\": %d }, [",j,save_data_rooms[i].hotspot_Enabled[j]));
        j++;
      }
      rstr=rstr.Append(String.Format("    },"));
      
    }
    i++;
  }
  rstr=rstr.Append(String.Format("  }[}["));
  return rstr;
  
}

void storeCurrentData(){
  storeCharacterData();
  storeCurrentRoomData();
}

void writeToFile(String content, String filename){
  File *output = File.Open(filename, eFileWrite);
  if (output == null) {
    Display("Error opening file.");
  } else {
    String lineToWrite;
    String restOfContent;
    
    restOfContent = content.Copy();
    
    while(restOfContent.Length > 0 && restOfContent.IndexOf("[")>0){
      lineToWrite = restOfContent.Substring(0, restOfContent.IndexOf("["));
      restOfContent = restOfContent.Substring(restOfContent.IndexOf("[")+1, restOfContent.Length- restOfContent.IndexOf("[")-1);
      output.WriteRawLine(lineToWrite);
    }
    
    
    output.Close();
  }
}

String readFile(String filename){
  
}


//------------------------------------------------------------------------
// PUBLIC FUNCTIONS
//------------------------------------------------------------------------
static void PortableSaveGame::save(int slot, String description){
  storeCurrentData();
  String fstr;
  fstr=CharacterSaveData_toString();
  fstr=fstr.Append(RoomSaveData_toString());
  writeToFile(fstr, "atemp.json");
}

static void PortableSaveGame::restore(int slot){
  
}

//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
function on_event (EventType event, int data)
{
  if(event == eEventLeaveRoom){
    storeCurrentRoomData();
  } else if(event == eEventEnterRoomBeforeFadein){
    //do necessary loading from save
  }
}
[close]

PortableSaveGame.ash
Spoiler
Code: ags

struct PortableSaveGame{
  import static void save(int slot, String description);
  import static void restore(int slot);
};
[close]

#147
Hey,

There's a scene where we wanted our character to push a box, which he will use to climb on top and reach something that's normally out of reach. How would you design such a scene? Our first design idea is that interacting with the box reveals a GUI with forward and backward arrows.
#148
Hey,

Rooms have a property called ShowPlayerCharacter in the editor, that can be either true or false. How do I get that specific property value for the current room the player is, in the scripts?
#149
Ok, just to try something new I decided to try to port a code for creation of a 3D cube in AGS. I am following this tutorial for processing.

I am falling in a problem where I get a melting cube:



My code is below:
Spoiler
Code: ags

// new module script
//********************************************************
// * See tutorial at:
// * http://www.petercollingridge.appspot.com/3D-tutorial
//*********************************************************/
int backgroundColour;
int nodeColour;
int edgeColour;
int nodeSize;
int pmouseX;
int pmouseY;
int off_x;
int off_y;
DynamicSprite * SpriteScreen;
DrawingSurface * ScrSurf;
Overlay* myOverlay;

struct node{
  int x;
  int y;
  int z;
};

struct edge{
  int n0;
  int n1;
};

#define NODESLENGTH 8
#define EDGESLENGTH 12
node nodes[NODESLENGTH];
edge edges[EDGESLENGTH];


void rotateZ3D(float theta) {
  float sin_t = Maths.Sin(theta);
  float cos_t = Maths.Cos(theta);
  
  int i=0;
  while(i<NODESLENGTH){
    int x = nodes[i].x;
    int y = nodes[i].y;
    nodes[i].x = FloatToInt(IntToFloat(x)*cos_t-IntToFloat(y)*sin_t);
    nodes[i].y = FloatToInt(IntToFloat(y)*cos_t+IntToFloat(x)*sin_t);
    i++;  
  }
}

void rotateY3D(float theta) {
  float sin_t = Maths.Sin(theta);
  float cos_t = Maths.Cos(theta);
  
  int i=0;
  while(i<NODESLENGTH){
    int x = nodes[i].x;
    int z = nodes[i].z;
    nodes[i].x = FloatToInt(IntToFloat(x)*cos_t-IntToFloat(z)*sin_t);
    nodes[i].z = FloatToInt(IntToFloat(z)*cos_t+IntToFloat(x)*sin_t);
    i++;
  }
}

void rotateX3D(float theta) {
  float sin_t = Maths.Sin(theta);
  float cos_t = Maths.Cos(theta);
  
  int i=0;
  while(i<NODESLENGTH){
    int y = nodes[i].y;
    int z = nodes[i].z;
    nodes[i].y = FloatToInt(IntToFloat(y)*cos_t-IntToFloat(z)*sin_t);
    nodes[i].z = FloatToInt(IntToFloat(z)*cos_t+IntToFloat(y)*sin_t);
    i++;
  }
}

void translate(int x, int y, int z) {
  int i=0;
  while(i<NODESLENGTH){
    nodes[i].x=nodes[i].x+x;
    nodes[i].y=nodes[i].y+y;
    nodes[i].z=nodes[i].z+z;
    i++;
  }
}

void draw() {
  ScrSurf = SpriteScreen.GetDrawingSurface();
  ScrSurf.Clear(backgroundColour);
  ScrSurf.DrawingColor=edgeColour;
  
  int i=0;
  while(i<EDGESLENGTH){
    ScrSurf.DrawLine(nodes[edges[i].n0].x+off_x, nodes[edges[i].n0].y+off_y, nodes[edges[i].n1].x+off_x, nodes[edges[i].n1].y+off_y);
    i++;
  }
  
  
  ScrSurf.DrawingColor=nodeColour;
  
  i=0;
  while(i<NODESLENGTH){
    ScrSurf.DrawPixel(nodes[i].x+off_x, nodes[i].y+off_y);
    i++;
  }
  ScrSurf.Release();
}

void mouseDragged() {  
  rotateY3D(IntToFloat(mouse.x - pmouseX));
  rotateX3D(IntToFloat(mouse.y - pmouseY));
  pmouseX=mouse.x;
  pmouseY=mouse.y;
}

void autoRotate() {  
  rotateY3D(Maths.Pi/32.0);
  rotateX3D(Maths.Pi/32.0);
}


void repeatedly_execute(){
  //mouseDragged();
  autoRotate();
  draw();
  myOverlay.Remove();
  myOverlay = Overlay.CreateGraphical(0, 0, SpriteScreen.Graphic, true);
}

void game_start(){
  backgroundColour =  Game.GetColorFromRGB(255, 255, 255);
  nodeColour =  Game.GetColorFromRGB(40, 168, 107);
  edgeColour =  Game.GetColorFromRGB(34, 68, 204);
  nodeSize = 8;
  
  nodes[0].x= -25;
  nodes[0].y= -25;
  nodes[0].z= -25;
  nodes[1].x= -25;
  nodes[1].y= -25;
  nodes[1].z= 25;
  nodes[2].x= -25;
  nodes[2].y= 25;
  nodes[2].z= -25;
  nodes[3].x= -25;
  nodes[3].y= 25;
  nodes[3].z= 25;
  nodes[4].x= 25;
  nodes[4].y= -25;
  nodes[4].z= -25;
  nodes[5].x= 25;
  nodes[5].y= -25;
  nodes[5].z= 25;
  nodes[6].x= 25;
  nodes[6].y= 25;
  nodes[6].z= -25;
  nodes[7].x= 25;
  nodes[7].y= 25;
  nodes[7].z= 25;
  
  edges[0].n0= 0;
  edges[0].n1= 1;
  edges[1].n0= 1;
  edges[1].n1= 3;
  edges[2].n0= 3;
  edges[2].n1= 2;
  edges[3].n0= 2;
  edges[3].n1= 0;
  edges[4].n0= 4;
  edges[4].n1= 5;
  edges[5].n0= 5;
  edges[5].n1= 7;
  edges[6].n0= 7;
  edges[6].n1= 6;
  edges[7].n0= 6;
  edges[7].n1= 4;
  edges[8].n0= 0;
  edges[8].n1= 4;
  edges[9].n0= 1;
  edges[9].n1= 5;
  edges[10].n0= 2;
  edges[10].n1= 6;
  edges[11].n0= 3;
  edges[11].n1= 7; 
  
  rotateZ3D(30.0);
  rotateY3D(30.0);
  rotateX3D(30.0);
  
  translate(25, 25, 0);
  
  SpriteScreen = DynamicSprite.Create(System.ViewportWidth, System.ViewportHeight, true);
  myOverlay = Overlay.CreateGraphical(0, 0, SpriteScreen.Graphic, true);
  off_x=100;
  off_y=100;
}
[close]

AGS Project Download as zip here

I think maybe this is due to rounding errors but I don't know how to prevent it. :/
#150
Hey,

In the Forum Profile Settings, we have:

AIM, WLM, YIM, Facebook, MySpace, Twitter, Google+, LinkedIn, YouTube, deviantArt, Pinterest, Skype.

I think it would be cool to have Github profile there too. :)
#151
Joystick for BASS Template - adventureJoy - v0.1.0

Written with AGS 3.4.1-7 Beta. This module depends on the Joystick plugin from Wyz (at this time, is v1.2.0), which must be enabled for this module to work.

Download Source Code adventureJoy.asc

Download Source Header adventureJoy.ash

Download Module adventureJoy.scm

Download demo joybass.zip

This module was written with an Xbox360 Gamepad in mind and it's still in very alpha stage, but I thought about sharing it because I could use some ideas on this. My main idea is to create a relatively drop in module to help converting a point and click game to use with joystick - I know this is hard, so at first, I thought about the BASS Template, which has a simpler interface.

You need to configure the inventory window shortcut. Somewhere in your code, for example on your global_script do:
Code: ags

//------------------------------------------------------------------------------
// game_start
//------------------------------------------------------------------------------
function game_start() 
{

  adventureJoy.setInventoryArea(InventoryWindow1.X, InventoryWindow1.Y, InventoryWindow1.Width, InventoryWindow1.Height, InventoryWindow1.ItemsPerRow, InventoryWindow1.RowCount);

  //or you can also do
  //adventureJoy.setInventoryWindow(InventoryWindow1)

}



adventureJoy.on()
The module is always automatically on. But in case you turn it off, you will need to turn it back on mannualy.

adventureJoy.off()
To turn off the module.

adventureJoy.beforeSave()
Do this before saving any game state, this will disable the module temporarly.

adventureJoy.afterSaveLoad()
Do this after restoring a save.

Github Repository, fork me there!
#152
Hey,

Every time I test my game on Windows 10, when it tries to run, it defaults to Not Run for me. And then I have to do a small click in the box and then have to tell it's safe to run the game or the winsetup for config.

Is it possible to make the game.exe file and maybe also the winsetup.exe trusted executables? And is there a way to configure the Publisher Name ?

Thanks!
#153
Is it possible to make an event on a GUI point to a function not on GlobalScript.asc ? Is it normal that if I click on the three dots (...) it doesn't lead to my function?
#154
Hello!

I am trying to understand if it's possible to have separate audio controls for music and sound effects. My first approach was to implement two modules, one to deal with music and another to deal with sound effects. The sound effects module is very simple so I will show it below.

SFXPlayer.asc
Spoiler
Code: ags

AudioChannel *acSound;
int gloabalSoundVol;

static void SFXPlayer::play(AudioClip * soundClip){ 
  acSound = soundClip.Play(eAudioPriorityNormal, eOnce);
  acSound.Volume = gloabalSoundVol;
}

static void SFXPlayer::stop(AudioClip * soundClip){
  soundClip.Stop();  
}

static void SFXPlayer::setGlobalVolume(int soundVolume){
  gloabalSoundVol = soundVolume;
  if(acSound != null){
    acSound.Volume = gloabalSoundVol;
  }
}

static void SFXPlayer::setVolume(int soundVolume){
  if(acSound != null){
    acSound.Volume = FloatToInt(IntToFloat(soundVolume) * IntToFloat(gloabalSoundVol)/100.0);
  }
}

static int SFXPlayer::getGlobalVolume(){
  return gloabalSoundVol;  
}

function game_start()
{
  gloabalSoundVol=100;
}
[close]

So the idea was that manipulating the volume of the returned audioChannel I could set it's volume to the appropriate one from a previously set volume. So this approach kind of works, but some sounds are not passed through neither my MusicPlayer module or my SFXPlayer module. I am talking about the sounds originating from view frames, like the sounds of the main character walking and the sounds in between complex animations... So my question is, is there any way to set those sounds volume?

I know there is a variable called System.AudioChannelCount that tells me the number of SystemChannels, and I can access them using System.AudioChannels[ i] ... But I couldn't find out how to use this information to create a sounds volume control. I think if I could understand which audio channel is currently holding the current music, I could assume all other are dealing with the SFX, but I have no idea what to do with this information...

EDIT:

Maybe I need to do something like this, but I don't know where yet.

Code: ags

//AudioClip * currentMusic;
//this variable holds the current playing music

int i = 0;
AudioChannel *ac;

while (i < System.AudioChannelCount)
{
  ac = System.AudioChannels[i];
 
  if (ac.PlayingClip != currentMusic)
  {
    System.AudioChannels[i].Volume = globalSoundVolume;
  } else {
    System.AudioChannels[i].Volume = globalMusicVolume;
  }
  i++;
}


EDIT2:

Apparently the AGS Manual says the following:

QuoteAGS currently has an 8-channel audio system, which means that up to 8 sounds can be playing at the same time. With the default Audio Types settings, one channel is reserved for speech, one for music and one for ambient sounds; thus leaving 5 available for sound effects.

So, when I play and audioClip, how do I specify it's a music or an ambient sound so it attaches to the correct audio channel?

EDIT3:

Apparently I am an idiot... Ok, so each audio clip has a type, on the interface you can set each of the audio clips type. You can even create a folder and set the default type for the audio clips in that folder. Now if you do this correctly, and it's pretty easy, you can then set the volumes for the audio types using:


Code: ags

  Game.SetAudioTypeVolume(eAudioTypeAmbientSound, 100, eVolExistingAndFuture);
  Game.SetAudioTypeVolume(eAudioTypeMusic, 100, eVolExistingAndFuture);
  Game.SetAudioTypeVolume(eAudioTypeSound, 100, eVolExistingAndFuture);


So... Yeah... That's it, it doesn't require all my ramblings on there...
#155
Site & Forum Reports / Embedded Youtube Videos
Thu 31/08/2017 15:17:41
Hello,

Is the embedded youtube video of the forum broken? To me they appear as text now instead of being embedded ?

"[embed=x,y] video link [/embed]" are the ones I am referencing... Like here: Blackwell Epiphany trailer embedded.
#156
NormRnd

NormRnd is a tiny (2.1kB) module adding a standard normal random number function. It uses Box-Muller method, which should be reasonably fast.

It exposes two functions:

float normrnd_unitary()
Returns a standard normal random number, as a float, with median 0, and variance 1.

int normrnd_asint(int median, int sigma, int clamp_ceil,  int clamp_floor)
Returns a standard normal random number, as a int, requires passing a defined median and sigma. It also allow clamping to restrict the possible returning values. If clamp_ceil and clamp_floor are equal, clamping is disabled.

NormRnd.asc

Spoiler
Code: ags

// normrnd
//
// This module allows returning standard Normal random number.
// This code is based on Box-Muller method, implemented in C
// by Prof Darren Wilkinson here:
//
// http://www.mas.ncl.ac.uk/~ndjw1/teaching/sim/transf/norm.html
//
// My work was porting to AGS and giving utilitary functions
// Author: Erico Porto
//

float surand()
{
  return ( IntToFloat(Random(1000))/1000.0 );
}

float urand(float low, float high)
{
  return(low+(high-low)*surand());
}

float genexp(float lambda)
{
  float u,x;
  u=surand();
  x=(-1.0/lambda)*Maths.Log(u);
  return(x);
}

float gennor()
{
  float theta,rsq,x;
  theta=urand(0.0,2.0*Maths.Pi);
  rsq=genexp(0.5);
  x=Maths.Sqrt(rsq)*Maths.Cos(theta);
  return(x);
}

//normrnd_unitary
//
// returns a float that is a standard Normal random number
float normrnd_unitary(){
  return gennor();
}

// returns an int that is a standard Normal random number
// first paramter, median, is the median of the distribution
// second parameter, sigma, is proportional to the variance of the distribution
// third and fourth parameter allow clamping the result if a hard limit is desired
int normrnd_asint(int median, int sigma, int clamp_ceil,  int clamp_floor){
  int return_value = FloatToInt(gennor()*IntToFloat(sigma)+IntToFloat(median));
  if( clamp_ceil != clamp_floor && clamp_ceil > clamp_floor){
    if (return_value > clamp_ceil){
      return_value = clamp_ceil;
    }
    if (return_value < clamp_floor){
      return_value = clamp_floor;
    }
  }
  
  return return_value;
}
[close]

NormRnd.ash

Spoiler
Code: ags

// normrnd
//
// This module allows returning standard Normal random number.
// This code is based on Box-Muller method, implemented in C
// by Prof Darren Wilkinson here:
//
// http://www.mas.ncl.ac.uk/~ndjw1/teaching/sim/transf/norm.html
//
// My work was porting to AGS and giving utilitary functions
// Author: Erico Porto
//
import float normrnd_unitary();
import int normrnd_asint(int median, int sigma, int clamp_ceil = 0,  int clamp_floor = 0);
[close]

Download NormRnd.scm here

Doc website

Github Repository
#157
Hi guys,

I am trying to create an effect over the entire screen on room load, imagine like a old tube TV effect.

Right now my implementation uses DynamicSprite.CreateFromScreenShot(320, 180) to get the screen after the room is loaded, I create another DynamicSprite where I get this first image and apply the effects, then I use MyOverlay.Remove() and Overlay.CreateGraphical(0,0 , MyScreenFromSShot.Graphic, true) on the repeatedly_execute_always to generate my effect.

Ok so this approach has some problems:


  • I can only apply the dynamic effect from the first possible screenshot in the room, so I can use the effect only for a short transition;
  • The fade in has to be finished before I can take a screenshot;
  • The screenshot will include the game cursor, which is undesirable.

Also this is working slow under Direct Draw but working with no fps drop under Linux - I haven't tested OpenGL on Windows yet, so I am not that worried right now about performance.

Have you guys tried to do any over entire screen effect? Is there a way to capture the screen just before it's drawn and edit the painting without having to use screenshot, which essentially has to let the entire screen be painted at least one frame before being useful.

Example implementation of what I described above
Spoiler

Code: ags


// Effect on entire screen using overlay
DynamicSprite* Spr_ScreenShot;
DynamicSprite* Spr_EffectedSShot;
DrawingSurface* EffectedSShot_Surface;
Overlay* Effect_Overlay ;
bool Effect_enabled;

// Effect_start is exposed using import on header
function Effect_start(){
  Spr_ScreenShot = DynamicSprite.CreateFromScreenShot(320, 180);
  Spr_EffectedSShot = DynamicSprite.CreateFromExistingSprite(Spr_ScreenShot.Graphic, false);
  Effect_enabled=true;
  Effect_Overlay = Overlay.CreateGraphical(0,0 , Spr_EffectedSShot.Graphic, true);
}

// Effect_stop is exposed using import on header
function Effect_stop(){
  if(Effect_enabled){
    Effect_enabled=false;
    Effect_Overlay.Remove();
    Spr_EffectedSShot.Delete();
    Spr_ScreenShot.Delete();
  }
}



function renderEffect(){
  Effect_Overlay.Remove();
  EffectedSShot_Surface = Spr_EffectedSShot.GetDrawingSurface();
  
  //draw stuff on the surface and manipulate it ...
  
  EffectedSShot_Surface.Release();
  Effect_Overlay = Overlay.CreateGraphical(0,0 , Spr_EffectedSShot.Graphic, true);
  
}


// draws the effect once it's enabled
function repeatedly_execute_always()
{
  if (!IsGamePaused() && Effect_enabled)
  {
    renderEffect();
    
  }
}

// certify that effect stops when leaving the room
function on_event(EventType event, int data){
    if(event == eEventLeaveRoom){
      Effect_stop();
    }
}


[close]

EDIT: SOLVED

Link to mp4 video of it working!

Hey guys, thanks for pointing me in the right direction! I followed the Underwater module, arj0n gave me the source, and there was the FakeScreen module. I modified it to generate a constant FakeSprite and used it's graphic in my VHSEffect! :D
#158
AGS Games in Production / Future Flashback
Sun 30/07/2017 04:03:54

Story:

Behold a future where a drug is capable of recreate vivid memories. Untangle the obsessive mind of an ex surgeon, that after an accident starts reliving the memories of a girl he never met, seeing her past, looking into her dreams and unfolding the mysterious future they share together.

Expected Release date: 2020

Story by Guilherme Henrique
Programing by Erico Porto
Art by Ricardo Juchem
Music by Jordan Pool

Screenshots:





Blog | YouTube Channel | Facebook Page | Twitter @futureflashbck | presskit() | indieDB

Update Log:
0 - 30 Jul 2017 - Game Announcement
1 - 31 Aug 2017 - Theme Music Reveal
2 - 24 Sep 2017 - Game Menu is a Smartphone
3 - 02 Oct 2017 - Characters Portrait Reveal
4 - 19 Oct 2017 - Gameplay video of First Level
5 - 28 Oct 2017 - Title Screen and updated Theme Music
6 -  4 Nov 2017 - We Have a Website
7 - 10 Nov 2017 - The evolution of a background art
8 - 14 Nov 2017 - Social Networks + Ship of Theseus Development
9 - 17 Dec 2017 - Screenshot and New Background
10 -  8 Apr 2018 - More Screenshots
11 - 10 Jul 2018 - One more screenshot, art streaming and Future Flashback in a game event!
12 - 21 Aug 2018 - Geek & Game Rio Festival 2018
13 - 17 Sep 2018 - Going to AdventureX
14 - 18 Nov 2018 - Future Flashback on AdventureX
15 - 15 Dec 2018 - Library Screenshot
16 - 31 Dec 2018 - 2018 Recap
17 - 13 Feb 2019 - Steam Capsules poll
18 - 13 Jul 2019 - A small update of themes
#159
Hello,

I am trying to create an InteractionDetector for my game, Objects and Characters are easy to get the X and Y positions, but Hotspots are trickier.

So I decided to go pixel by pixel on the screen, check which hotspot that pixel belongs to, and note down the minimal and maximum x and y positions, the idea is to later do:
y=miny+(maxy-miny)/2 .

But it's not working! I know the error is in the generate_hotspot_xy function, but I have no idea what's wrong.

Below is the full code, if you want to test it, just replace the font enumerator for your game. I think the rest is not dependent of game code.


Fullcode: InteractionDetector.asc
Spoiler

Code: ags

// new module script
DynamicSprite* interactionSprite;
DrawingSurface * interactionSurface;
Overlay* interactionOverlay ;
bool interactionShown;

int hotspot_minx[MAX_HOTSPOTS_PER_ROOM];
int hotspot_maxx[MAX_HOTSPOTS_PER_ROOM];
int hotspot_miny[MAX_HOTSPOTS_PER_ROOM];
int hotspot_maxy[MAX_HOTSPOTS_PER_ROOM];
int hotspot_x[MAX_HOTSPOTS_PER_ROOM];
int hotspot_y[MAX_HOTSPOTS_PER_ROOM];

//this is the function used to draw each label
function draw_label(String text, int textx,  int texty){
  int textcolor = Game.GetColorFromRGB(216,216, 216);
  if(textx+GetTextWidth(text, eFontSmallFont)+4>Room.Width){
    textx=Room.Width-GetTextWidth(text, eFontSmallFont)-4;
  }
  interactionSurface.DrawingColor = textcolor;
  interactionSurface.DrawString(textx, texty, eFontSmallFontOutlined, text);
}

//this function shows the interaction detector when pressed
static void InteractionDetector::show() {  
  int obj_count = Room.ObjectCount;
  int cha_count = Game.CharacterCount;
  int i;
  int textx,  texty;
  String text;
  interactionShown = true;
  interactionSprite = DynamicSprite.Create(Room.Width, Room.Height);
  interactionSurface = interactionSprite.GetDrawingSurface();
  Character *c;

  
  i=0;
  while(i<obj_count){
    if(object[i].Visible && object[i].Clickable){
      text = object[i].Name;
      textx=object[i].X;
      texty=object[i].Y;
      draw_label(text, textx, texty);
    }
    i++;
  }
  
 
  i=0;
  while(i<MAX_HOTSPOTS_PER_ROOM){
    text = hotspot[i].Name;
    if(hotspot[i].Enabled){
      if(text.Length > 1 && text.IndexOf("Hotspot") == -1){
        textx=hotspot_x[i];
        texty=hotspot_y[i];
        
        draw_label(text, textx, texty);
      }      
    }
    i++;
  }
  
  
  i=0;
  while (i < cha_count) {
    c = character[i];
    if (c.Room == player.Room && c.Clickable && c.ID != player.ID) {
      ViewFrame *cviewframe = Game.GetViewFrame(c.View, c.Loop, c.Frame);
      textx = c.x;
      texty = c.y - Game.SpriteHeight[cviewframe.Graphic]*c.Scaling/2*100;
      text = c.Name;
      if(text.Length>2){
        draw_label(text, textx, texty);
      }
    }
    i++;
  }
  
  interactionOverlay= Overlay.CreateGraphical(0, 0, interactionSprite.Graphic, true);
  player.Say("testx = %d, texty = %d",hotspot_x[2], hotspot_y[2]);
}

//hides the Interaction Detector Overlay
static void InteractionDetector::hide() { 
  interactionOverlay.Remove();
  interactionSurface.Release();
  interactionSprite.Delete();
  interactionShown = false;
}

//external function to check if the Interaction Detector Overlay is already shown
static bool InteractionDetector::isShown() {  
  return interactionShown;
}

//this allows toglin on and off the Interaction Detector Overlay
static void InteractionDetector::toggle() {  
  if(interactionShown){
    InteractionDetector.hide();
  } else {
    InteractionDetector.show();
  }
}

//this function calculates each hotspot position on the screen
function generate_hotspot_xy(){
  int i;
  Hotspot *h;
  
  i=0;
  while(i<MAX_HOTSPOTS_PER_ROOM){
    hotspot_minx[i]=999;
    hotspot_miny[i]=999;
    hotspot_maxx[i]=0;
    hotspot_maxy[i]=0;
    hotspot_x[i]=-1;
    hotspot_y[i]=-1;
    i++;
  }
  
  int x=0;
  int y=0;
  int step=3;
  int h_id;
  
  //we will walk the screen in steps
  //get the hotspot at the x,y point
  //and note down the smaller and bigger
  //x,y position for each hotspot
  while(y<System.ViewportHeight - 1){
    while(x<System.ViewportWidth - 1){

      h = Hotspot.GetAtScreenXY(x, y);
      h_id = h.ID;
      if(h_id>0){
        if(x < hotspot_minx[h_id] ){
          hotspot_minx[h_id] = x;
        } 
        
        if(y < hotspot_miny[h_id]){
          hotspot_miny[h_id] = y;
        } 
        
        if(x > hotspot_maxx[h_id]){
          hotspot_maxx[h_id] = x;
        } 
        
        if(y > hotspot_maxy[h_id]){
          hotspot_maxy[h_id] = y;
        } 
      }
      
      
      x+=step;
    }
    y+=step;
  }
  
  //using the previously obtained max and min x and y values
  //we calculate the center of the hotspot
  i=0;
  while(i<MAX_HOTSPOTS_PER_ROOM){
    hotspot_x[i]=hotspot_minx[i]+(hotspot_maxx[i]-hotspot_minx[i])/2;
    hotspot_y[i]=hotspot_miny[i]+(hotspot_maxy[i]-hotspot_miny[i])/2;
    i++;
  } 
}


//everytime a room is loaded, we must recalculate it's hotspot position.
function on_event (EventType event, int data)
{
  if (event == eEventEnterRoomBeforeFadein){
    generate_hotspot_xy();
  } 
}


[close]

Fullcode InteractionDetector.ash
Spoiler

Code: ags

// new module header
#define MAX_HOTSPOTS_PER_ROOM 49

struct InteractionDetector
{
  import static void show();
  import static void hide();  
  import static void toggle();  
  import static bool isShown();  
};

[close]

EDIT:

Fixed Code Below! Thanks Khris!

InteractionDetector.asc
Spoiler
Code: ags

// new module script
DynamicSprite* interactionSprite;
DrawingSurface * interactionSurface;
Overlay* interactionOverlay ;
bool interactionShown;

int hotspot_minx[MAX_HOTSPOTS_PER_ROOM];
int hotspot_maxx[MAX_HOTSPOTS_PER_ROOM];
int hotspot_miny[MAX_HOTSPOTS_PER_ROOM];
int hotspot_maxy[MAX_HOTSPOTS_PER_ROOM];
int hotspot_x[MAX_HOTSPOTS_PER_ROOM];
int hotspot_y[MAX_HOTSPOTS_PER_ROOM];

//this is the function used to draw each label
function draw_label(String text, int textx,  int texty){
  int textcolor = Game.GetColorFromRGB(216,216, 216);
  if(textx+GetTextWidth(text, eFontSmallFont)+4>Room.Width){
    textx=Room.Width-GetTextWidth(text, eFontSmallFont)-4;
  }
  interactionSurface.DrawingColor = textcolor;
  interactionSurface.DrawString(textx, texty, eFontSmallFontOutlined, text);
}

//this function shows the interaction detector when pressed
static void InteractionDetector::show() {  
  int obj_count = Room.ObjectCount;
  int cha_count = Game.CharacterCount;
  int i;
  int textx,  texty;
  String text;
  interactionShown = true;
  interactionSprite = DynamicSprite.Create(Room.Width, Room.Height);
  interactionSurface = interactionSprite.GetDrawingSurface();
  Character *c;

  
  i=0;
  while(i<obj_count){
    if(object[i].Visible && object[i].Clickable){
      text = object[i].Name;
      textx=object[i].X;
      texty=object[i].Y;
      draw_label(text, textx, texty);
    }
    i++;
  }
  
 
  i=0;
  while(i<MAX_HOTSPOTS_PER_ROOM){
    text = hotspot[i].Name;
    if(hotspot[i].Enabled){
      if(text.Length > 1 && text.IndexOf("Hotspot") == -1){
        textx=hotspot_x[i];
        texty=hotspot_y[i];
        
        draw_label(text, textx, texty);
      }      
    }
    i++;
  }
  
  
  i=0;
  while (i < cha_count) {
    c = character[i];
    if (c.Room == player.Room && c.Clickable && c.ID != player.ID) {
      ViewFrame *cviewframe = Game.GetViewFrame(c.View, c.Loop, c.Frame);
      textx = c.x;
      texty = c.y - Game.SpriteHeight[cviewframe.Graphic]*c.Scaling/2*100;
      text = c.Name;
      if(text.Length>2){
        draw_label(text, textx, texty);
      }
    }
    i++;
  }
  
  interactionSurface.Release();
  interactionOverlay= Overlay.CreateGraphical(0, 0, interactionSprite.Graphic, true);
}

//hides the Interaction Detector Overlay
static void InteractionDetector::hide() { 
  interactionOverlay.Remove();
  interactionSprite.Delete();
  interactionShown = false;
}

//external function to check if the Interaction Detector Overlay is already shown
static bool InteractionDetector::isShown() {  
  return interactionShown;
}

//this allows toglin on and off the Interaction Detector Overlay
static void InteractionDetector::toggle() {  
  if(interactionShown){
    InteractionDetector.hide();
  } else {
    InteractionDetector.show();
  }
}

//this function calculates each hotspot position on the screen
function generate_hotspot_xy(){
  int i;
  Hotspot *h;
  
  i=0;
  while(i<MAX_HOTSPOTS_PER_ROOM){
    hotspot_minx[i]=999;
    hotspot_miny[i]=999;
    hotspot_maxx[i]=0;
    hotspot_maxy[i]=0;
    hotspot_x[i]=-1;
    hotspot_y[i]=-1;
    i++;
  }
  
  int x=0;
  int y=0;
  int step=3;
  int h_id;
  
  //we will walk the screen in steps
  //get the hotspot at the x,y point
  //and note down the smaller and bigger
  //x,y position for each hotspot
  while(y<System.ViewportHeight - 1){
    x=0;
    while(x<System.ViewportWidth - 1){

      h = Hotspot.GetAtScreenXY(x, y);
      h_id = h.ID;
      if(h_id>0){
        if(x < hotspot_minx[h_id] ){
          hotspot_minx[h_id] = x;
        } 
        
        if(y < hotspot_miny[h_id]){
          hotspot_miny[h_id] = y;
        } 
        
        if(x > hotspot_maxx[h_id]){
          hotspot_maxx[h_id] = x;
        } 
        
        if(y > hotspot_maxy[h_id]){
          hotspot_maxy[h_id] = y;
        } 
      }
      
      
      x=x+step;
    }
    y=y+step;
  }
  
  //using the previously obtained max and min x and y values
  //we calculate the center of the hotspot
  i=0;
  while(i<MAX_HOTSPOTS_PER_ROOM){
    hotspot_x[i]=hotspot_minx[i]+(hotspot_maxx[i]-hotspot_minx[i])/2;
    hotspot_y[i]=hotspot_miny[i]+(hotspot_maxy[i]-hotspot_miny[i])/2;
    i++;
  } 
}


//everytime a room is loaded, we must recalculate it's hotspot position.
function on_event (EventType event, int data)
{
  if (event == eEventEnterRoomBeforeFadein){
    generate_hotspot_xy();
  } 
}
[close]


InteractionDetector.ash
Spoiler
Code: ags

// new module header
#define MAX_HOTSPOTS_PER_ROOM 49

struct InteractionDetector
{
  import static void show();
  import static void hide();  
  import static void toggle();  
  import static bool isShown();  
};

[close]


EDIT2:

This is my final version that solves label colision:

InteractionDetector.asc

Spoiler
Code: ags

// new module script
DynamicSprite* interactionSprite;
DrawingSurface * interactionSurface;
Overlay* interactionOverlay ;
bool interactionShown;

int hotspot_minx[MAX_HOTSPOTS_PER_ROOM];
int hotspot_maxx[MAX_HOTSPOTS_PER_ROOM];
int hotspot_miny[MAX_HOTSPOTS_PER_ROOM];
int hotspot_maxy[MAX_HOTSPOTS_PER_ROOM];
int hotspot_x[MAX_HOTSPOTS_PER_ROOM];
int hotspot_y[MAX_HOTSPOTS_PER_ROOM];


int textcolor;

int label_index;
String scheduled_text[MAX_LABELS];
bool scheduled_text_enabled[MAX_LABELS];
int scheduled_text_x[MAX_LABELS];
int scheduled_text_y[MAX_LABELS];

//this is the function used to draw each label
function schedule_drawing(String text, int textx,  int texty){
  scheduled_text[label_index]=text;
  scheduled_text_enabled[label_index]=true;
  scheduled_text_x[label_index] = textx;
  if(textx+GetTextWidth(text, eFontSmallFont)+4>System.ViewportWidth+40){
    scheduled_text_enabled[label_index]=false; 
  }
  
  if(textx+GetTextWidth(text, eFontSmallFont)+4>System.ViewportWidth){
    scheduled_text_x[label_index]=System.ViewportWidth-GetTextWidth(text, eFontSmallFont)-4;
  }
  scheduled_text_y[label_index] = texty;
  label_index++;
}


function draw_label(String text, int textx,  int texty){

  interactionSurface.DrawingColor = textcolor;
  interactionSurface.DrawString(textx, texty, eFontSmallFontOutlined, text);
}

function fix_label_positions(){
  int i;
  int j;
  i=0;
  j=0;
  int textwidth;
  while(i<MAX_LABELS){
    if(scheduled_text_enabled[i]){
      textwidth=GetTextWidth(scheduled_text[i], eFontSmallFont) ;
      j=0;
      while(j<MAX_LABELS){
        if(scheduled_text_enabled[j] && i!=j){
           if(scheduled_text_y[i]<scheduled_text_y[j]+9 && scheduled_text_y[i]>scheduled_text_y[j]-9){
             if(scheduled_text_x[i]<textwidth+scheduled_text_x[j]+8 && scheduled_text_x[i]>scheduled_text_x[j]-textwidth-8){
               scheduled_text_y[i]=scheduled_text_y[i]-6;
               scheduled_text_y[j]=scheduled_text_y[j]+2;
               break;
             }
           }
        }
        j++;
      }
    }
    i++;
  }  
}

function draw_all_labels(){
  textcolor = Game.GetColorFromRGB(216,216, 216);  
  int i;
  fix_label_positions();
  fix_label_positions();
  
  i=0;
  while(i<MAX_LABELS){
    if(scheduled_text_enabled[i]){
      draw_label(scheduled_text[i] ,scheduled_text_x[i], scheduled_text_y[i]);
    }
    i++;
  }
}


//this function calculates each hotspot position on the screen
function generate_hotspot_xy(){
  int i;
  Hotspot *h;
  
  i=0;
  while(i<MAX_HOTSPOTS_PER_ROOM){
    hotspot_minx[i]=999;
    hotspot_miny[i]=999;
    hotspot_maxx[i]=0;
    hotspot_maxy[i]=0;
    hotspot_x[i]=-1;
    hotspot_y[i]=-1;
    i++;
  }
  
  int x=0;
  int y=0;
  int step=3;
  int h_id;
  
  //we will walk the screen in steps
  //get the hotspot at the x,y point
  //and note down the smaller and bigger
  //x,y position for each hotspot
  while(y<System.ViewportHeight - 1){
    x=0;
    while(x<System.ViewportWidth - 1){

      h = Hotspot.GetAtScreenXY(x, y);
      h_id = h.ID;
      if(h_id>0){
        if(x < hotspot_minx[h_id] ){
          hotspot_minx[h_id] = x;
        } 
        
        if(y < hotspot_miny[h_id]){
          hotspot_miny[h_id] = y;
        } 
        
        if(x > hotspot_maxx[h_id]){
          hotspot_maxx[h_id] = x;
        } 
        
        if(y > hotspot_maxy[h_id]){
          hotspot_maxy[h_id] = y;
        } 
      }
      
      
      x=x+step;
    }
    y=y+step;
  }
  
  //using the previously obtained max and min x and y values
  //we calculate the center of the hotspot
  i=0;
  while(i<MAX_HOTSPOTS_PER_ROOM){
    hotspot_x[i]=hotspot_minx[i]+(hotspot_maxx[i]-hotspot_minx[i])/2;
    hotspot_y[i]=hotspot_miny[i]+(hotspot_maxy[i]-hotspot_miny[i])/2;
    i++;
  } 
}

//this function shows the interaction detector when pressed
static void InteractionDetector::show() {  
  int obj_count = Room.ObjectCount;
  int cha_count = Game.CharacterCount;
  int i;
  int textx,  texty;
  String text;
  interactionShown = true;
  interactionSprite = DynamicSprite.Create(System.ViewportWidth, System.ViewportHeight);
  interactionSurface = interactionSprite.GetDrawingSurface();
  Character *c;
  
  label_index=0;
  generate_hotspot_xy();

  i=0;
  while(i<MAX_LABELS){
    scheduled_text_enabled[i] = false;
    i++;
  }
  
  i=0;
  while(i<obj_count){
    if(object[i].Visible && object[i].Clickable){
      text = object[i].Name;
      textx=object[i].X-GetViewportX();
      texty=object[i].Y-GetViewportY();
      schedule_drawing(text, textx, texty);
    }
    i++;
  }
  
 
  i=0;
  while(i<MAX_HOTSPOTS_PER_ROOM){
    text = hotspot[i].Name;
    if(hotspot[i].Enabled){
      if(text.Length > 1 && text.IndexOf("Hotspot") == -1){
        textx=hotspot_x[i];
        texty=hotspot_y[i];
        
        schedule_drawing(text, textx, texty);
      }      
    }
    i++;
  }
  
  
  i=0;
  while (i < cha_count) {
    c = character[i];
    if (c.Room == player.Room && c.Clickable && c.ID != player.ID) {
      ViewFrame *cviewframe = Game.GetViewFrame(c.View, c.Loop, c.Frame);
      textx = c.x-GetViewportX();
      texty = c.y - Game.SpriteHeight[cviewframe.Graphic]*c.Scaling/2*100-GetViewportY();
      text = c.Name;
      if(text.Length>2){
        schedule_drawing(text, textx, texty);
      }
    }
    i++;
  }
  
  draw_all_labels();
  interactionSurface.Release();
  interactionOverlay= Overlay.CreateGraphical(0, 0, interactionSprite.Graphic, true);
}

//hides the Interaction Detector Overlay
static void InteractionDetector::hide() { 
  interactionOverlay.Remove();
  interactionSprite.Delete();
  interactionShown = false;
}

//external function to check if the Interaction Detector Overlay is already shown
static bool InteractionDetector::isShown() {  
  return interactionShown;
}

//this allows toglin on and off the Interaction Detector Overlay
static void InteractionDetector::toggle() {  
  if(interactionShown){
    InteractionDetector.hide();
  } else {
    InteractionDetector.show();
  }
}



//everytime a room is loaded, we must recalculate it's hotspot position.
function on_event (EventType event, int data)
{
  if (event == eEventLeaveRoom){
    if(interactionShown){
      InteractionDetector.hide();
    }
  } 
}

[close]
#160
Hello,

I am having some reports from people (the game artist and musician) playtesting our game that they are having trouble with playing the game in Full screen in Windows 10. I tested the game on virtual machines and had no problem with Windows XP, Windows 7. In virtual machines, Windows Server 2012 and Windows 10 didn't launch the game in full screen. The only PC that's Windows 10 I have access to, which is my girlfriend's, the game worked. Has anyone had problems with full screen and Windows10?
SMF spam blocked by CleanTalk