ftp.nice.ch/pub/next/games/card/NEXTVegas3.0.src.tar.gz#/NEXTVegas/Documentation/WritingModules.rtf

This is WritingModules.rtf in view mode; [Download] [Up]

Writing Modules for NEXTVegas

	NEXTVegas provides many things that make writing your game relatively simple.  The dynamic loading of games, opening and closing of players, and the maintanance of player files are all handled by NEXTVegas.  In addition, NEXTVegas provides you with NVDealer, an abstract superclass that your contolling object must inherit from, which frees you from having to worry about many of the mundane tasks such as game loading, rules, inspectors, etc.  Finally, NEXTVegas provides some support classes, such as BetView and the CardSet, which can be incorporated into your game and which provide mechanisms for using cards and interactive betting.
	
	If you want to write a module for NEXTVegas, please write to me.  I'll keep track of whose writing modules, and let you know if someone is already working on the game you want to write.  I'll also be able to keep you up to date of any changes that are made to NEXTVegas - at least those that will affect your module.

Documentation
	
	Before you being to write a NEXTVegas game, you should continue reading this file, and then take a glance at the following files:
	
	WritingModules.rtf
	This File.  Provides tips and things for the first time NEXTVegas game writer.
	
	BetView.rtf
	Class description of BetView.  Full documentation.
	
	ChipPile.rtf
	Class description of ChipPile.  Full documentation.
	
	TypesAndConstants.rtf
	Describes the types and constants used in NEXTVegas.
	
	MethodsOfInterest.rtf
	Methods from PitBoss, Player, and NVDealer that your module may need to use.  Full documentation on these classes is omitted out of lazyness on my part.  However, you should only need the methods described in this file.
	
	CardSet documentation:
	The documentation for the CardSet was written by the authors of the CardSet, and modified by me later. Definately read through all of it before attempting to use the CardSet.
	
	Card.rtf
The basic object in the CardSet.  Full documentation.
	
	CardPile.rtf
	Sort of like a List object, but handles Cards.  Full documentation.
	
	CardPileView.rtf
	A special view that displays CardPiles.  Full documentation.


Template.nvgame

	Included with the source code to NEXTVegas is a template module called Template.  This module is a bare bones .nvgame, but it is compilable and loadable under NEXTVegas.  It also includes a blank inspector and empty rules. 

	I suggest copying Template, and then modifying it for your needs.  The first and most obvious thing you will need to do is to change all the references to Template to the name of your game.  For example, if your game is called Baccarat, then change Template to Baccarat, change Template.m and Template.h to Baccarat.m and Baccarat.h,  parse Baccarat.h into the nib file "table.nib", change the name of the bundle from Template to Baccarat in ProjectBuilder, etc.   After you have done that, compile the new game and see if it loads properly.  Once you get it compiled and loading properly, you can then begin to work on the specifics of your game.  Really, this is a very easy thing to do.
	
	You should definately check out the source code for the modules I've written.  Probably the most concise and easiest to follow is the VideoPoker game.  Also, VideoPoker uses DPSTimedEntry's for animation, which you might want to take a look at.  Both Blackjack and VideoPoker will give you a good idea of how to use the CardSet.  Blackjack also uses the BetView class which you should definately become familiar with.

	One thing you should keep in mind:  when your code is loaded into NEXTVegas, it mustn't conflict with code already in memory.  Class names and constants must be unique to your module.  As a rule, always preface your class names or constants with the name of your module.  My modules tend not to do it, but yours should!  
	


Every module must have the following:

1.  A controller object, with the same name as your module, that inherits from NVDealer.  
For example, if your game is named Baccarat, you must have an NVDealer subclass named Baccarat.  This is the object that the PitBoss will assume is the controlling object of your module, and will be the receiver of all of PitBoss's messages to your module.

2.  A main nib file, called "table.nib".  
In this file should be a view called "gameView" that is the view that will be added to the NEXTVegas main window.  

The gameView box should be no larger than 900 points wide and 570 points tall.

In addition to the above, modules may also have the following:

