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

Messages - timid_dolphin

#1
I've got a few helper functions which I think would be pretty much universally useful for making adventure games.

For example, this basic but super useful function:

Code: ags

function replaceInvItem(InventoryItem* turnThis, InventoryItem* intoThis)
{
  int index;
  for (int i = 0; i < invCustom.ItemCount; i++) {
    if (invCustom.ItemAtIndex[i] == turnThis) index = i;
  }
  player.LoseInventory(turnThis);
  player.AddInventory(intoThis, index);
}


All it does is replace one inventory item with another at the same index, so if you're mixing drinks in the inventory, you'd just call:

Code: ags

replaceInvItem(iTomatoJuice, iBloodyMary);
player.loseInventory(iVodka);


Also note that invCustom needs to be changed to whatever the name of your inventory window is.

EDIT: My sincere apologies, I'm going to get myself into trouble by not reading the forum rules. I now realise this doesn't fit the guidelines for a module on this board.
I have a few of these handy functions which I think would be really helpful, especially for beginners, I was thinking of putting them together in a sort of handy-helper library.

They include handy functions that:
-capitalise the first letter of a string
-highlight the inventory cursor over a hotspot
-Character.InRect()  -check if a character is within a rectangle

If there's interest in this sort of thing, maybe we can put together an open source collection of handy helper functions?
#2
Awesome! Glad you liked it - thanks for the motivation to figure out that object structuring stuff in AGS.

I'm learning to code for the first time so it's satisfying to apply some of the concepts I've been learning with webdev stuff in AGS!
#3
Quote from: imagazzell on 01 Dec 2021, 05:49
Hey, timid_dolphin. I like the sound of this approach for the organization and streamlined scripting, but I'm a little unclear on how to set up the custom properties (i.e., what to set as the type and value, etc.). Are you simply setting the type to "number" and the value to the number of the appropriate view?

Your instincts are absolutely correct!
#4
Good question!

someone else could correct me here, but I believe the answer is: you can't, at least not without meddling with the module script.

Here's what I'd do:
1 - If you want to automate the cursor in just one room, create an object using the cursor sprite, if you want this in several rooms, make it a character.

2 - Hide the cursor at the same time make the object / character visible at the same screen position as the cursor.

3 - Tween that fake cursor!

Hope this helps.
#5
Ok, so here's how it works:

first you need to go into the character editor and add a custom property to the schema for your alternate views, eg angryView. For each character you can initialise this in the character editor with the view you want to use.

Then in your script file with all of your character animation stuff, add this function:
Code: ags

function angry(this Character*)
{
  this.SpeechView = this.GetProperty("angryView");

}


and in the header for this file:

Code: ags

import function angry(this Character*);


now you can just put cBob.angry(); so switch to the angry speechview. It won't switch back though so you need another function to go back to the default.
This is a lot better for me because previously i had separate functions for every character, which clutters up the autopredict menu. It's also a neater object orientated way of organising the code.
#6
following imagazzell's thread about custom character animation, I've been trying to streamline my own system.

It would be great if I could call cBob.angry(); to switch bob's speechView to the angry view.

I've finally got my head around extender functions, but what i need is a custom property which stores the angry view so the function can call this.ChangeView(this.angryView);

kind of like this:
Code: ags

function angry(this Character*)
{
this.ChangeView(this.angryView);
}


Is it possible to add properties to all characters so this would work?

Edit: I actually figured this out moments after posting this. Doh!

so in the edit character dialogue, you can edit the schema, which adds a custom property for all characters. Pretty easy!
#7
I like to categorise my functions and keep them out of the main script where possible.

So I have a script which has all of my custom character animations, as well as costume changes, so all of the logic around that stuff is wrapped up in neat little functions.

I think it's a good idea to spend a bit of time sorting out these things at the start so when it comes to writing dialogue you can just drop in different gestures and expressions on the fly, it means you can get away with writing less when you can communicate something more directly with 'acting'!

