Sign in to follow this  
Akusei

Cheat codes

Recommended Posts

Here are the header and cpp file for a cheat code class I've made along with a usage example. I've never done anything like this before so please let me know what you think.

CheatCode.h
[source lang="cpp"]
#pragma once
#ifndef CHEATCODE_H
#define CHEATCODE_H

#include "Global.h"
#include <SFML\Graphics.hpp>
#include <vector>

class CheatCode
{
private:
std::vector<sf::Key::Code> m_Code;
DWORD m_KeyCount;
DWORD m_TriggerCount;
DWORD m_MaxTrigger;
BOOL m_Triggered;

public:
CheatCode();
~CheatCode();

void Init(char *Code, DWORD TriggerCount = 1);
void RegisterKey(sf::Key::Code Key);
BOOL IsTriggered();
DWORD GetTriggerCount();
void Reset();
};

#endif
[/source]

CheatCode.cpp
[source lang="cpp"]
#include "CheatCode.h"

using namespace std;
using namespace sf;

CheatCode::CheatCode()
{
this->m_MaxTrigger = 0;
this->Reset();
}

CheatCode::~CheatCode()
{
this->m_Code.clear();
}

void CheatCode::Init(char *Code, DWORD TriggerCount)
{
DWORD size = strlen(Code);

for (int i = 0; i < size; i++)
{
char ch = tolower(Code[i]);
this->m_Code.push_back((Key::Code)ch);
}

this->m_MaxTrigger = TriggerCount;
}

void CheatCode::RegisterKey(sf::Key::Code Key)
{
if (this->m_TriggerCount >= this->m_MaxTrigger)
return;

if (this->m_Code.at(this->m_KeyCount) == Key)
this->m_KeyCount++;
else
this->m_KeyCount = 0;

if (this->m_KeyCount >= this->m_Code.size())
{
this->m_TriggerCount++;
this->m_Triggered = TRUE;
this->m_KeyCount = 0;
}
}

BOOL CheatCode::IsTriggered()
{
return this->m_Triggered;
}

DWORD CheatCode::GetTriggerCount()
{
return this->m_TriggerCount;
}

void CheatCode::Reset()
{
this->m_KeyCount = 0;
this->m_TriggerCount = 0;
this->m_Triggered = FALSE;
}
[/source]

usage
[source lang="cpp"]
//initialize the cheat code
//this might go in a class constructor or init function
this->m_CheatTonk.init("tonksmash");

//this is an example game loop showing just an example of using this cheat class
while (this->m_App.IsOpened())
{
Event msg;
while (this->m_App.GetEvent(msg))
{
if (msg.Type == Event::Closed)
this->m_App.Close();

if (msg.Type == Event::KeyPressed)
{
this->m_CheatTonk.RegisterKey(msg.Key.Code);
}
}

if (this->m_CheatTonk.IsTriggered())
{
Sprite sprite(*this->m_ResMan.GetImage("RESOURCE1"));
this->m_App.Draw(sprite);
}

this->m_App.Display();
}
[/source]

Any suggestions or things I should change, things I'm doing wrong or even things I'm doing right? Thanks

Share this post


Link to post
Share on other sites
Not bad :-)

I would make a couple of suggestions:
[list][*]Your game loop where you test the message type could use an else-if instead of two separate ifs[*]You could simplify things a bit by using std::string[*]No need to clear a vector (or any SC++L container, including strings) in your destructor. The container destructor will do it for you automatically[*]Your TriggerCount parameter to Init() should be named MaxTriggerCount to make it clear what it does[*]Any reason why you use a separate Init() instead of a constructor for the cheat code?[/list]

As a fun exercise, try writing another piece of code that lets you set up multiple cheat codes easily, without having to copy/paste a lot of code :-)

Share this post


Link to post
Share on other sites
[quote name='ApochPiQ' timestamp='1318367333' post='4871610']
Not bad :-)

I would make a couple of suggestions:
[list][*]Your game loop where you test the message type could use an else-if instead of two separate ifs[*]You could simplify things a bit by using std::string[*]No need to clear a vector (or any SC++L container, including strings) in your destructor. The container destructor will do it for you automatically[*]Your TriggerCount parameter to Init() should be named MaxTriggerCount to make it clear what it does[*]Any reason why you use a separate Init() instead of a constructor for the cheat code?[/list]