1.  Rules
If you've copied Template as a starting point, then rules are already a part of your game.  All you need to do is edit the text object in rules.nib.  

If you didn't copy Template, then to add rules to your module create a nib file called "rules.nib" and add it to your project.  Parse your your controller object into the nib file and set the "file's owner" to be your controller.  Add a window to the nib file, and a text object, and connect your controllers instance variable rulesWindow to the new window.  Finally, in your controller object, override NVDealer's hasRules method to return YES.  A good example of rules can be found in the Craps module.

2.  Inspector
If you've copied Template, then an inpector is already a part of your game.  All you need to do is add the objects to the inspector and methods that your game will need.  

If you didn't copy Template, then to add an inspector to your module add a window to your nib file "table.nib".  Add a box to the window, and place the objects you need for the inspector inside of it.  Connect your controller's instance variable inspector to the box.  Thats all you need to do for NEXTVegas' part, but you will also need to add your own instance variables or methods to deal with the inspector.  A good example of an inspector can be found in the Blackjack module.

The inspector should be no larger then 370 points wide and 350 points tall!

You will get the following messages sent to your Dealer when your Inspector is loaded on the Preferences Panel and before it is removed, respectively:

	- inspectorWasInstalled:sender;
	- inspectorWillBeRemoved:sender;

If you want the object that controlls the Preferences Panel (PrefController) to interact with your Dealer object, then you should have the following methods (which are declared in NVDealer, so your Dealer already inherits them):

	- preferencesChanged:sender;
		Any objects in your inspector should be set in IB to send this message to your Dealer.  You can use it to know that the player has changed a value of one of your preferences.  You can overwrite this method, but make sure that it still sends the same message to super (NVDealer).  It will inform the PrefController object that things have changed and that it needs to enable the set and revert buttons on the panel.  If it does not do it, you will never get the setPreferences: and revertPreferences: calls.

	- setPreferences:sender;
		This is sent to your Dealer when the player has hit the Set button on the Preferences Panel and your inspector is loaded.  

	- revertPreferences:sender;
		This is sent to your Dealer when the player has hit the Revert button on the Preferences Panel and your inspector is loaded.

3.  Sound Effects
There are many ways to incorporate sound into your module.  NEXTVegas provides a couple of ways that make adding sound very easy.   The easiest way is to just use the default sounds that are provided with NEXTVegas.  To use them, simply send the PitBoss object the playSound: message, with one of the following constants as an argument:

Constant					Description
NV_WINSOUND			Combination of a bowling strike and applause.
NV_LOSESOUND			Combination of "taps" and grumbling from a crowd.
NV_WARNSOUND			A gun being cocked.
NV_CHIPSOUND			Supposed to sound like a chip.
NV_REMOVECHIPSOUND	Supposed to sound like a the opposite of NV_CHIPSOUND, but exaggerated.
NV_SHUFFLESOUND		Sound of a deck of cards being shuffled.
NV_CARDSOUND			Sound of a single card being dealt.

For example, when the player wins a hand or something, you could play an appropriate sound by sending PitBoss the following message:

	[PBoss() playSound:NV_WINSOUND];

Another method of adding sound to your module is to use PitBoss's addSoundFromSection: method.  This method creates a SoundEffect object for you, and returns an integer that you can then use when you want to play the sound.  For example, consider the code:

	char path[MAXPATHLEN+1];
	int	aSound;
	
	if ([bundle getPath:path forResource:"mySound" ofType:"snd"]) 
	{
		aSound = [PBoss() addSoundFromSection:path];
	}

This code instructs PitBoss create a SoundEffect object for the file "mySound" (which is pointed to by path).  PitBoss returns an index to that SoundEffect object, which is stored in the integer variable aSound.  You can then play the sound by sending PitBoss the message playSound:, with the argument aSound, as follows:

	[PBoss() playSound:aSound];

Sounds created this way should be removed when the module is freed or when you don't need them anymore.  Remember to only free the sounds that you create.  You free a sound by sending PitBoss the message removeSound:, with the index that was returned from the addSoundFromSection: method as an argument.  
	
	[PBoss() removeSound:loseSound];