If you can figure out how to do it (I'm not quite there yet!) these functions should be put inside the character object, so instead of paulAngry(); you would call cPaul.angry();
#8
I've got a bunch of general mood functions in a script file with all of my little character animations.
So all of the logic around changing character views is encapsulated in these, and they can be easily called from anywhere in the script, or within dialogue scripts.

eg:
  paulThinking();
PAUL: Hey, wait a second...
  paulAngry();
PAUL: That guy didn't pay for those bananas!
  paulShakeFist();
  paulSigh();
  paulNeutral();
PAUL: Oh well...
#9
Thanks!

I may not even use this as a rain machine, but it's been cool to learn about all this stuff, there's so much potential for other weird special effects, like you could use parts of this function to make the whole room drip with blood and melt away or something, or VHS glitch effects?
#10
No worries, I'm happy to help!

I think AGS is really well designed, and as with any coding thing, the more you work with it, the more sense it'll make.
Soon enough, you'll be looking over your old code thinking - why did I do it that way? And you'll rewrite the whole thing in five minutes using half the lines.

Good luck with your project!
#11
This feels weird replying to myself to help myself, perhaps reason to spend a little longer reading the manual before posting in this forum. I'll do it anyway, because this could be helpful to other beginners who want to get into the cool weird parts of the AGS engine!

so I create a DynamicSprite from a fully transparent screen sized sprite (slot 1234),

make a DrawingSurface from the DynamicSprite,

then the for-loop creates raindrops from the background image and draws them onto the DrawingSurface one pixel up from the source.

then I create a graphical Overlay from the background sprite which has been drawn on.

There's a timer there which slows them down a bit so they're not flickering every game loop but closer to a film frame rate.

It looks about as good as I would expect at this basic level. More could be done to make it more generalised and adaptable for different purposes.

It also needs another layer of 'splash' effects as rain drops hit the ground, which could be a variation on the same algorythm but with single pixels randomly appearing within a  few defined rectangles representing flat surfaces.

The effect could be more improved with customised animations of slower drips coming off the edges of surfaces etc.

I'm certain it will look cool when it's done!

Code: ags

//rainDrop1 and rainOverlay are defined as global variables

int howMuchRain = 10;             //to make 10 raindrops at a time
int rainDropLength = 20;          //each drop is 1 pixel wide, 20 pixels long

DynamicSprite *rainBackground;
DrawingSurface *rainDrawingSurface;

function makeItRain()
{
  if(!IsGamePaused())
  {
    if(rainDrawingSurface != null)
    {
      rainDrawingSurface.Clear();
    } else {
      rainBackground = DynamicSprite.CreateFromExistingSprite(1234);
      rainDrawingSurface = rainBackground.GetDrawingSurface();
    }
      if(Game.DoOnceOnly("initRain")){
      SetTimerWithSeconds(13, 0.1);
    }
    if(IsTimerExpired(13))
    {
      for (int i = 0; i < howMuchRain; i++)
      {
        int coordX =  Game.Camera.X + 1 + Random(Game.Camera.Width - 2);
        int coordY =  Game.Camera.Y + 1 + Random(Game.Camera.Height - rainDropLength - 2);       
        rainDrop1 = DynamicSprite.CreateFromBackground(GetBackgroundFrame(), coordX, coordY, 1, rainDropLength);       
        rainDrop1.Tint(1, 1, 1, 30, 100);
        rainDrawingSurface.DrawImage(coordX, coordY-1, rainDrop1.Graphic);
      }
      rainOverlay = Overlay.CreateGraphical(0, 0, rainBackground.Graphic, true);
     
      SetTimerWithSeconds(13, 0.15);
    }   
  }
}

function late_repeatedly_execute_always()
{
  makeItRain();
}


#12
Hi Malcoladdin!
let me see if I can help.

My first suggestion is to organise your code a little differently. The way you've laid it out is perfectly fine in terms of functionality, but you can make your code more readable by taking a 'function based' approach.

What I would do is break things down into functions, where each function does one thing, which is labelled very clearly and unambiguously, for example:

Code: ags

function samWarning()
{
  cSamuel.Walk(70, 197, eBlock, eWalkableAreas);
  cSamuel.Say ("Stay out of the mine! It is dangerous!");
}

function samWalk()
{
  if (cSamuel.x==70)
  {
  cSamuel.AddWaypoint(286, 183);
  } else if (cSamuel.x==286)
  {
  cSamuel.AddWaypoint(161, 187);
  } else if (cSamuel.x==161)
  {
  cSamuel.AddWaypoint(286, 193);
  }
}

function room_RepExec()
{
  samWalk();
}

function room_FirstLoad()
{
  samWarning();
}



So now we have our two functions basically behaving how we want, it's easier to test their behaviour. I suspect that the problem is that we're calling samWarning() from room_FirstLoad(), but we want to call it every time the player enters the room. There's also a room_Load and a room_AfterFadeIn function, these are called every time the player enters the room. You can access these in the Room Events panel in the Room Edit window.

You should put samWalk() into room_AfterFadeIn, as you can't put blocking functions before the fade-in.

I hope this helps!


EDIT: I reread your code and I think I understand what you want a bit better. Do you only want Samuel's warning to happen the first time the room loads, and after this you want the walking back and forth behaviour?

If this is the case, you should keep samWarning() in room_FirstLoad(), and figure out the conditions for every other room load. For example in room_AfterFadeIn() you could check to see if this character is moving with cSamuel.Moving, eg:

Code: ags


function sendSamToLoop()
{
  if(!cSamuel.Moving)                     //checks if Samuel is not moving using the logical NOT operator !
{
  cSamuel.AddWayPoint(70, 197);   //send him to the start of the loop you set up in samWalk()
}
}

function room_AfterFadeIn()
{
  sendSamToLoop();
}




Also - as your code gets more complicated, you can call functions from your own functions to keep your code organised, eg:

Code: ags


function doOneThing()
{
  //bla bla bla code
}

function doSomeOtherThing()
{
  //etc etc etc
}

function complicatedFunction()
{
  doOneThing();
  doSomeOtherThing();
}



This way when you need to fix something you can focus on one thing at a time, and you can even reuse the same block of code somewhere else when it makes sense!
#13
Okay, after going through a lot of old forum posts and rereading the manual a thousand times, I think I'm starting to get my head around these things.

For now I'm testing this in a global late_repeatedly_execute_always()

It's beginning to look kind of ok. I really want the for loop to render x amount of raindrops, and it seems to only do one at a time each time this function is called.
But if I remove the for loop, the 'rain' stops when the player exits the first room.
Any ideas why it's behaving this way?

(DynamicSprite raindrop1 and Overlay surface are defined as in global variables.)

Code: ags

int howMuchRain = 10;             //to make 10 raindrops at a time
int rainDropLength = 50;          //each drop is 1 pixel wide, 50 pixels long

function makeItRain()
{
  if(!IsGamePaused())
  {
    if(rainDrop1 != null)
    {
    //  rainSurface.Remove();
    }

     for (int i = 0; i < howMuchRain; i++)
    { 
      int coordX =  Game.Camera.X + 1 + Random(Game.Camera.Width - 2);
      int coordY =  Game.Camera.Y + 1 + Random(Game.Camera.Height - rainDropLength - 2);
     
      rainDrop1 = DynamicSprite.CreateFromBackground(GetBackgroundFrame(), coordX, coordY, 1, rainDropLength);
      rainDrop1.Tint(1, 1, 1, 30, 100);
      rainSurface = Overlay.CreateGraphical(coordX - Game.Camera.X, coordY - 1 - Game.Camera.Y,  rainDrop1.Graphic, true);
      //raindrop1.Delete();
    }
  }
}

#14
I've been working on a new rain module which will suit my aesthetic a bit better than some of the other ones out there.

After looking at a lot of pixel art, I find the most realistic ones don't animate the raindrops, but just randomly place them, but simulate refraction of the light and slightly desaturate and lighten from the background.

I think I'm on the right track, but there isn't a lot of detailed information about DynamicSprite and drawing surfaces (If I'm incorrect, please direct me to the relevant part of the tutorial!)

My code is posted below, I think that I need to create an overlay and draw a bunch of raindrops onto it, then reset it for the next rain. should be pretty lightweight if it works!

Thanks in advance to anybody who can help me with this!


Code: ags


function makeItRain(){
 
  //  set/reset overlay, using a sprite which is the same as the screensize, transparent colour
  //  overlay  = sprite 1234, etc...
 
  int howMuchRain = 10;             //to make 10 raindrops at a time
  int rainDropLength = 20;          //each drop is 1 pixel wide, 20 pixels long
 
  for (int i = 0; i < howMuchRain; i++)
  { 
    int coordX = Random(359);
    int coordY = Random(170);       //find random places for the raindrops
   
  DynamicSprite* raindrop1 = DynamicSprite.CreateFromBackground(GetBackgroundFrame(),
                                                                coordX, coordY, 1, rainDropLength);
                                                                //makes a raindrop from the background image
                                                                //would be better if it was from a screenshot,
                                                                //so includes objects, characters
  raindrop1.Tint(1, 1, 1, 20, 100); //slightly lighten and desaturate each drop

  DrawingSurface *surface = myOverlay;  //draw on the overlay
 
  surface.DrawImage(coordX, coordY-1, raindrop1.Graphic);       //place the raindrop 1 pixel higher than it's source
                                                                //creating a light refraction effect
  surface.Release();
  raindrop1.Delete(); 
  }
}

[/code/
#15
Hey, that template looks great!

Not quite what I'm after though, I really like the user experience of the Modernized VerbCoin template which game with AGS- it's pleasingly minimal.
Is there any documentation or a thread about that particular template? I've been searching but haven't been able to find it.

It seems like the Modernized Verb is hardwired to support four verb buttons, but I'd love to be proven wrong on that!
#16
Hi all,
this is my first post after lurking for a while.

I've been experimenting with customizing the "modernized VerbCoin" in the template which comes with the latest version of AGS. I love the template, it just needs some new sprites to match the aesthetic of the game I'm creating. However, I really want six verbs on the coin. I've been analysing the code and don't really understand how it positions the buttons, or how I would go about adding more?

SMF spam blocked by CleanTalk