A Development Tutorial: Chapter 18
|A Development Tutorial: Chapter 18|
|Get the developer tools and try building new content for this game!|
|This Chapter||18: A Roulette Game|
|More Chapters||Title Page|
Development Kit Setup
The World Map Editor
Create a 2-Map Zone
Monsters, Loot, and Scripts
Tracking Quest Progress
The Scripting Environment
Understanding Quest Scripts
Dialogue: The "SAY" Menu
Floating FA Text
Dialogues vs Scripts
Simple Kill Quest
Mysterious Stranger Perk
Black Jack Game
Black Jack Installation
We will write what the name suggests. At first, it's sounds like an easy and fast task, but once you get into details, you will see it's quite long. The good side of it is, that it helps us understand a lot about dialogues. Almost everything. And gives a lot of practice also.
First, I will explain the mechanic of how our roulette will work, then you should copy/paste the codes and use it to try it out on your development environment. I will give step by step instructions to make it seamless. After that I will explain most of the functions, the structure and highlight some specifics that can be useful later on. The code will be attached at the end.
Our Simplified Roulette
- Numbers range from 0 to 36, no double zero.
- Player can bet only one bet at a time, and only one player can play with the host.
- The following bets are available:
- Straight Bet: A chosen number is the bet. Chance to win is 2.7% (1/37), win multiplier 35.
- Color Bet: Red or black. Chance to win is around 48% (18/37), win multiplier is 2.
- Parity Bet: Even or odd. Chance to win like color bet.
- Mangue Bet: Numbers from 1 - 18 win. Chance to win like color bet.
- Passe Bet: Numbers from 19-36 win. Chance to win like color bet.
- Player places a bet on one of the bets, then pays, then the table will roll the winning number and it's color, then this is compared to the players bet and the results are decided.
- Player plays against the host/table difficulty. The NPC Gambling skill is compared to the Players Gambling skill, in order to achieve negative effects or not. If the player's skill is equal or higher than the host's skill, there are no negative effects concerning the roll. If the player's skill is lower, then there is a chance that if the roll matches the winning pattern, a re-roll is made. If that is a win again, then the player still can win. This is how the gambling skill is simulated, table's altering effect to different odds and the players ability to detect, thus counter it, or whatever reasons to justify the role-play :)
- How to use scripts in dialogue answers:
- Add a demand or a result to the answer.
- On the demand/answer select the script radio box.
- Write the script file name, a "@" and the script function name to specify the function that shall be called when the answer is selected by player. (for example: gambling_roulette@r_SetBetValue)
- Select the parameters required, the Critter and the NPC parameters are set by default, you don't need to set those.(for example: 100)
- If you don't have parameters set it to NA.
- For this, there has to be a script file by the name (gambling_roulette.fos) and a function inside it with the required parameters (Critter& player, Critter@ npc, int betValue)
- The used prefix for functions that are called from dialogues is "d_" for demand calls and "r_" for result calls.
- Whenever you find in the scripts a function starting with "d_", that means that function is called in a dialog as a demand somewhere. Check dialog.fos for examples.
- How to use "lexems" (dynamic text) in dialogues:
- When you write the dialogue you need to link a script to it with similar formula how you add script to demands/results. (module_name@function_name)
- By default you can select None or Attack, but just write in the module@function it will work.
- You also need a script with the specified name in the specified script file and an extra parameter of type string. (example: "void dlg_ShowBetInfo(Critter& player, Critter@ npc, string@ text)").
- To use the lexems, use the following formula: "@lex variableName@"
- In the script, add the following line to set the variable name: text += "$variableName" + "enter value here"; (example: text += "$betValue" + 250;).
- The prefix used for scripts that are accessed from dialogs is "dlg_" (ex: dlg_ShowBetInfo).
- How to use the "Say" textbox in a dialogue:
- You need to link a script to the dialog as described previously.
- The signature of the function should be like this: "uint dlg_SetBetNumberFromDialogue(Critter& player, Critter@ npc, string@ say)". It has to return a uint type, that will say where to return after the player pressed the "Ok" on the say dialogue.
- If the return value is 0, then the dialogue will result in the same dialogue step. You can use "player.Say(SAY_DIALOG, "message");" to change the current dialogue message. This will look like you have a new one, but as of structure it's the same with different text, meaning the same function is called when u used the "Say" option again.
- If the return value is other than 0, then the dialogue will jump to the specified number in the dialogue structure.
- Function prefixes for dialogues:
- "d_" - demand prefix, added to function names which are called from an answer demand.
- "r_" - result prefix, added to function names which are called from an answer result.
- "dlg_" - dialogue prefix, added to function names which are called from dialogues.
- Clean code:
- This topic is a bit advanced here, but definitely needed. The reason for that is, that most developers, especially those used to C or other lower level languages tend to over optimize stuff or just express themselves in ways that other coders don't understand easy. While this sound cool, the drawback is that at larger projects it will back stab. Always. You can find a lot on this topic on the net, searching around, but here we will only talk about some basic ideas, how to keep your code clean (easily readable for everyone, including yourself later). At first, as for a rookie, the code in this tutorial seems too large and hard to understand because of this. You will need a good IDE, and you will get used to this very soon, after that it will make much more sense and you will agree that this is much better this way.
- A code (in our case considering that our tools and language is nowhere comparable to high level programming languages like Java, C# etc) is clean when:
- There are no needs for comments, because the names of variables and functions describe very well their behavior and the logic is easy to follow. (I made a lot of comments, but that was for the purpose of the tutorial, barely any of those is needed, as you probably can see already.)
- Interfaces are separated from main logic code. (For example see how the functions that are used by dialogues are written.)
- Follows the same naming conventions and rules through the whole project. (example: the dialogue prefixes mentioned above)
- Higher level logic and lower level logic are separated, and not in the same function.(example: r_SpinTheRoulette)
- Add anything to the list which you can find applicable here by the Clean Code standards, what I mentioned above are the minimum.
- Clean code should always be used when the performance of the module, function is not critical. In that case, performance optimization overrides this, however that is usually less than 5% of the whole projects code base and I don't think that here is different, but it's for you to find it out.
- Our casino game is obviously not a performance critical scenario, so the worst thing one could to is to fill it with left shifts to moving around flag values, etc.
- Also, our casino game is designed to be extendable in the future. The sign for this is the modularity. The interface can be replaced to a GUI later on if needed, without changing too much of the actual code. If we had classes I would advise to make your programs open for extension but closed for modification, but that is another story.
Full dialogue in editor:
Full Source Code
- Header file used for gambling: gambling_h.fos
- Main script file for roulette operations: gambling_roulette.fos
- Dialog file for casino roulette host: all_casino_roulette_host.dlg
- Dialog list modifications inside the dialogues list file, add this line to dialogs.lst:
- "$ 2110 all_casino_roulette_host"
- Variable list modification: _vars.fos
- All that is left, that you make a new NPC near a casino table (or edit one) and set it's dialogue number to 2110.
- obj& does not need valid() check - Fixed
- lines 471-479 could be just return( Random(1,100) <= gamblingblabla );
- 488+ is slow. wait, slow and ugly
- if( red.find(number) >= 0 ) return GAMB_CRAP; - Fixed
- getBetTypeAsString() begs for switch() - Fixed (did not become better)
Ideas to Enhance the Gambling Script
- Make the roulette offer advantages as well if the gambling skill is raised, not just the diminishing of penalties.
- Add script that actually helps (has a chance to help) the player in case he has higher gambling than the host NPC.
- Limit how exploitable by cooldown timers if a player has won too much.
- Add a random factor to host NPC gambling skill. This means that whenever a session starts, the host NPC's gambling skill is modified to a different value, depending on the base gambling value. So there is no sure way to know if someone spams the tables, he eventually comes out with a net win.
- Add some special quest/encounter where the casino lord sends some bounty hunters after the player.
- Additionally, if the player won a huge amount, some thugs NPC's will follow him and try to ambush him. Maybe these assassin would be able follow the player even to his tent, kill him and loot random stuff from there!
- Items or quests that temporarily affect gambling.