As a fun exercise, try writing another piece of code that lets you set up multiple cheat codes easily, without having to copy/paste a lot of code :-)
[/quote]

Great, thanks for the suggestions, I will make these changes. The reason I went with a separate init function and a default constructor with no parameters is because I wanted to keep the cheat codes as a private member variable of the main game class and I would have to make them all dynamically allocated and handle deleting them later because I can't initialize the class variable inside the header... for example:

I can't do this
[source lang="cpp"]
class test
{
private:
CheatCode m_code("codestring");
};
[/source]

I could do this, but want to avoid having to allocate with new were possible
[source lang="cpp"]
class test
{
private:
CheatCode *m_code;
};

test::test()
{
this->m_code = new CheatCode("codestring");
}

test::~test()
{
delete this->m_code;
}
[/source]

Or am I completely missing something or possibly not understanding it correctly?Is it just better practice to use the constructor for initialization and not have an init function?

Share this post


Link to post
Share on other sites
[quote name='Akusei' timestamp='1318370207' post='4871626']
Great, thanks for the suggestions, I will make these changes. The reason I went with a separate init function and a default constructor with no parameters is because I wanted to keep the cheat codes as a private member variable of the main game class and I would have to make them all dynamically allocated and handle deleting them later because I can't initialize the class variable inside the header... for example:

I can't do this
[source lang="cpp"]
class test
{
private:
CheatCode m_code("codestring");
};
[/source]

I could do this, but want to avoid having to allocate with new were possible
[source lang="cpp"]
class test
{
private:
CheatCode *m_code;
};

test::test()
{
this->m_code = new CheatCode("codestring");
}

test::~test()
{
delete this->m_code;
}
[/source]

Or am I completely missing something or possibly not understanding it correctly?Is it just better practice to use the constructor for initialization and not have an init function?
[/quote]

Initializer lists can help with this:

[source lang="cpp"]
// H
class Test {
private:
CheatCode code;
};

// CPP
Test::Test() :
code("cheatcode") {
}
[/source]

You can call constructors of private members (on stack or on heap) in the initializer list of the constructor of the class.

Share this post


Link to post
Share on other sites
[quote name='colinhect' timestamp='1318371314' post='4871633']
Initializer lists can help with this:

[source lang="cpp"]
// H
class Test {
private:
CheatCode code;
};

// CPP
Test::Test() :
code("cheatcode") {
}
[/source]

You can call constructors of private members (on stack or on heap) in the initializer list of the constructor of the class.
[/quote]

Thanks, that works great. Although it seems a little clumsy i guess you could say; it could just be me and my limited knowledge of how things are meant to be done (read: best practices). Is this the "proper" way to do it or would allocating a new instance of the CheatCode class and likewise destroying it in the destructor be more acceptable?

Share this post


Link to post
Share on other sites
[quote name='Akusei' timestamp='1318374642' post='4871647']
Thanks, that works great. Although it seems a little clumsy i guess you could say; it could just be me and my limited knowledge of how things are meant to be done (read: best practices). Is this the "proper" way to do it or would allocating a new instance of the CheatCode class and likewise destroying it in the destructor be more acceptable?
[/quote]

This is the "proper" way and it is not clumsy at all. When a member of an object is initialized on the stack it is destroyed (the destructor is called) after the destructor of the class that contains it is called. If you are allocating a new object in the constructor and deleting it in the destructor then most likely it should just be on the stack (like in the example I posted).


I would suggest reading some material on C++ object lifecycles and initialization lists. It is also helpful to write up some tests with constructors/destructors that print to the console to get a good idea on the ordering of constructors/destructors. This is also important for inheritance; if you aren't careful you will find that your destructors are not even begin called.

Hope that helps.

Share this post


Link to post
Share on other sites
What is actually clumsy is having "this->" all over the place.
I recommend doing a search and replace to change "this->" to "" i.e. an empty string.

Initialisation lists are definitely the correct way to go in C++.

Note that because you do not need to manually clear a vector in a destructor (that's the vector's responsibility), you should actually remove the destructor entirely rather then leaving it empty.

Share this post


Link to post
Share on other sites
Hidden
[quote name='iMalc' timestamp='1318397829' post='4871736']
What is actually clumsy is having "this->" all over the place.
I recommend doing a search and replace to change "this->" to "" i.e. an empty string.

Initialisation lists are definitely the correct way to go in C++.

