So has a kyr system been created?

Started by Imc7r, Tue 20/02/2018 16:55:52

Previous topic - Next topic

Crimson Wizard

#60
Quote from: Imc7r on Wed 23/05/2018 23:27:36
The itemIsSelected == true is because it can be selected and then if no Inventory, the clicking on Ground leaves it on the ground (returns the character back to the player room and restores cursor).

You may do the same by testing "if (pickedItem != null)" which would mean that some item is selected. I am not sure if a separate boolean variable required.

I do not see if you clear pickedItem anywhere, it has to be set to null if you either put it back or add to inventory.

Quote from: Imc7r on Wed 23/05/2018 23:27:36
Alas, nothing happens, there, where I change the cursor I also change the pickedItem = iKey; but still does not show up in inventory. Anything wrong with my InventoryWindow on gIconbar? Its height is 88 and the item icon is 15.


Hm, I do not see anything else wrong with the shown script.
Could you try giving player anything upon some simple event (like a key press) and see if inventory window will display it?

Things to double check:
- InvWindow is actually linked to your player character (CharacterID property).
- InvWindow has valid ItemWidth and ItemHeight values.
- Inventory item itself actually has assigned sprite (Image property).



Quote from: Imc7r on Wed 23/05/2018 23:27:36
The itemIsSelected == true is because it can be selected and then if no Inventory, the clicking on Ground leaves it on the ground (returns the character back to the player room and restores cursor).
This made that problem though and it doesnt matter if my objects/ dynamic sprites are huge or not, since anyone can try to click on a dynamic sprite - if the item is dropped on some dynamic sprite it can be hidden behind and never to be picked up again (funny that's why Kyrandia used some bounce effect where the item bounces like a ball to a certain location and can't be put exactly where you click). One way is setting the Baseline which would be unrealistic like putting it on top of everything or transparency at least to show it is two objects overlapping.

But then I would need to detect not the objects since they cover large parts, but the Dynamic sprites or graphic.
I think you are already using formulas to get the actual tree graphic position in relation to room coordinates - when calculating Baseline and blocking object's position. You may use similar logic to find a bounding box around tree (not necessarily precise), and if player leaves item there, then make it move towards nearby free area. Not sure what's the best way of finding that free area though.

For starters you could simply cancel item drop in such case. Or define number of preset locations in the room, and choose closest one.

Crimson Wizard

Oh wait, I am stupid. eMouseLeftInv only works when you click on actual item in inventory, not on the inventory itself.

So, yes, you actually need to find out whether you clicked in the InvWindow.

First of all, find out if you have "Override build-in inventory click handling" in General Settings.

Crimson Wizard

#62
Alright, my apologies, this is one of the AGS quirks I forget all the time.

I am bit tired right now, so may as well miss something else, but apparently AGS does not send on_mouse_click events when player clicks on the GUI, at all. The only exception is when player clicks on actual item in inventory window (then it calls on_mouse_click with eMouseLeftInv).

So far the only way I found that works, is to track mouse button state in repeatedly_execute:
Code: ags

function repeatedly_execute()
{
    GUIControl *inv = GUIControl.GetAtScreenXY(mouse.x, mouse.y);
    if (gIconbar.Visible)
    {
        if (inv == InventoryWindow && mouse.IsButtonDown(eMouseLeft))
        {
	    // do something
        }
    }
}


Note that this script example above is not perfect, because it will continiously trigger so long as you keep mouse button down. So probably it should be expanded, remembering mouse button state (was down or up) and perform actions only once, when it was just pressed down or released.


UPDATE:
Alright, another correction.
You may actually receive clicks to the GUI by adding OnClick event handler.
Problem is, that this event only fires when you click on GUI background, not on its controls. So it won't work right in your case.
And InvWindow control does not have a OnClick event on its own, sadly.
If you do not like the solution with catching clicks in repeatedly_execute, then only other solution I currently see is to script custom inventory drawing right on GUI background (using another dynamic sprite). Then you will be able to handle clicks using gIconbar's OnClick event.
Perhaps this is not worth it without other reasons.

Khris

I didn't read the last few posts so in case this doesn't apply, please ignore.