The last way for you to incorporate sounds into your module is for your object to create the SoundEffect objects that they will use themselves.  This is how all the module's that I've written do it.  For example, the Dice object in Craps creates its own SoundEffect object for the rolling of the dice sound.  This seems to work a little faster than going through PitBoss to play a sound. 

	id myBundle = [NXBundle bundleForClass:[self class]];

	if ([myBundle getPath:path forResource:"roll" ofType:"snd"])
		rollSound = [[SoundEffect allocFromZone:[self zone]] initFromSection:path];	
Playing the sound is then done with SoundEffect's own play or play:pan: method:

	[rollSound play:1.0 pan:0.0];

4.  Images
One thing that I had to find out the hard way is that if you add images to your nib files, you will need to create "local" images.  Simply adding images to your project isn't enough.  If you want to display a particular image on a button, for example, make sure you "create local image" when you add the image to the nib.  If you don't, the image won't be there when the module runs.  Another way you can do it (if you add an image through ProjectBuilder rather than through InterfaceBuilder) is to use NXBundle to get the path to the image, and then set the image for the button programatically:

	id myBundle = [NXBundle bundleForClass:[self class]];
	char path[MAXPATHLEN+1];

	if ([myBundle getPath:path forResource:"one" ofType:"tiff"])
		image[1] = [[NXImage  alloc] initFromFile:path];
	
	[dieButton1 setImage:image[1]];

You probably already knew this, but it cost me a little time discovering it, so I thought I'ld pass it on.

5.  An icon
You can have an icon for your displayed in the browser on the NEXTVegas welcome panel simply by including a 48X48 tiff file with the name [moduleName]Icon.tiff.  That is, if your module is named Baccarat, you add a tiff file named BaccaratIcon.tiff to your project.  NEXTVegas will find it and use it if it is there.  If its not, it will use a default image for the icon.

6.  BetView
The BetView class provides an object that can both display chips and respond to user actions such as betting or removing bets.  There's a full class description with this documentation, you should read it.  Check out either Blackjack or Craps for examples on using BetViews.

7.  The CardSet
The CardSet was taken from the game Solitaire.app and provides a really slick way of using cards in your game.  There are three object that you should know about:  Card, CardPile, and CardPileView, each of which is fully explained in the CardSet documentation.  Check out either Blackjack or VideoPoker for examples on using the CardSet. 

The following code is a small example of using the CardSet.   Assuming that you've created a nib file with two CardPileView's in it, and connected them to your instance variables cardPileView1 and cardPileView2, the following demonstrates how to initialize them and move a card from one pile to the other:

	id cardPile1, cardPile2, topCard;
	
	cardPile1 = [cardPileView1 cardPile];	// cardPile1 maintains the Cards for the cardPileView1
	cardPile2 = [cardPileView2 cardPile];	// cardPile2 maintains the Cards for the cardPileView2
	
	[[[cardPileView1	setOffset:60.0 :0.0]	// sets the offset for each card displayed
					setDrawOutline:NO]	// doesn't draw an outline (is invisible when cardPile's empty)
					setCoversOthers:YES];	// re-draws area underneth the view when cardPile's empty.
	
	[[[cardPileView2	setOffset:60.0 :0.0]	// sets the offset for each card displayed
					setDrawOutline:NO]	// doesn't draw an outline (is invisible when cardPile's empty)
					setCoversOthers:YES];	// re-draws area underneth the view when cardPile's empty.
	
	[cardPileView1 setCardSize:CS_SMALL];	// will display small cards (as opposed to CS_LARGE)
	[cardPile1 addDeck];			// adds a deck (52 Cards) to cardPile
	[cardPile1 shuffle];			// shuffles the deck
	[cardPileView1 display];	// any changes to cardPile should be followed with a display to cardPileView
	
	topCard = [cardPile1 cardAt:CS_TOP];	// topCard is the card at the top of cardPile1
	[cardPile1 removeCard:topCard];	// removes topCard from cardPile1
	[cardPileView1 display];		// must redisplay the pile...
	
	[topCard setFaceUp:YES];		// turns the card over so that it is face up
	
	if([topCard value] == CS_ACE)	// userValue is independant of value.  You can use it so that all
		[topCard setUserValue:11];	// face cards have a user value of 10, for example.
	
	[cardPile2 addCard:topCard];	// Adds topCard to the top of cardPile2
	[cardPileView2 display];		// must redisplay the pile...