Note that because you do not need to manually clear a vector in a destructor (that's the vector's responsibility), you should actually remove the destructor entirely rather then leaving it empty.
[/quote]

Could you explain to me why the "this->" is clumsy and not just a personal preference like having a curly bracket on the same line as the if statement instead of on the line below? Also, please explain the reasoning behind leaving the destructor out all together if it's empty. I'm not trying to sound rude or anything; I feel I need to say this because no matter how I wrote those questions it came across as being a jerk, but in all honesty I'm just curious and would like to know.

edit:
There are a couple reasons why I use "this->"

1. It's a habit that formed a long time ago to trigger intellisense in visual studio for things in the class' scope, which allows for very quick and easy glances into what I've created as variables/objects in the class scope without having to re-read the header file over again after I've taken a month broke from the project.

2. It helps me to, at a glance, see what scope that variable or object is at (along with the m_ designation). anything with a this-> is not the local function or block scope.So anything with a this-> I know is class scope and anything without I know is local to that code block.

Share this post


Link to post
[quote name='Akusei' timestamp='1318399190' post='4871741']
There are a couple reasons why I use "this->"
[/quote]

Using this-> along with m_ just seems redundant. It would seem much easier to read - especially if asking for peer review or collaborating - to choose one or the other. Personally I find m_ preferable to this-> but I know people who feel the opposite way. If you're thoroughly attached to the intellisense thing then perhaps eliminating m_ would be preferable?

Share this post


Link to post
Share on other sites
[quote name='iMalc' timestamp='1318397829' post='4871736']
What is actually clumsy is having "this->" all over the place.
I recommend doing a search and replace to change "this->" to "" i.e. an empty string.

Initialisation lists are definitely the correct way to go in C++.

Note that because you do not need to manually clear a vector in a destructor (that's the vector's responsibility), you should actually remove the destructor entirely rather then leaving it empty.
[/quote]

I understand your opinion, however your suggestions are related solely to personal preference or aesthetics. Your suggestions do not make the code any more understandable, let alone improve performance or functionality in any way. Leaving out the empty destructor does not increase program performance or help code readability and is not a required C++ standard, it is simply a personal preference and does not affect anything. Using the this pointer is the same thing, personal preference and it in no way hinders performance nor does it hinder readability; in fact, I use the this pointer to increase code readability and understanding.

The other suggestions in this thread made by the other gamedev members were very helpful and insightful. Using initialization lists was a wonderful tip and leaving out the vector::clear() because it is unnecessary, not only increases readability but helps to improve program performance because an unneeded call is no longer being executed. The suggestion to add an else-if instead of 2 ifs was great as well because it increases, again, both readability and performance; instead of processing 2 if statements, at its best it will only process 1. Those are three examples of great suggestions.

With that said, I do value your suggestions and opinions but please if you're going to critique or suggest changes for code, make sure they are non-cosmetic and not related to personal preference. If you believe that the suggestions you note are of significant value then please cite sources for your argument.

Share this post


Link to post
Share on other sites
[quote name='latent' timestamp='1318400944' post='4871752']
Using this-> along with m_ just seems redundant. It would seem much easier to read - especially if asking for peer review or collaborating - to choose one or the other. Personally I find m_ preferable to this-> but I know people who feel the opposite way. If you're thoroughly attached to the intellisense thing then perhaps eliminating m_ would be preferable?
[/quote]

Yeah, the "m_" is also a habit I picked up... but it does help when intellisense pops up, you have all your variables and objects listed in one spot because they all start with "m_", whereas the member functions and other odds and ends do not. Though, again, this all boils down to personal preference and is not an actual suggestions on the functionality and overall OO design of the CheatCode class.

However, between the this pointer and the "m_", if I had to drop one, I would drop the "m_" and may just drop it from now on. Thanks.

Share this post


Link to post
Share on other sites
I generally would not use Win32 types unless the code is designed to interact with the Windows API. This also indicates that you're pulling in the Windows header somewhere, which should be unnecessary for code like that. Likewise, if sf::Key::Codes map directly to characters, I would probably just store a std::string instance, and have my interface in terms of characters. Again, this means yet another header can be removed, and the class is usable with other frameworks, rather than tied to SFML.

I believe the value of m_Triggered can be derived from the expression m_TriggerCount > 0. It could be removed.

The code does not attempt to enforce const correctness - i.e. I cannot pass a CheatCode by const reference to a function and do anything useful.