There's on_event / eEventMouseGUIUp/Down, which I've used in the past to handle arbitrary GUI clicks.

Crimson Wizard

#64
Quote from: Khris on Thu 24/05/2018 11:21:03
There's on_event / eEventMouseGUIUp/Down, which I've used in the past to handle arbitrary GUI clicks.

Yes, I can confirm, this detects click on InvWindow also! It even works if you click on inventory item.
Although, I do not see a way to know which mouse button was pressed/released without mouse.IsButtonDown.

Code: ags

function on_event(EventType event, int data)
{
    if (event == eEventGUIMouseDown && data == gIconbar.ID)
    {
        GUIControl *inv = GUIControl.GetAtScreenXY(mouse.x, mouse.y);
        if (inv == InventoryWindow && mouse.IsButtonDown(eMouseLeft) && !mouse.IsButtonDown(eMouseRight))
        {
            // do something
        }
    }
}


(Damn, all those different methods in AGS are confusing...)

Imc7r

#65
Yes override-inventory is True in General Settings. And yes I use itemIsSelected for other functions. I still use the code for how to drop the item on the ground as I have it here but Im ready to start a new function specially for clicking within the Inventory window.

I won't restrict the button you can click inventoryWindow on, so as long as Left works, any other working is even better. I will set pickedItem to null for when placed on the ground or in inventory. Yes the code works, the item is now created! Thanks for that code both Crimson Wizard and Khris!

Works:
Spoiler
Code: ags
function on_event(EventType event, int guiType)
{
    if (event == eEventGUIMouseDown && guiType == gIconbar.ID && itemIsSelected == true)
    {
        GUIControl *inv = GUIControl.GetAtScreenXY(mouse.x, mouse.y);
        if (inv == InventoryWindow && mouse.IsButtonDown(eMouseLeft) || mouse.IsButtonDown(eMouseRight))
        {
          player.AddInventory(pickedItem);
          itemIsSelected = false;
          mouse.ChangeModeGraphic(Mouse.Mode, 2061);
          pickedItem = null;
        }
    }
}
[close]

Now for the other part, taking from inventory event and similar actions as dropping on ground. This below doesn't register the pick up, I suppose since Im not on the default Mode for picking up an item. I tried few other places but now i cant pick it up from the inventory xD , this event is for 'Other click on inv item' event of the item, im on my usual custom cursor mode.

Not working next posts

QuoteI think you are already using formulas to get the actual tree graphic position in relation to room coordinates - when calculating Baseline and blocking object's position. You may use similar logic to find a bounding box around tree (not necessarily precise), and if player leaves item there, then make it move towards nearby free area. Not sure what's the best way of finding that free area though.

Yes I will see, the problem is that when I used Collision comparison it didn't work even though I put it under For loop and checked with every object, it worked on one object but the rest didn't. I guess I will play on my own with it, I like the idea to put the item in front of all and 50 transparency, as it still shows 2 objects overlapping and not by making transparent the whole huge object like it's done on other games, just the item itself.

Crimson Wizard

#66
Imc7r, you have a mistake in the code here:

Code: ags

if (inv == InventoryWindow && mouse.IsButtonDown(eMouseLeft) || mouse.IsButtonDown(eMouseRight))


You need to put || operation in brackets, because && operation takes precedence:
Code: ags

if (inv == InventoryWindow && (mouse.IsButtonDown(eMouseLeft) || mouse.IsButtonDown(eMouseRight)))





Quote from: Imc7r on Thu 24/05/2018 22:17:18
Now for the other part, taking from inventory event and similar actions as dropping on ground. This below doesn't register the pick up, I suppose since Im not on the default Mode for picking up an item. I tried few other places but now i cant pick it up from the inventory xD , this event is for 'Other click on inv item' event of the item, im on my usual custom cursor mode.

The best way to know is to put Display("IT WORKS") command there.

But I guess you do not have to use "Other click" event at all. In your on_mouse_click you may catch "eMouseLeftInv" because this definitely what it's meant for.
The currently clicked inventory item may be found using "game.inv_activated" variable. I think it returns item's numeric ID, so to get actual InventoryItem you do:
Code: ags

InventoryItem *clickedOnItem = inventory[game.inv_activated];

