Point and Click Tutorial: Chapter 5 - Scripting the Inventory Room

From SCI Wiki
Revision as of 18:50, 5 January 2011 by Andrew Branscom (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

Chapter 5 - Scripting the Inventory Room

So here it is in all it's current glory! Script 500, what a surprise about that number eh. All right so I'll be the first to admit that all in all this is actually a pretty hefty script. So let's see if I can break it down into some manageable sections, not only to make it seem less intimidating, but also to see if I can explain what it is we are doing and why.

Inventory Room

Let's start at the beginning, I have included a large section at the very start of the script which describes the necessary steps in order to add a new item to your game, this isn't essential, it is all commented out, but it does make it much easier in the future because all you will need to do is follow the outlined steps. You'll find that I have numbered the edits required in this script to make it easier for you to find those sections in the future. Assuming I have done everything if not efficiently then at least correctly, you shouldn't have to make any changes outside those areas.

Code:
// Inventory Room                                                    Version .36
//****************************************************************************** 
// In general adding inventory items is a pain in the ass, simply because of the
// necessary steps. The process of adding an item requires edit of multiple files
//   => game.sh
//      - Add item to inventory list ex. (define INV_CASH 1 )
//   => Text 500
//      - Place description used for look item in same number as defined
//   => Text 501
//      - Place any miscellaneous interaction responses as needed for item
//   => main.sc
//      - quarter of the way down add the item to inventory list ex. {Cash}
//      - bottom of the script add instance for item ex. (instance {Cash} of Iitem(properties))
//   => This file
//      .1 - Insert if statement to check if item should init
//      .2 - Create the Instance that holds views display information
//      .3 - Unload the item cursors in doit method to preserve memory space
//      .4 - Add click on information for each item depending on cursor
//      .5 - Decide if item is droppable and declare it in the droppable switch
//      .6 - add call in click on trash to switch cel to 0 when item is dropped
//   => Create cursor image, numbered the same as item number
//   => View 500 (4 images) in the inventories view sets, => icon width = 25, height = 20
//      - Add item menu un-highlighted view to loop 0 where cel the same as item number
//      - Add item menu highlighted view to loop 1 where cel the same as item number
//      - Add item icon view to loop 8 where cel the same as item number
//      - Add item look view to loop 9 where cel the same as item number
//******************************************************************************

Now we move on to the normal script stuff, the same here as any other rooms, the includes and uses. Likewise, you will find a list of local variables. These allow you to activate or deactivate certain menu options that your game users are allowed to use, everything except look, use, and OK are pretty much up to you. If you would like to turn off any of the current options, simply change the variable here in this script from TRUE to FALSE and the code will take care of the rest for you. Also there are some placement variables which allow you to customize where and how many of your items are placed in a single row, likewise where the menu buttons are placed.

Code:
 (include "sci.sh") (include "game.sh")                             (script 500) 
//******************************************************************************
(use "main") (use "controls") (use "game") (use "feature") (use "obj")
//******************************************************************************
(local
allowTalk = TRUE // Can your players talk to items if they want
allowTrash = TRUE // Can your players drop items if they want
allowHelp = TRUE // Is the help button available
allowInvblock = TRUE // Do you want the selected item block

placeMenuX = 79 // the x position of first menu button
placeMenuY = 52 // the y position of menu buttons
spacerMenuX = 28 // the width of your standard buttons
sizeMenublock = 38 // the width of your menu block button

firstItemX = 52 // the x position of first row of items
firstItemY = 81 // the y position of first row of items
spacerItemX = 30 // the amount the x position changes from item to item
spacerItemY = 25 // the amount the y position changes from row to row
maxItemX = 240 // the maximum x position before starting new row of items
newItemX = 0 // the x variable that changes to place the items, sets itself
newItemY = 0 // the y variable that changes to place the items, sets itself

inv_mode = TRUE // keeps the doit method from dumping us from room yet
started_up = FALSE // used solely to change cursor upon loading - see doit method
spacerMenublock = 0 // extra space needed to place menu block button, it will set itself later
txtstring[50] // used to display and update any amount variables
dropAble = 0 // Is an Item droppable - set in droppable section (5,6))

Now we begin the public Instance of the Inventory room. This is where everything that is needed is actually init-ed. There's probably a word or two I should say here about what it is that is going on.

Take notice that right after the instance line, the properties are empty, usually you would have a "picture scriptNumber" in there or some variable representing the picture you would like to load up for the room. In the case, we are trying to make the inventory room load up directly on top of the last rooms picture that we were in. So if you look directly below the (method(init) line, you will see that we are drawing pic 500 instantly to the screen with the no clear property set. Making the inventory room look like a window instead of a room.

You should also see that there is a number of calls for lookInv, useInv, etc. This area is init-ing the menu buttons and placing them according to the spacer and placement variables that are set in the locals section just above. It is not possible (at least that I could accomplish) to place those x and y variables into the same line as the init(), so they are initially init-ed offscreen and the second line places them onscreen based off the local variables. In order to change where they are placed, change the local variables, you shouldn't need to edit any of this bit.

Code:
(instance public rm500 of Rm
(properties)

  (method (init)
  DrawPic(500 dpOPEN_INSTANTLY dpNO_CLEAR)
  (super:init())
      = spacerMenublock (/ (- sizeMenublock spacerMenuX)  2  )// actually decides space needed
      = newItemX firstItemX // prepares the first x position
      = newItemY firstItemY // prepares the first y position

        // init and place menu buttons you want based off local variables
        (lookInv:setPri(15)init())                 // start off screen
       (lookInv: x (placeMenuX) y (placeMenuY))    // move it onscreen
        += placeMenuX spacerMenuX                  // prepare next position

        (useInv:setPri(15)init())
        (useInv: x (placeMenuX) y (placeMenuY))
        += placeMenuX spacerMenuX

        (if(== allowTalk TRUE) (talkInv:setPri(15)init())
          (talkInv: x (placeMenuX) y (placeMenuY))
          += placeMenuX spacerMenuX     
        )

        (if(== allowInvblock TRUE)
          += placeMenuX spacerMenublock // needs a hair more space if used
          (if(== itemIcon 900)          // nothing yet, got used, or dumped
            (itemInv:setPri(15)cel(0)init())
            (itemInv: x (placeMenuX) y (placeMenuY))
            += placeMenuX spacerMenuX)
          (else                        // display selected item
            (itemInv:setPri(15)cel(itemIcon)init())
            (itemInv: x (placeMenuX) y (placeMenuY))
            += placeMenuX spacerMenuX)
          += placeMenuX spacerMenublock // and some more space here too
        )

        (if(== allowTrash TRUE) (dumpInv:setPri(15)init())
          (dumpInv: x (placeMenuX) y (placeMenuY))
          += placeMenuX spacerMenuX
        )

        (if(== allowHelp TRUE) (helpInv:setPri(15)init())
          (helpInv: x (placeMenuX) y (placeMenuY))
          += placeMenuX spacerMenuX
        )

        (okInv:setPri(15)init())
        (okInv: x (placeMenuX) y (placeMenuY))
        += placeMenuX spacerMenuX

Here it is, the first area that needs any kind of edit from you. If you looked over the comments from the first section of code, You should have found ".1 - Insert if statement to check if item should init" well this is that part. This code here is what actually places the item's thumbnail views. While at first glance, it might seem a little confusing, partly because of the lack of code indentation, but also because of all of the math operations that seem to be going on. Don't worry this is actually a pretty easy edit, but you will need to copy everything that appears. The math that is going on is what determines the items placement on the screen, we don't want a bunch of empty gaps after all, or I assumed we don't anyway, so it will all need to be added for each item that is added to your game . Take notice of the commented out bit here, that is your general layout for most of your items, except cash, that one is special so I took care of it for you. But most others you will simply need to copy and paste that code block, only changing the ### to the appropriate number.

Code:
//******************************************************************************
// 1.                                      Check for all inventory items to init
//******************************************************************************
/* To add new item, copy and paste this, Then change the ### to the item's number
********************************************************************************
    (if(send gEgo:has(###))(inventory###:init())
      (if(>= newItemX maxItemX) += newItemY spacerItemY = newItemX firstItemX)
      (inventory###: x (newItemX) y (newItemY))   += newItemX spacerItemX)
*******************************************************************************/

  (if(> totalcash 0)(inventory1:setPri(15)init())// only have cash if its more than 0
    (if(>= newItemX maxItemX)                    // checks the x position, if at the end of row
    += newItemY spacerItemY                      // drop down to the next one by changing y
    = newItemX firstItemX                        // and start over at first x position
  )                                              // do this check in each & every items init
  (inventory1: x (newItemX) y (newItemY))        // now, place the thumbnail
  += newItemX spacerItemX                        // ready next x position
  )                                              // ends cash item

  (if(send gEgo:has(2))(inventory2:setPri(15)init()) // balloon
    (if(>= newItemX maxItemX) += newItemY spacerItemY = newItemX firstItemX)
    (inventory2: x (newItemX) y (newItemY))   += newItemX spacerItemX)

  (if(send gEgo:has(3))(inventory3:setPri(15)init()) // string
    (if(>= newItemX maxItemX) += newItemY spacerItemY = newItemX firstItemX)
    (inventory3: x (newItemX) y (newItemY))   += newItemX spacerItemX)

  (if(send gEgo:has(4))(inventory4:setPri(15)init()) // balloon on a string
    (if(>= newItemX maxItemX) += newItemY spacerItemY = newItemX firstItemX)
    (inventory4: x (newItemX) y (newItemY))   += newItemX spacerItemX)


//******************************************************************************
  (self:setScript(RoomScript))
  )// ends method (init)
)// ends instance public

That brings us not only to the close of the init area, but it also brings us to the actual inventory instances, the second edit that we need to make in order to add new items to the game. Just like in a regular room, if we want to place a view on the screen, then it is going to require an instance. This is the place. Once again we have a very simple edit to make here in this section. As you may notice, all items are initially init-ed offscreen, that way they can be put in a proper place by the code, because of this, you don't have to worry about changing the x or y position when you insert a new instance, just leave them all at 400 400. Again, there is some commented code to let you know how to add a new item in.

Code:
//******************************************************************************
// 2.                              Create Instances for all inventory items here
//******************************************************************************
/* To add new item, copy and paste this, Then change the ### to the item's number
********************************************************************************
(instance inventory### of Prop(properties view 500 loop 9 cel ### x 400 y 400))
*******************************************************************************/
(instance inventory1 of Prop(properties view 500 loop 9 cel 1 x 400 y 400)) // cash
(instance inventory2 of Prop(properties view 500 loop 9 cel 2 x 400 y 400)) // balloon
(instance inventory3 of Prop(properties view 500 loop 9 cel 3 x 400 y 400)) // string
(instance inventory4 of Prop(properties view 500 loop 9 cel 4 x 400 y 400)) // balloon on a string


//******************************************************************************

That brings us to the doit method, this part here does two things, first if we just get here it takes care of changing the cursor (probably from walk) to the hand unless the user came in carrying an item cursor. If they did, we'll go ahead and let them keep that one, but otherwise it changes.

The second aspect that this part of the doit method takes care of is highlighting the appropriate menu buttons. When the user changes cursors, the doit method highlights and un-highlights the appropriate buttons. For instance if you had the hand selected, that button would be highlighted and if you then choose an item, the item selected box would become highlighted by going to loop 1 and cel whatever number the item is, meanwhile the hand would switch to it's un-highlighted cel. If an item was selected, the item box would be highlighted, and the user chose to look at something, the item box would un-highlight but still show the item we are currently carrying, by changing to loop 0 but maintaining the appropriate cel, meanwhile the look button would highlight. Does that make sense?

Anyway, you shouldn't have to edit any of this unless of course you add a new inventory menu option.

Code:
(instance RoomScript of Script
(properties)
  (method (doit)
  (super:doit())
    (if(== started_up FALSE)            // When we first get here, we want to switch cursors
        (if(== gCurrentCursor itemIcon) // Unless its an item
        = started_up TRUE               // let em keep that cursor
                )
                (else  
        = gCurrentCursor 995       // Change the global variable first
        SetCursor(995 HaveMouse()) // Now change cursor to the hand, Sierra does the arrow
        = started_up TRUE          // Now we change started_up so this doesn't keep happening
                )
    )
    (if(== started_up TRUE)   // Depending on which cursor, highlight button
     (switch(gCurrentCursor)  // look, get, talk, or selected item
        (case itemIcon        // check the item selected
                 (if(== itemIcon 900) // if no item selected
                         )                        // do nothing
                 (else  
                        (talkInv:cel(0)) (lookInv:cel(0)) (useInv:cel(0)) (itemInv:loop(1) cel(itemIcon))
                 )
                )  
        (case 995  (talkInv:cel(0)) (lookInv:cel(0)) (useInv:cel(1)) (itemInv:loop(0)) ) // hand
        (case 998  (talkInv:cel(0)) (lookInv:cel(1)) (useInv:cel(0)) (itemInv:loop(0)) ) // look
        (case 996  (talkInv:cel(1)) (lookInv:cel(0)) (useInv:cel(0)) (itemInv:loop(0)) ) // talk
       ) // ends switch
    )

Now, I use this part of the doit method to actually transport us back out of the room based off of the inv_mode variable that we defined in the local section. The main reason that I do this is to attempt to get all of my memory resources back that I used by coming here. It would be a terrible thing to visit the inventory and immediately run out of heap or hunk when returning to the game.

So let's see, what all is happening here? The first part, which checks the current cursor, determines whether an item was actually selected, if there is then we need to update the cursor variables so that the room we return to gets it right. If we are leaving empty handed then we switch back to the cursor that was being used before coming here.

With that taken care of, let's unload some resource. This is one of the reasons that I tried so hard to keep all of the inventory rooms resources as compact as possible. As long as you numbered every resource the way I've told you to, the only thing that will need to be edited here is section 3 as described by the outline above. UnLoad any inventory item cursors that have been added. After that, we return to the room we came from with gPreviousRoomNumber. Simple enough right?

Code:
(if(== inv_mode FALSE)                  // Decides When to switch back to last room
      (if(not(== gCurrentCursor itemIcon))  // If no item selected
      SetCursor(gPreviousCursor HaveMouse())// revert back to previous cursor
      = gCurrentCursor gPreviousCursor      // make it happen
      )
    Unload(rsPIC scriptNumber)              // In order to get all the memory back that we
    Unload(rsSCRIPT scriptNumber)           // used by coming here, lets go ahead and Unload
    Unload(rsTEXT scriptNumber)             // all of the resources we utilized
    Unload(rsVIEW scriptNumber)            // All Inventory views
    UnLoad(rsTEXT scriptNumber)            // Inventory Descriptions
    Unload(rsTEXT 501)            // Inventory Instructions, help and other misc.
    Unload(rsCURSOR 998)          // look cursor
    Unload(rsCURSOR 997)          // loading cursor
    Unload(rsCURSOR 996)          // talk cursor
    Unload(rsCURSOR 995)          // get cursor
//******************************************************************************
// 3.                                 Unload all possible Inventory Cursors here
//******************************************************************************
    Unload(rsCURSOR 001)          // the number corresponds to item #
    Unload(rsCURSOR 002)
    Unload(rsCURSOR 003)
    Unload(rsCURSOR 004)



//******************************************************************************
    (send gRoom:newRoom(gPreviousRoomNumber)) // go back to room we came from
    ) // ends if inv_mode FALSE
  ) // end method (doit)

Which brings us to the handleEvent method. Um, yeah this is a pretty hefty section of the script. I don't want to bog you down too much with one big endless stretch of code, so I'm going to take it one section one item at a time. Just some quick background info, this whole section covers the mouse clicks, and based off which cursor is being used to make those clicks, different responses occur. Basically we are dealing with a whole little army of switches all based off of the cursor.

First let's go ahead and take care of any clicks on the right mouse button. Whenever one occurs, the cursor changes depending on what inventory menu options you have allowed your users to make use of. Eye changes to the Hand, changes to Talk, changes to the last selected Item, changes to eye and repeat.

Code:
(method (handleEvent pEvent) (super:handleEvent(pEvent))
    (if(==(send pEvent:type())evMOUSEBUTTON) (if(& (send pEvent:modifiers) emRIGHT_BUTTON) // handles the mouses right clicks
       (send pEvent:claimed(TRUE))                // used solely to cycle through cursor
       (switch(gCurrentCursor)                    // options mainly look, get, last item
        (case itemIcon  SetCursor(998 HaveMouse()) = gCurrentCursor 998)    // change item to eye
        (case 998 SetCursor(995 HaveMouse()) = gCurrentCursor 995)          // change eye to hand
        (case 995 
          (if(== allowTalk TRUE)
          SetCursor(996 HaveMouse()) = gCurrentCursor 996 // change hand to mouth
          ) (else
              (if(== itemIcon 900) // no item selected
              SetCursor(998 HaveMouse()) = gCurrentCursor 998 // change hand to eye
              ) (else
              SetCursor(itemIcon HaveMouse()) = gCurrentCursor itemIcon // change hand to item
              )))
        (case 996
            (if(== itemIcon 900) // no item selected
              SetCursor(998 HaveMouse()) = gCurrentCursor 998 // change mouth to eye
              ) (else
              SetCursor(itemIcon HaveMouse()) = gCurrentCursor itemIcon // change mouth to item
              ))) // ends switch
     ) // ends if right button

Now we begin the process of handling the left button mouse clicks, basically we need to check over and over again not only where it is that they clicked, but also which cursor they were using when they clicked there. This is also section 4 of the outlined steps at the beginning of the script.

Let's begin by checking if they clicked inside the boundaries of the view of the first item, cash. If it is, then we set up a switch to check which cel is actually being displayed. Remember cel 1 is the view of the cash's thumbnail. On a side note, if for some reason an item is dropped, used, or otherwise misplaced on this visit to the inventory room, the cel switches to 0 which is a blank cel. If that's the case then do nothing because technically it is not there anymore, we'll cover that a little more when we take care of the trash can, but I wanted to make you aware of why the inventory1 switch included a case 0 .

If in fact cel 1 is displayed and that is where they clicked, then we determine the cursor with another switch and take the appropriate action. Notice in the gCurrentCursor switch case 998, or cursor 998, AKA look, we are actually displaying the totalcash variable that was set in the main script in chapter 1 via line 1 of text resource 500, remember the %d that we placed there, this is how that works. If you wanted to display the number of rocks or whatever that your character is carrying, you would do something similar.

I have also included a case 1 under the gCurrentCursor switch which basically means that they clicked on the pile of cash with the pile of cash cursor. I could have added any other item number, or cursor number, they are supposed to be the same, to include any other responses of using some item on the cash. We'll do more with that in the next couple of items and it works exactly the same way, add the cursor number to the gCurrentCursor switch and do the response.

Code:
      (if(not(& (send pEvent:modifiers) emRIGHT_BUTTON)) // handles the mouses left clicks
//******************************************************************************
// 4.                           Create Interactions for all inventory items here
//******************************************************************************
   (send pEvent:type(evMOUSEBUTTON) claimed(TRUE))
   //  *****************************************  item  1  - Cash
   (if((> (send pEvent:x) (inventory1:nsLeft))and
      (< (send pEvent:x) (inventory1:nsRight))and
      (< (send pEvent:y) (inventory1:nsTop))and
      (< (send pEvent:y) (inventory1:nsBottom)))
      (switch(inventory1:cel) (case  0  ) // cel  0  means its been dropped, do nothing
        (case  1  // its there and waiting
          (switch(gCurrentCursor) (case 998 // look
                Format(@txtstring 500  1  totalcash)
                Print(@txtstring totalcash #title "Money" #icon 500  8   1  #time 15 ))
            (case 995 // get
                SetCursor(001 HaveMouse())
                = gCurrentCursor 001
                = itemIcon  1  ) (case 996  // talk
                Print(501 15))
            (case  1  // cash
                Print(501 7)
                Print(501 8))
            (default  Print(501 10)) // other items
          ) // end current cursor switch
        ) // end itself case
     ) // end inventory cel switch
   ) // ends click on cash view

Next, we move on to the second inventory item, the balloon. This begins the exact same way as the cash did, and all of the other inventory items are going to. First we check that the click was inside the boundaries of the thumbnail view. Followed by a check of which cel is actually being displayed. Just like before, case 0 represents the item being dropped or more likely used. This time since this is item 2 , or cel 2 in the item views, we are going to use case 2 for the balloon being there just like it is supposed to be.

If the cel is in fact cel 2 , we now check to see which cursor was used to do the clicking. Cursor 998 , the eye, means they looked at the balloon so we display the looked at view in the print statement. Cursor 995, the hand, means they want to carry the balloon around so we change the cursor to the balloon and we tell the doit method to put the balloon in the menu item box. Cursor 996, the mouth, means they want to talk to the balloon. OK probably not, but if you allowed it in the allowed local variables, then we need to tell them something just in case they do. Cursor 3, the string, means that they have already selected the string and now want to try to combine the two. I don't know where they would have gotten such a crazy idea they must have a hint book or something. Anyway, since that is the idea lets take care of everything.

This simple balloon has been changed into a balloon on a string. That means that they no longer have the string, so put that, they no longer have the balloon so we need to put that, but they have gained item 4 so we need to get that. We change the cursor to 4, the menu item box to 4, and we change the balloon cel to cel 4. Since we had the string when we came in, the thumbnail view was init-ed, so we need to get rid of the that, we simply change that to cel 0 .

Now though, we have a special case to add into the inventory2 cel switch. Since we changed the balloon, cel 2 , into a balloon on a string, cel 4, we need to check if they click on it after that, basically if it is cel 4 we add in all the click on responses that we would be using for the actual item 4, because that is what it is now.

Code:
   // ****************************************  item  2  - balloon
   (if((< (send pEvent:x) (inventory2:nsLeft))and
      (< (send pEvent:x) (inventory2:nsRight))and
      (< (send pEvent:y) (inventory2:nsTop))and
      (< (send pEvent:y) (inventory2:nsBottom)))
      (switch(inventory2:cel) (case  0  ) // cel  0  means its been dropped
          (case  2  // its there and waiting
          (switch(gCurrentCursor) (case 998 // look
                Print(500  2  #title "Balloon" #icon 500  8   2  #time 15 ))
            (case 995 // get
                SetCursor(002 HaveMouse())
                = itemIcon 002
                = gCurrentCursor 002) (case 996 // talk
                Print(501 15))
            (case 003 // string
                Print(501 14)
                SetCursor(004 HaveMouse())
                = itemIcon 004
                = gCurrentCursor 004
                (inventory3:cel(0)) // remove string from thumbs
                (inventory2:cel(4)) // change balloon thumb to balloon on a string
                (send gEgo:put(2)) // drop balloon
                (send gEgo:put(3)) // drop string
                (send gEgo:get(4)) // get balloon on a string
                ) (case 002  Print(500 0)) // itself              
            (default  Print(501 10)) // other items
          ) // end current cursor switch
        ) // end itself case
        (case  4  // its already been turned into a balloon with a string this visit 
          (switch(gCurrentCursor) (case 998 // look
                Print(500  4  #title "Balloon on a String" #icon 500  8   4  #time 15 ))
            (case 995 // get
                SetCursor(004 HaveMouse())
                = itemIcon 004
                = gCurrentCursor 004) (case 996  // talk
                Print(501 15))
            (case 004 Print(500 0)) // itself              
            (default  Print(501 10)) // other items
          ) // end current cursor switch
        ) // end its a balloon on a string case
     ) // end inventory cel switch
   ) // ends click on view

Item 3, or the string is very similar to item 2 , just like with it, there is a possibility that it will get turned into a string on a balloon. If the balloon cursor is clicked on it that is. So just like with the balloon, we'll need to put the balloon and the string and get the balloon on a string. We change the string to cel 4 and the balloon to cel 0. We'll also need to change the cursor and tell the doit method to change the menu item box. That occurs anytime that gCurrentCursor is equal to the itemIcon variable.

Otherwise, everything is almost exactly the same, we first check to see if they clicked inside the boundaries of the view. If they did, we then check to see what cel the view is set at to determine what it is they are actually clicking on, then in each of the cels cases we set up a gCurrentCursor switch to see what it is they are actually clicking on the item with.

Code:
   // ****************************************  item  3  - string
   (if((< (send pEvent:x) (inventory3:nsLeft))and
      (< (send pEvent:x) (inventory3:nsRight))and
      (< (send pEvent:y) (inventory3:nsTop))and
      (< (send pEvent:y) (inventory3:nsBottom)))
      (switch(inventory3:cel) (case  0  ) // cel  0  means its been dropped
          (case  3   // its there and waiting
          (switch(gCurrentCursor) (case 998 // look
                Print(500  3  #title "String" #icon 500  8   3  #time 15 ))
            (case 995 // get
                SetCursor(003 HaveMouse())
                = itemIcon 003
                = gCurrentCursor 003) (case 996 // talk
                Print(501 15))
            (case 002 // balloon
                Print(501 14)
                SetCursor(004 HaveMouse())
                = itemIcon 004
                = gCurrentCursor 004
                (inventory3:cel(4)) // change string thumb to balloon on a string
                (inventory2:cel(0)) // remove balloon from thumbs
                (send gEgo:put(2)) // drop balloon
                (send gEgo:put(3)) // drop string
                (send gEgo:get(4)) // get balloon on a string
                ) (case 003 Print(500 0)) // itself
            (default Print(501 10)) // other items
          ) // end current cursor switch
        ) // end itself case
        (case  4  // its already been turned into a balloon with a string this visit
          (switch(gCurrentCursor) (case 998 // look
                Print(500  4  #title "Balloon on a String" #icon 500  8   4  #time 15 ))
            (case 995 // get
                SetCursor(004 HaveMouse())
                = itemIcon 004
                = gCurrentCursor 004) (case 996 // talk
                Print(501 15))
            (case 004 Print(500 0)) // itself               
            (default Print(501 10)) // other items
          ) // end current cursor switch
        ) // end its a balloon on a string case
     ) // end inventory cel switch
   ) // ends click on view

So we finally made it, this is the last item that we are going to cover for this tutorial, and it just so happens that this is the most straight forward item in the inventory. Most of your items will probably be patterned off of this one. We aren't printing any variables, it doesn't get used on another item and nothing gets used on it. It just is.

We begin by again checking if the click occurred inside the boundary of the view. If so then we check to see which cel is being displayed for the item. If 0 it has probably been dropped so do nothing. If it is cel 4, the same as the item number, then we are ready to check to see which cursor was doing the clicking and simply take the appropriate action. Look, print the look statement with icon, use, switch to the item's cursor and change the inventory block by setting itemIcon to the item number, talk to it, well then print out the talking dialog, if clicked with any other cursor, presumably items. Then print whatever you want out of text 501.

Code:
   // ****************************************  item  4  - string on a balloon
   (if((< (send pEvent:x) (inventory4:nsLeft))and
      (< (send pEvent:x) (inventory4:nsRight))and
      (< (send pEvent:y) (inventory4:nsTop))and
      (< (send pEvent:y) (inventory4:nsBottom)))
      (switch(inventory4:cel) (case  0  ) // cel  0  means its been dropped
          (case  4   // its there and waiting
          (switch(gCurrentCursor) (case 998 // look
                Print(500  4  #title "String on a Balloon" #icon 500  8   4  #time 15 ))
            (case 995 // get
                SetCursor(004 HaveMouse())
                = itemIcon 004
                = gCurrentCursor 004) (case 996 // talk
                Print(501 15))
            (case 004 Print(500 0)) // itself          
            (default Print(501 10)) // other items
          ) // end current cursor switch
       ) // end itself case
     ) // end inventory cel switch
   ) // ends click on view
   
   
   
//******************************************************************

With the inventory items out of the way, it is time to check if they are clicking on one of the menu buttons. Just like with items, we begin by checking if the click fell inside the boundaries of the menu button views. These first few buttons are all basically the same, if they clicked on the look, hand, or mouth then simply change the gCurrentCursor to the appropriate value.

Code:
// Clicks on the look button
   (if((< (send pEvent:x) (lookInv:nsLeft))and
      (< (send pEvent:x) (lookInv:nsRight))and
      (< (send pEvent:y) (lookInv:nsTop))and
      (< (send pEvent:y) (lookInv:nsBottom)))
     SetCursor(998 HaveMouse())
     = gCurrentCursor 998
   ) // ends if look
   
// clicks on use button
   (if((< (send pEvent:x) (useInv:nsLeft))and
      (< (send pEvent:x) (useInv:nsRight))and
      (< (send pEvent:y) (useInv:nsTop))and
      (< (send pEvent:y) (useInv:nsBottom)))
      SetCursor(995 HaveMouse())
      = gCurrentCursor 995
   ) // ends if use
   
// clicks on talk button
   (if((< (send pEvent:x) (talkInv:nsLeft))and
      (< (send pEvent:x) (talkInv:nsRight))and
      (< (send pEvent:y) (talkInv:nsTop))and
      (< (send pEvent:y) (talkInv:nsBottom)))
      SetCursor(996 HaveMouse())
      = gCurrentCursor 996
   ) // ends if talk

The next menu button is a little special but not much, the selected item menu block. In some ways, I've treated this kind of like the item itself just because I didn't know what else to do with it. First we check if the click fell inside the views boundaries. If it did and no item has been selected yet, tell them they need to select an item first, else if there is an item we start the usual check to see which cursor did the clicking. Depending on which one it is as to the appropriate action.

If they want to look at it, then display the look print statement, if they want to hold it, then change the cursor to it. If they want to talk to it, then print whatever dialog you've got for it and that should just about do it. We don't have to worry about them using an item on it because once they select a different item the button will change to whatever item they are sporting.

Code:
// clicks on item button in menu
   (if((< (send pEvent:x) (itemInv:nsLeft))and
      (< (send pEvent:x) (itemInv:nsRight))and
      (< (send pEvent:y) (itemInv:nsTop))and
      (< (send pEvent:y) (itemInv:nsBottom)))
      (if(== itemIcon 900)Print(501 0)) // no item selected
         (else // item is in menu
       (switch(gCurrentCursor) (case 998 // look
                (if(== itemIcon  1  ) // cash is special because of the variable
                Format(@txtstring 500  1  totalcash)
                Print(@txtstring totalcash #title "Money" #icon 500  8   1  #time 15 ))
                (else Print(500 itemIcon #icon 500  8  itemIcon #time 15 )))
        (case 995 // get
                SetCursor(itemIcon HaveMouse())
                = gCurrentCursor itemIcon
        ) (case 996 Print(501 15)) // talk
                
       ) // end switch
      ) // end else item is in menu   
   ) // ends if click on item in menu

Now we move on to the trash can. Just like everything else first we determine if the click was inside the views boundaries. If it is then we start checking to see which cursor was selected. Now this area here requires a little edit on your part, we have a switch set up which defines whether or not an item can actually be dropped, after all we don't want them to drop an item that they are going to need later. So you will need to add in a case for each possible item. If it is droppable then set the dropAble variable to 1 for yes, if it isn't then set the variable to 0.

Code:
// clicks on trash button
  (if( (> (send pEvent:x) (dumpInv:nsLeft))and
       (< (send pEvent:x) (dumpInv:nsRight))and
       (> (send pEvent:y) (dumpInv:nsTop))and
       (< (send pEvent:y) (dumpInv:nsBottom)))
     (switch(gCurrentCursor) (case itemIcon
         (switch(itemIcon)
//******************************************************************************
// 5.                      Decide here if items can be dropped  0  =no  1  =yes
//******************************************************************************
  (case  1  = dropAble  0  ) // not droppable -- cash
  (case  2  = dropAble  1  ) // droppable -- balloon
  (case  3  = dropAble  1  ) // droppable -- string
  (case  4  = dropAble  1  ) // droppable -- balloon on a string



        ) // end switch

Now that we know whether an item can be dropped or not, it is time to either tell them that they can't drop it, or give them a yes or no option to make sure that they are sure they want to drop the item. If they actually click yes then we need to put the item to get it out of their inventory and also change the item's cel number to 0 to remove the thumbnail from the inventory screen. This will require one more edit from you. The good news is this is the last edit you will have to make in order for any additional items to be fully functional.

Take note of case 4, or rather dropping item 4. This may be the same visit that they made the balloon on the string so we can't be sure which inventory# instance the item is currently hanging out in. If you remember from a few sections up, we changed the balloon cel to the string on a balloon cel but we never actually did anything to the instance, so to be sure that we remove any possible cel which might have been changed into cel 4, we change all the items that it takes to make the item. So in this case we change the string to cel 0 , the balloon to cel 0 , and the actual balloon on a string to 0 , after all technically if they are dropping the balloon on a string then they are also dropping the balloon and the string too.

Directly following this whole dropAble bit you may notice that we do a check of the current cursor and the one that they brought with them into the room. This is to insure that if they dropped the item they were carrying that when they leave the room they aren't still using the item's cursor. If it was then we make a quick change of the gPreviousCursor and reset it to the walking one, just in case they don't select a different item before they leave.

If they click with any cursor besides an item cursor then let's go ahead and give some instructions on how to drop an item.

Code:
       (switch(dropAble) (case  0  Print(501 9)) // not droppable
         (case  1  (if(Print(501  1  #title "Dump" #font gDefaultFont #button "Positive"  1  #button "No Way" 0))
               (switch(itemIcon)
//******************************************************************************
// 6.                                      Change to cel  0  if item is dropped
//******************************************************************************
  (case  1  (inventory1: cel(0))(send gEgo:put(1)))
  (case  2  (inventory2: cel(0))(send gEgo:put(2)))
  (case  3  (inventory3: cel(0))(send gEgo:put(3)))
  (case  4  (inventory2: cel(0)) // this might be same visit they made item 4
            (inventory3: cel(0)) // out of item  2  and  3  so lets just drop all
            (inventory4: cel(0))(send gEgo:put(4))) // of those items needed to make item  4  too



//*****************************************************************************
// Nothing else needs edited -- You are all done!!!!
//*****************************************************************************
               ) // end switch
              // is item they dropped the cursor they brought in
              (if(== gPreviousCursor gCurrentCursor)
                 = gPreviousCursor 999   // if so change old cursor to walking
               )                         // in case they leave empty handed
               (send gEgo:put(itemIcon)) // dropped it
               SetCursor(995 HaveMouse( ))
               = gCurrentCursor 995
               = itemIcon 900 // no item selected now
               ( itemInv:cel(0)) // change menu block to blank
             ) // ends click yes, wow they really dropped something?
             (else) // no, do nothing
         )) // end switch
    ) // ends clicks with items
    (case 998 Print(501 11 #title "Trash")) // look
    (case 995 Print(501 12 #title "Trash")) // hand
    (case 996 Print(501 15 #title "Trash")) // talk
  ) // ends switch
) // ends click on trash

Wow, we are so close to having this room completed. All we have left to do now is check whether or not they clicked on the last two menu buttons. Like the others we check if the click was inside the views boundaries and if it is then we do something. In the case of the help button, no matter which cursor was used, print out the help messages. If they clicked on the OK with anything but the look cursor then we set the variable that the doit method is waiting for to kick us out. If they are just looking at the OK, then print out a statement to let them know how to exit the inventory room.

Code:
// clicks on help button
   (if((> (send pEvent:x) (helpInv:nsLeft))and
      (< (send pEvent:x) (helpInv:nsRight))and
      (> (send pEvent:y) (helpInv:nsTop))and
      (< (send pEvent:y) (helpInv:nsBottom)))
      Print(501 2 #title "Inventory Help") //describes look
      Print(501 3 #title "Inventory Help") //describes selecting item
      Print(501 4 #title "Inventory Help") //describes mixing items
      (if(== allowTrash TRUE)Print(501 5 #title "Inventory Help")) // describes trash
      Print(501 6 #title "Inventory Help") // click on OK to return to game
   ) // ends click on help

// clicks on OK button
     (if((> (send pEvent:x) (okInv:nsLeft))and
        (< (send pEvent:x) (okInv:nsRight))and
        (> (send pEvent:y) (okInv:nsTop))and
        (< (send pEvent:y) (okInv:nsBottom)))
        (if(== gCurrentCursor 998) // look cursor
          Print(501 13 #title "OK"))
        (else  = inv_mode FALSE)) // any other cursor let doit boot us
      ) // ends click on OK
  
    ) // ends mouse button
  ) // ends method
) // end instance

OK, here it is, the last little bit of code that makes up the point and click inventory room. Quite simply the instances that take care of the menu buttons. They could have gone way up top with the inventory items instances, but they work just fine down here out of the way, so that's where I put them. Notice the x and y positions of the buttons. They are initially placed off screen and the init code takes care of repositioning them on screen based off of the local variables.

Code:
//*****************************************************************************
(instance talkInv of Prop(properties x 400 y 400 view 500 loop 4 cel 0))
(instance itemInv of Prop(properties x 400 y 400 view 500 loop 0 cel 0))
(instance lookInv of Prop(properties x 400 y 400 view 500 loop 2 cel 0))
(instance useInv of Prop(properties x 400 y 400 view 500 loop 3 cel 0))
(instance dumpInv of Prop(properties x 400 y 400 view 500 loop 5 cel 0))
(instance helpInv of Prop(properties x 400 y 400 view 500 loop 6 cel 0))
(instance okInv of Prop(properties x 400 y 400 view 500 loop 7 cel 0))

Congratulations, you have made it through the inventory portion of the point and click tutorials. Hopefully some of this makes some sense, because making the rest of the game point and click is going to be very similar to how we handled the clicks on views and gCurrentCursor switches so if you are just totally lost, look over it a couple of times, and feel free to ask any questions you need in the community forums.

There it is, the Point and Click Inventory Room Completed! Are you ready to tackle the rest of the game?

 

< Previous: Chapter 4 - Inventory Views Next: Chapter 6 - Inventory Room Loose Ends >