Having a declared destructor is more than a stylistic issue, when looking at the header file it makes the class appear to have complex lifecycle semantics, and would prompt questions about whether the class respects the rule of three. In other words, valuable time lost because I would need to open the implementation to discover that this is not the case.

Share this post


Link to post
Share on other sites
You can get a lot of ideas from [url="http://lspiroengine.com/?p=126"]this[/url] article.
Some of it is cosmetic, but nearly all of the “Structure” section is more than just aesthetics. Reasons for each point are given so that you can know why it can be helpful and help you decide whether you want to adopt it or not.

Mainly, I would point out the order of your private/public sections (see the guide for the reasons) and the alignment of your functions. If the names of each function are indented to the same level, it helps scanning for functions a ton.

Not mentioned in my guide, but correctly mentioned above, is const correctness. Pay attention to that strictly.


L. Spiro

Share this post


Link to post
Share on other sites
Another set of great suggestions, thanks guys! I do have a couple questions about them though. First, could you point me in the right direction or give me an example of const correctness? I'm not too familiar with that and would love to learn more. Second, if it is confusing or more work to see a destructor and then have to open implementation would it be better to just declare the destructor in the header like this:

[source lang="cpp"]
class test
{
public:
test();
~test() { }
}
[/source]

It's really no big deal if I remove it completely, I just feel it is still more of a personal preference than anything else. In the above example, I would no longer need to open the implementation and at a glance can tell that the destructor is not used.

Oh, and great idea about using m_Triggered to replace the bool variable; I can't believe I didn't catch that!

Share this post


Link to post
Share on other sites
[quote]
First, could you point me in the right direction or give me an example of const correctness?
[/quote]
[url="http://www.parashift.com/c++-faq-lite/const-correctness.html"]Const correctness[/url].

[quote]
It's really no big deal if I remove it completely, I just feel it is still more of a personal preference than anything else. In the above example, I would no longer need to open the implementation and at a glance can tell that the destructor is not used.
[/quote]
Sure. Still, why bother including it? What advantage does it bring to explicitly define this destructor?

Share this post


Link to post
Share on other sites
[quote name='rip-off' timestamp='1318433791' post='4871877']
Sure. Still, why bother including it? What advantage does it bring to explicitly define this destructor?
[/quote]

Thanks for the link and you do have a good point about the destructor. I think I'll not include the destructor if it is empty.

Share this post


Link to post
Share on other sites
[quote name='rip-off' timestamp='1318433791' post='4871877']
[url="http://www.parashift.com/c++-faq-lite/const-correctness.html"]Const correctness[/url].
[/quote]

So, I looked at the parashift link and understood a little bit, but am still slightly confused. I'll just have to dig a little more but for now, this is what I came up with for const correctness in my header file. Does this look like I caught everything as far as const correctness goes?

[source lang="cpp"]
class CheatCode
{
public:
CheatCode(const std::string &Code, DWORD MaxTriggerCount = 1);

void RegisterKey(char Key);
bool IsTriggered() const;
DWORD GetTriggerCount() const;
void Reset();

private:
std::vector<char> Code;
DWORD KeyCount;
DWORD TriggerCount;
DWORD MaxTrigger;
};
[/source]

Oh, and as for the windows types like DWORD, I declare those in the global.h file anyway, even if I'm not using windows. I like using unsigned types and typing it all out is a pain and coming from a reverse engineering and assembler background I like the terms WORD, DWORD and such. So even though they are defined in a windows header, that doesn't necessarily mean I'm using the windows headers.

Also, I tried the whole "string const& code" thing instead of "const string &code" and I can't help it, I like the const first one, the other way just looks funny to me.

Thanks

Share this post


Link to post
Share on other sites
[quote]
Does this look like I caught everything as far as const correctness goes?
[/quote]
Yes.

[quote]
Oh, and as for the windows types like DWORD, I declare those in the global.h file anyway, even if I'm not using windows. I like using unsigned types and typing it all out is a pain and coming from a reverse engineering and assembler background I like the terms WORD, DWORD and such. So even though they are defined in a windows header, that doesn't necessarily mean I'm using the windows headers.
[/quote]
Fair enough.

[quote]
Also, I tried the whole "string const& code" thing instead of "const string &code" and I can't help it, I like the const first one, the other way just looks funny to me.
[/quote]
I use the latter style myself, and so does most C++ code I've come across.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this