Then you may call your function, passing the clicked item as parameter (so no need to do GetAtScreenXY again).

So in the end it may look something like this:
Code: ags

function on_mouse_click(MouseButton button)
{
<....>

    // clicking on item in inventory window
    if (button == eMouseLeftInv)
    {
        if (pickedItem != null)
        { // this means we have picked item from the ground
            <...>
        }
        else if (player.ActiveInventory)
        { // this means we have an item from inventory in the cursor (not sure if you are going to differentiate between these two cases)
            <...>
        }
        else
        { // cursor is just a pointer, so pick the item
             InventoryItem *clickedOnItem = inventory[game.inv_activated];
             itemPickedFromInventory(clickedOnItem);
        }
    }

<....>
}

Imc7r

#67
Hey thanks for the correction with the bracket. I manage the drop on ground elsehwere, it's good. I just needed the correct click detection in inventory, that does it for me as well:

SOLVED - Pick up and Drop Kyr style inventory - thanks for the below post explanation, updated script here to not paste the same over and over
Code: ags

  else if (button == eMouseLeftInv || button == eMouseRightInv)
  {
    pickedItem = InventoryItem.GetAtScreenXY(mouse.x, mouse.y);
    itemIsSelected = true;
    mouse.ChangeModeGraphic(Mouse.Mode, pickedItem.Graphic);
    player.LoseInventory(pickedItem);
    Overlay * pickedItemString = Overlay.CreateTextual(FloatToInt(IntToFloat(System.ViewportWidth) * 0.0104, eRoundNearest), FloatToInt(IntToFloat(System.ViewportHeight) * 0.96, eRoundNearest),  200, Game.SpeechFont, 15, "%s picked.", charClicked.Name);
    Wait(40);
  }


Just for a small q - is there a way to display text at any given moment without pausing the game and animations? Display text pauses it, I use player.Say that creates a small Wait cursor but it would have been nicer if there was a text that just shows up as notification while all else is running.

[WIP o working on Fixing...]
And for now I will be forced to remove my slots drawn at the inventory area, would have been good to limit inventory to 12 for my game and place them at specific slots, but eh, will deal w this another time.

The item is already re-created and the cycle works all over again. If I want to put the item at specific place in the world, this can be further expanded. Thanks for the help with this again, I will work on my drop-behind --tree prevention and will not bother you with other questions for some time.

Crimson Wizard

I think you may simplify your item picking code to the point when you no longer need switch, because you may get every value from the "item" pointer:
Code: ags

mouse.ChangeModeGraphic(Mouse.Mode, item.CursorGraphic); // or alternatively -- item.Graphic
itemIsSelected = true;
pickedItem = item;
player.LoseInventory(item);


Quote from: Imc7r on Fri 25/05/2018 14:40:01
Just for a small q - is there a way to display text at any given moment without pausing the game and animations? Display text pauses it, I use player.Say that creates a small Wait cursor but it would have been nicer if there was a text that just shows up as notification while all else is running.

Overlay.CreateTextual may work for that.
For more complex behavior you may create a GUI with label(s), show it up and remove by timer (see SetTimer).

Imc7r

#69
Hey thanks for these, I updated or simplified some script but having some bugs,  I had to try with 2 items at least (or 2 item-characters at the same time on the ground).  imgI noticed how the SWITCH function (in the pick up) screwed things up cause it ran both cases even if I clicked only one of the two Item chars, it ran the action twice and  changed cursor twice. This was fixed when I use if / if else instead of switch with cases.

Bugs that happen: I already redid my inventory sprite to have circle slots next to each other, so that when you put e.g 82x82 images for inventory icon (respectively mouse cursor graphic when changed) they would fill each circle correctly, by changing the ItemWidth and ItemHeight in the InventoryWindow also to 82, so now items appear correctly. I guess, I can't expect if I used different size images for icons to be equidistant. Since I use the same for cursor and item sprite, I did all these images same 82x82 and hotspot 41.

A bit of a prob is that you have to click always right of existing item in the inventory to get the new item added, if you click on the first item slot it will attempt to add an inventory but will fail, and return to as if you never clicked the inventory, so always have to click way on the right:



I guess a main question would be: using custom properties for chars and items, can I replace "If pickedItem == ikey, set charClicked = Key, if pickedItem == iBracelet, set charVar = Bracelet' with something like set charClicked = "the char, whose Custom value is ..)? This is essential for custom values to have use, if you can replace the direct identification of an item or char with 'The one who has the Custom value of..'

At least I also made the custom GUI using a new background for it, set it Clickable = True, GUI in Room 301 and the buttons with associated script to run and other properties. Is there no way to have a Text paragraph in the GUI? Labels allow you to paste big text but not new paragraphs. I was meaning to add some text as a value directly, without using script.

Im posting my code and some elsewhere throughout this thread for my or anyone's info, since there were some who asked for kyr template in past years.


Code: ags

//Global Script
bool itemIsSelected;
int dialogIncrement[20];
Character * charClicked;
InventoryItem * pickedItem;

function on_mouse_click(MouseButton button)
{
  if (player.Room <= 300)
  {
    if (IsGamePaused() == 1) { }
    else if (itemIsSelected == true && gIconbar.Visible == false && GetLocationType(mouse.x, mouse.y) != eLocationCharacter && GetLocationType(mouse.x, mouse.y) != eLocationHotspot && charClicked.GetTextProperty("Behavior") == "Item")
    {
        if (charClicked == cVioletBerry && season == 1) { charClicked.ChangeView(7); }
        charClicked.ChangeRoom(player.Room);
        charClicked.x = mouse.x;
        charClicked.y = mouse.y;
        itemIsSelected = false;
        mouse.ChangeModeGraphic(Mouse.Mode, 2061);
        
        pickedItem = null;
        if (gMap.Visible == true) { gMap.Visible = false; }
        if (GetLocationType(mouse.x,mouse.y) == eLocationObject)
        { charClicked.Baseline = 10010; charClicked.Transparency = 50;
          Overlay * pickedItemString = Overlay.CreateTextual(charClicked.x, charClicked.y - 10,  600, Game.SpeechFont, 15, "%s", charClicked.Name); Wait(40);
        }
        else { for (int i = 0; i <= 7 && player.Room != 1; i++) { charClicked.Baseline = object[i].Baseline - 1; charClicked.Transparency = 0; } }
    }
    
    else if (button == eMouseLeftInv || button == eMouseRightInv)
    {
      pickedItem = InventoryItem.GetAtScreenXY(mouse.x, mouse.y);
      charClicked = character[pickedItem.GetProperty("ItemCharID")];
      itemIsSelected = true;
      mouse.ChangeModeGraphic(Mouse.Mode, pickedItem.Graphic);
      player.LoseInventory(pickedItem);
      Overlay * pickedItemString = Overlay.CreateTextual(FloatToInt(IntToFloat(System.ViewportWidth) * 0.0104, eRoundNearest), FloatToInt(IntToFloat(System.ViewportHeight) * 0.96, eRoundNearest),  600, Game.SpeechFont, 15, "%s picked.", charClicked.Name);
      Wait(40);
    }
    
    else if (button == eMouseLeft)
    {
      if (Mouse.GetModeGraphic(Mouse.Mode) == 6)
      {
        player.Walk(0,  mouse.y);
      }
      else if (Mouse.GetModeGraphic(Mouse.Mode) == 7)
      {
        player.Walk(System.ViewportWidth,  mouse.y);
      }
      else if (Mouse.GetModeGraphic(Mouse.Mode) == 4)
      {
        player.Walk(mouse.x,  0);
      }
      else if (Mouse.GetModeGraphic(Mouse.Mode) == 1)
      {
        player.Walk(mouse.x,  System.ViewportHeight);
      }
      else if (Mouse.GetModeGraphic(Mouse.Mode) == 37)
      {
        aError.Play();
      }
      else if (GetLocationType(mouse.x, mouse.y) == eLocationCharacter)
      {
        charClicked = Character.GetAtScreenXY(mouse.x, mouse.y);
        
        if (charClicked.GetTextProperty("Behavior") != "Item")
        {
          if (pickedItem != null)
          {
            Room.ProcessClick(mouse.x,  mouse.y,  eModeInteract);
          }
          else
          {
            Room.ProcessClick(mouse.x,  mouse.y,  eModeTalkto);
          }
        }
        else
        {
          Room.ProcessClick(mouse.x,  mouse.y,  eModePickup);
        }
      }
      else if (GetLocationType(mouse.x, mouse.y) == eLocationHotspot)
      {
        Room.ProcessClick(mouse.x,  mouse.y,  eModeInteract);
      }
      else if (GetLocationType(mouse.x, mouse.y) == eLocationObject)
      {
        Object * obj = Object.GetAtScreenXY(mouse.x, mouse.y);
        if (obj.GetTextProperty("Behavior") == "Item") { Room.ProcessClick(mouse.x,  mouse.y,  eModePickup); }
      }      
      
      else { player.Walk(mouse.x,  mouse.y); }
    }
    
    else if (button == eMouseRight)
    {
      if (GetLocationType(mouse.x, mouse.y) == eLocationCharacter)
      {
        Room.ProcessClick(mouse.x,  mouse.y,  eModeLookat);
      }
      else
      {
        dialogIncrement[0]++;
        if (dialogIncrement[0] <= 1)
        {
          player.Say("Talk 1.");  
        }
        else if (dialogIncrement[0] == 2)
        {
          player.Say("Talk 2.");   
        }
        else
        {
          dialogIncrement[0] = 0;
          player.Say("Talk 3.");
        }
      }
    }
  }
  else { Room.ProcessClick(mouse.x,  mouse.y,  eModePointer); }
}