I have added a few things to the CardSet that may be of use to you.  In the Card Class, I have added a new instance variable, userValue, that you can use to set a value of the card independent of what it physically is.  I needed this because changing the value of a Card changes what the card looks like -- for example changing a King to having a value of 10 will cause the Card to look like a Ten.  Anyway, use setUserValue: and userValue to set the user value and get the user value, respectively.

The the CardPileView class, I have added the method setUseShoe:.  This just changes the way a deck of cards is displayed.  When you send a CardPileView the setUseShoe:YES message, it will display its CardPile inside a casino-style shoe box thing.   I used it a lot.  The size of the shoe is 113 points wide and 240 points tall.  You don't need to display the whole height of the shoe, but you should display the whole width.

The CardPile class has a bunch of new methods, mostly dealing with Poker hands.  There are methods to test a CardPile to see if it is a full house, flush, straight, etc.  There are also methods to find the lowest or highest cards in the pile.  There's a lot of them, so you should check out the documentation on this one.

8.  Debugging Your Module
Since the modules are loaded dynamically, I have found it difficult to use the debugger with them (I can't figure it out, anyway).  And besides, sometimes you want to make the game do certain things in order to test it.  I have added a "hidden" feature that you can use to debug your module.  When you type [CONTROL]+[SHIFT]+[ALT]+'d', the current Dealer gets sent the message runDebug (declared in NVDealer, so your object inherits it).   Blackjack uses this method to bring up a panel that allows me to pick a player, a player's hand, and a card, and swap it out with the next card from the deck.  That way I can test different hands and make sure the module interprets them correctly.  Anyway, its a feature that you may find useful....



Some of the important messages that get sent to your Dealer, when, and why...

- initFromBundle:aBundle withName:(const char *)name;
This method is defined in NVDealer and should be left alone.   It is sent immediately after your module has been dynamically loaded.

- gameView;
Sent to your Dealer during loading to by PitBoss to get your modules gameView.  Return the gameView.  (NVDealer already does this for you, you don't need to worry about it really...).

- (BOOL)hasRules;
Sent to your Dealer during loading by PitBoss to see if your module has rules.  Return YES if it does, NO if it does not.  By default, NVDealer returns NO, but Template returns YES (if you're using Template as a starting point).

- inspector:sender;
Sent to your Dealer during loading by PitBoss to see if your module has an inspector.  It should return the view of your inspector.  For example, my modules group all their preference items in a box, and this method returns the contentView of the box.  If you've started with Template, this is already taken care of for you.

- view:aView wasLoadedOnTable:tableObject;
This message is sent to your Dealer after your gameView (which is aView) has been loaded onto the table, but before it is displayed.  

- inspectorWasInstalled:sender;
This gets sent to you when the player has requested the Preferences Panel, and your module is active.

- rulesWindow;
If your module has rules, this method should return the window that has the rules on it.  If you've started with Template, this is handled for you.

- (BOOL)playerWillJoin:sender;
Sent to your Dealer by PitBoss when "New Player" is selected.  Return NO if you don't want a new player added, and YES if it is okay.  By default, NVDealer returns YES (PitBoss won't allow more than 4 players anyway).

- playerDidJoin:player;
Sent to your Dealer when a new player enters the game.  You may want to reset the table or something.

- mouseEnteredView:sender
Sent to your dealer when a player moves the mouse over a BetView sender.

- player:aPlayer willBet:(int)betType onView:aBetView
Sent to your dealer when a player mouses down on the BetView aBetView.  Since any player can mouse down on a BetView, you must take care to make sure that you deal with the right player.  aBetView sends you the current player at the time of the mouseDown event, aPlayer.  This player, however, may not be the proper player.  aBetType is one of the following constants:

Constant	Description
NV_BET	When the player simply mouses down on the view
NV_REMOVEBET	When the player mouses down while pressing the SHIFT key.
NV_BETOTHER	When the player mouses down while pressing the ALTERNATE key.
NV_REMOVEOTHER	When the player mouses down while pressing the ALTERNATE & SHIFT keys.
NV_BETOFF	When the player mouses down while pressing the CONTROL key.

It is up to your Dealer to interpret this message and respond by either doing nothing or making/removing a bet for the proper player.  You should make sure that the player doesn't bet more than they have, or maybe that they don't go over the table maximum, etc.  You can than remove a chip from the player, and make the bet:
	
			int amount = [aPlayer selectedChip];
	
			case NV_BET:
				if([aPlayer removeChip:amount])
				{
					[sender bet:amount atRow:0 col:0];
				}
				break;
			...
				
When aBetType is NV_BET, the player should be attempting to place a bet, and NV_REMOVEBET is when they are attempting to remove a bet.  NV_BETOTHER and NV_REMOVEOTHER can be defined by your module.  I have used them for "Bet Minimum" and "Remove All", respectively.

- mouseExitedView:sender
Sent to your Dealer when the mouse moves out of the BetView sender.

- (BOOL)playerWillDragChipAtRow:(int)row andCol:(int)col fromView:sender
Sent to your dealer when the player tries to drag a ChipPile from the BetView sender.  row and col are the row and column that the ChipPile is located.   Return NO if you don't want the pile moved, YES if it is okay.  By default, NVDealer returns NO.

- playerDraggedChipAtRow:(int)row andCol:(int)col inView:sender
Sent to your dealer after a player has dragged ChipPile from the BetView sender.   row and col are the row and column that the ChipPile is located.   

- (BOOL)playerWillDropChipAtRow:(int *)row andCol:(int *)col InView:sender
Sent to your dealer when the player drags a ChipPile into the BetView sender.  row and col are pointers to the row and column that the ChipPile will be deposited.   You can reset row and col so that the ChipPile will deposited in the proper row and column.  Return NO if you don't want the pile dropped, YES if it is okay.  By default, NVDealer returns NO.

- playerDroppedChipAtRow:(int)row andCol:(int)col inView:sender
Sent to your dealer after a player has dropped ChipPile onto the BetView sender.   row and col are the row and column that the ChipPile was dropped..   

- preferencesChanged:sender;
See above...

- setPreferences:sender;
Sent by PrefController when the player hits the Set Button on the Preferences Panel.  Use this method to write your preferences.

- revertPreferences:sender;
Sent by PrefController when the player hits the Revert Button on the Preferences Panel.  Use this method to revert your preferences to their last saved state.

- (int)collectAllBetsForPlayer:(int)playerNum;
Sent to your dealer when the player hits the "Collect All Bets" button on their window.  playerNum is the index or tag of the player who has made the request.  You should remove all the bets that the player has on the table (if they can be removed), and return the total amount that you have removed.  That amount will be added to their bank roll.

- (BOOL)playerWillClose:aPlayer;
Sent just before PitBoss closes aPlayer's window.  If you don't want the player to leave, you can return NO, else return YES.

- playerDidClose:aPlayer;
Sent when aPlayer leaves the game.  You may want to reset the table or something.

- runDebug;
Sent to your dealer when the user presses [ALT]+[CONTROL]+[SHIFT]+'d', signaling that they want your module to run a debug session.  Use it to bring up a panel or something that can help you debug your module.

- inspectorWillBeRemoved:sender;
This gets sent to you during the unloading of your module.

- finishSessionAndClose;
This message is sent right before your game view is unloaded from the table (when the player selects a new game, for example).


Finally...

	Well, I guess that's about it.  I apologize if this information is incomplete.  I imagine that it is probably more than anyone would need, though.   You've got my source code to use as examples, which should be pretty easy to follow.  

	GOOD LUCK!  And let me know how you do!
	
	Mark Trombino
	trombino@wendy.ucsd.edu

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.