function cItem_PickUp()
{
  if (charClicked.Room != -1) { charClicked.ChangeRoom(-1); }
  itemIsSelected = true;
  Overlay * pickedItemString = Overlay.CreateTextual(FloatToInt(IntToFloat(System.ViewportWidth) * 0.0104, eRoundNearest), FloatToInt(IntToFloat(System.ViewportHeight) * 0.96, eRoundNearest),  600, Game.SpeechFont, 15, "%s picked.", charClicked.Name);
  pickedItem = inventory[charClicked.GetProperty("ItemCharID")];
  if (pickedItem == iVioletBerry && season == 1) { if (charClicked == cVioletBerry) { mouse.ChangeModeGraphic(Mouse.Mode, 72); } } else { mouse.ChangeModeGraphic(Mouse.Mode, pickedItem.Graphic); }
  Wait(40);
}

function itemChar_Interact()
{
  if (charClicked == player)
  {
    if (Mouse.GetModeGraphic(Mouse.Mode) == 3) { charClicked.Say(""); }
    else if (Mouse.GetModeGraphic(Mouse.Mode) == 75) { charClicked.Say(""); gMap.Visible = true; }
    else if (Mouse.GetModeGraphic(Mouse.Mode) == 83) { charClicked.Say(""); }
    else if (Mouse.GetModeGraphic(Mouse.Mode) == 70) { charClicked.Say("Yum! "); mouse.ChangeModeGraphic(Mouse.Mode, 2061); }
    else if (Mouse.GetModeGraphic(Mouse.Mode) == 72) { charClicked.Say(""); mouse.ChangeModeGraphic(Mouse.Mode, 2061); }
    else { charClicked.Say("");  }
  }
  
  else if (charClicked == cNPC)
  {
    if (Mouse.GetModeGraphic(Mouse.Mode) == 3) { }
  
    
    else if (Mouse.GetModeGraphic(Mouse.Mode) == 83) charClicked.Say("Some.");
    else { charClicked.Say("Not it"); }
  }
  
  if (pickedItem == iVioletBerry) { charClicked = null; itemIsSelected = false; pickedItem = null; }
  else { charClicked = character[pickedItem.GetProperty("ItemCharID")]; }

}


Crimson Wizard

Quote from: Imc7r on Sun 27/05/2018 18:40:13
I guess a main question would be: using custom properties for chars and items, can I replace "If pickedItem == ikey, set charClicked = Key, if pickedItem == iBracelet, set charVar = Bracelet' with something like set charClicked = "the char, whose Custom value is ..)? This is essential for custom values to have use, if you can replace the direct identification of an item or char with 'The one who has the Custom value of..'

You may assign character's numeric ID in the item's custom property, then you could do something like:
Code: ags

Character *charForItem = character[item.GetProperty("LinkedCharacter")];

The "character" is a global array of all characters in game, so its safe to use anytime.

Similarily you may do counter link using character's properties
Code: ags

InventoryItem *itemFromCharacter = inventory[ch.GetProperty("LinkedItem")];

Imc7r

#71
Thats what I was looking for, thanks. I was still not good at using such ways of identifying objects. The one thing that doesn't work well is a Hotspot in particular room doesnt fire the event function even though I made a hotspot rectangle on the room and set the event for Interaction and I have in my mouse click if (GetLocationType(mouse.x, mouse.y) == eLocationHotspot) to do Room.ProcessClick(mouse.x,  mouse.y,  eModeInteract); and that's the kind of event that uses the function and still nothing happens. However, as I would use placed objects as well, I can fire it upon the objects that will act as items.

I still have to see how to use more than 1 character of the same type if I wanted to drop 'several same type items' on the ground. With a single item per type it is a single character that appears, I can't duplicate chars it seems.
Also when you click on the inventory to create a 2nd item of the same type, the item is not created, you still remain with the first one. And the code for creating inventory item when at specific cursor graphic is here.

Crimson Wizard


Quote from: Imc7r on Sat 02/06/2018 23:51:46
I was still not good at using such ways of identifying objects. The one thing that doesn't work well is a Hotspot in particular room doesnt fire the event function even though I made a hotspot rectangle on the room and set the event for Interaction and I have in my mouse click if (GetLocationType(mouse.x, mouse.y) == eLocationHotspot) to do Room.ProcessClick(mouse.x,  mouse.y,  eModeInteract); and that's the kind of event that uses the function and still nothing happens.
I've seen there was a code before, but you removed it for some reason. First of all it is necessary to find out what exactly does not work, is it detection or triggering event function, or code inside function. That may be found by placing "Display" calls everywhere and see which is not get called.

Quote from: Imc7r on Sat 02/06/2018 23:51:46
I still have to see how to use more than 1 character of the same type if I wanted to drop 'several same type items' on the ground. With a single item per type it is a single character that appears, I can't duplicate chars it seems.
Since AGS cannot create characters at runtime, only way is to precreate more characters for particular item.
On another hand, if you want to have no limit of number of items of same kind on the ground in the room, then you will have to use "character pool" same way you are using "object pool" for decorations. In other words, not 1 particular character per each type of inventory, but simply a huge number of characters, which you fill into global array, take out as you need an item on ground, and put back as item is removed or you leave the room. Of course this way you would have to save item locations in variables too.
This is where SetProperty come handy, if you follow the character-item linking method mentioned above, you may change character's role on fly.


Quote from: Imc7r on Sat 02/06/2018 23:51:46
Also when you click on the inventory to create a 2nd item of the same type, the item is not created, you still remain with the first one.

Make sure you have "Display multiple icons for multiple items" enabled in General Settings.

Imc7r

^ ah it fails at the event run even but it's OK if the place is buggy I used a workaround where the object has the Item property and he interacts with it instead of the hotspot. I put all my updated related code at the bottom of this post.

QuoteSince AGS cannot create characters at runtime, only way is to precreate more characters for particular item.
On another hand, if you want to have no limit of number of items of same kind on the ground in the room, then you will have to use "character pool" same way you are using "object pool" for decorations. In other words, not 1 particular character per each type of inventory, but simply a huge number of characters, which you fill into global array, take out as you need an item on ground, and put back as item is removed or you leave the room. Of course this way you would have to save item locations in variables too.
This is where SetProperty come handy, if you follow the character-item linking method mentioned above, you may change character's role on fly.

Ah so I will have to use multi chars for same items. Probably I will not have more than 10 or less items of same type. So that's what I thought, even better than making 5 of each possible multiple objects, using same 5 Dummy chars with changed View (to whatever items are to be on the ground) and changing value with Set property and assigning them to whichever items would have to pop as chars on the ground. I think I can handle that myself just need to see a lot of inaccuracies that will occur.

QuoteMake sure you have "Display multiple icons for multiple items" enabled in General Settings

This was the case indeed, thanks


SMF spam blocked by CleanTalk