c++

You are currently browsing the archive for the c++ category.

Game developers have an all new playing field when it comes to picking their game engines.
Read the rest of this entry »



Tags: , ,

Hey everyone! It’s been a long time since I last posted but that’s because I’ve been busy with life. This morning, being free for the first time in almost a month, I decided to work on some code so I decided to make a proper program to encrypt/decrypt PSP savedata. This is much simpler than the JPCSP/PPSSPP hack (which only decrypted savedata). Despite previous failed attempts, this time it worked! So here’s the release!

Read the rest of this entry »

Tags: , , ,

Android, playing PSP games with the PPSSPP emulator. I’m going to jump right into it, you will either need a dual core or quad core processor in your phone. I would recommend that it is rooted and use CPU master app to make your device run at top speed to get the best results from the PSP emulator (PPSSPP) on your Android device.

Read the rest of this entry »

Tags: , , , , ,

Using a Raspberry Pi for reto I know, that it’s underpowered for quite a few different emulators. I’m very surprised what you can play on it . When you first buy a Rasberry PI, you will have to download their free os called Raspbian. Then you can sign up to the PI store, so you can download all kind of apps. They have four good classic gaming emulators available, all of them are free downloads: MAME4ALL , PiSNES — Super NES emulator, Pcsx_reARMed — PlayStation1, Atari800 — Atari 8-bit computers (800, XL, XE, etc.)

Read the rest of this entry »

Tags: , , , ,

Would you like to use your PS3 accessories on your PC? Well I have found three different third-party drivers and software that works great on your Windows or Linux PC.

Read the rest of this entry »

I’m sorry for the title, it was the best I could come up with.

This article comes from a message I was sent by dragonxtamer596, and also thank you to everyone else who suggested for this to be written on.

Unity / Unity3D is a game engine developed by gaming company known as ‘Unity Technologies’. They have done many things, so let’s see what they’ve done shall we?

Read the rest of this entry »

Like I requested in my previous article, commenter vFX suggested I write about the Source engine. So here it is, and as always, if you’re expecting an in depth review of every bit of history,feature, spec. Then you’ve come to the wrong place. Sorry!

Read the rest of this entry »

From the developer of the renowned Dolphin Emulator comes an update to the multiplatform, open source PSP emulator which has just been ported to iOS devices! Read the rest of this entry »

 

Just like my previous article, I have decided to do it on technology that has impacted the games industry more than English people like tea (I’m allowed to, I’m British. Yes, I do love it). Read the rest of this entry »

After studying both the Gripshift and the MOHH exploit, I now feel confident to write a little guide on creating a binary loader.

What This article is about

The first step to running homebrew on a PSP is to find a user mode exploit, which is done by taking control of the Ram through techniques such as a buffer overflow in a game or crafted images.
Imagine that you’ve found such a vulnerability, and have full control of our beloved variable $ra. The next step for you is to create a proof of concept. The basic idea is to create a binary loader, that is, a piece of code able to load another piece of code from a file on the memory stick, then run it.
Writing a binary loader is fairly simple once you know how to do it, but it relies on lots of tribal knowledge, as there is no central place that gathers that kind of information.

So there it is :)
Disclaimer: I just learned how to do it myself very recently. Some of the techniques I describe here might not be optimal. I also suck big time at MIPS. Oh, and I tend to write a ruby script for things that could probably go in a Makefile. As usual, take everything you read on the internet with a grain of salt.

In this guide I’m assuming you already have control of the variable $ra and can change it to whatever value you want. I’m taking the example of a savegame. Some steps are fairly different if you found a vulnerability in the XMB, but overall the principles are the same. I’m assuming you know how to use PSPLink, and have a fair knowledge on how to use a hexeditor, scripting languages, a bit of C and/or MIPS,  basic Makefiles…roughly, the kind of stuff you learn writing homebrews :)

Tools you will NEED (Download links)

  • prxtool and psplink are part of the minimal psp sdk. Honestly you should already have this installed if you are reading this.
  • For windows users, cygwin might be useful, and install ruby with it if you plan to run my scripts
  • A Hex editor (on Windows I use XIV32)
  • Savegame Deemer to work on savegame data easily
  • PRXDecrypter to decrypt Eboots
  • SED (Savegame Encrypter/Decrypter) to encrypt your exploit once it’s ready
  • Silverspring’s libdoc (for 6.60, update thanks to thecobra)

Contents

Writing a binary loader from a savegame exploit requires the following steps:

  1. Find a place to jump to
  2. Find the function imports
  3. Compile a binary loader
  4. Inject your binary loader in savegame
  5. Write a SDK adapted to your game
  6. Create a small “proof of concept” binary file (a.k.a. “Hello World”)
  7. Reencrypt your savegame

Find a place to Jump to

Run psplink and crash your game like you “usually” do. Keep that running, you’ll need it for this step.

You have control of $ra, and need to have it jump to wherever you will inject your binary loader code. Basically, your code will be injected in the savegame, so you need to know where in Ram that piece of the savegame ends up. To do that, you need to visually compare your Ram and the savegame. To do a dump of your ram just after you found a crash, type savemem 0×08800000 200000000 memdump.bin in psplink. This will create a memdump.bin file on your drive, which should be roughly 24MB.
Now look for a very recognizable pattern in your savegame. A series of letters or bytes that you can easily look for. And look for these values in your Ramdump as well (with an hexeditor, obviously). In the image below, my ramdump is on the left, and the savegame is on the right.

Once you’ve found them in you’re ramdump, you pretty much know where you will want to jump, AND where you will inject the code in your savegame. In this example, my pattern is at offset 0x43F0 in the savegame, and at offset 0x32C3E0 in the ramdump. That last one is actually 0×8800000 + 0x32C3E0 (0x08B2C3E0) in ram since we dumped our file starting at 0×08800000. You can check if your psplink is still open: you should find your pattern if you type:  memdump 0x08B2C3E0 20 (replace the address with whatever you chose, of course).
You want to make sure the place you jump to is exactly equivalent to the pattern you chose in your savegame, over a few hundred bytes. If there are differences in this area between your savegame and the ram, it means this savegame block is not entirely loaded at this position, and it will make your code injection more difficult (you want your code to be in one block, not splattered all over the Ram :P)
So you have step one: we found a nice place to jump to (0x08B2C3E0), and where in the SDDATA.BIN we will inject our binary loader (0x43F0).

Find the function imports

OK, this one sounds extremely difficult, as if it required lots of exceptional knowledge: that’s not the case. Or, rather, we have tools that do the work for us.
When you write a homebrew, you call functions of the PSPSDK. The position of these functions in Ram is not known in advance in the case of a game exploit, so we need to find them and “redirect” them correctly.
In the case of a game, you’ll want to extract the “EBOOT.BIN” from your game and decrypt it. The EBOOT.BIN is inside the iso of your game, so to get it you will need an iso of your game. If the game is an UMD, it’s very easy to do on any custom firmware with the “USB Drive” option that you can find in any custom firmware. (If the game is a PSN game, you’ll need NPDecryptor to create the ISO).

Most likely, this “EBOOT.BIN” is encrypted, so you additionally need to decrypt it with prxdecrypter.


Once you have your decrypted EBOOT.BIN handy, use prxtool to retrieve information from it, with the following syntax:

prxtool -f EBOOT.BIN

This will give you the actual addresses of each function that the game imports and uses. These are the functions that you will be able to use in your homebrews or in the binary loader.


You’ll notice that instead of function names, you get a library name followed by hexa values. From there you can either manually do the associations, or (better) get an xml file with the nids/function names associations. Such xml files can be found on silverspring’s website (http://silverspring.lan.st/).
Once you have one of those xml nids files, just type

prxtool -f -n yourfile.xml EBOOT.BIN

which will give you a much more readable output :)

Keep this output somewhere!
If prxtool complains about your file not being a prx… you probably screwed up the decryption process at some point, or you used the wrong EBOOT.BIN (there are several of those in the ISO, most of them are dummies)

Compile a binary loader

For this step I suggest you download my patapon exploit SDK as it is a good example. This SDK can be found as a part of HBL, here.
The binary loader itself is pretty simple to write, directly in assembly. You could write it in C based on the SDK you will create (that I describe below), but my inspiration here is the sparta_sdk which has its binary loader written in mips assembly. Assembly is a bit tougher than C/C++, but in this case, we are simply going to adapt my patapon work to fit your game, so it’s only a few replacements here and there.

Note: this work is heavily inspired by Mattiaz’s “sparta sdk”, it is recommended that you download it too if you want to see the differences, which can be educational.

Open the file “loader.S” from the patapon binloader folder.

Basically what we want to replace here are the function addresses used by the patapon binLoader. I used addresses that made sense for Patapon, but not for your game. So for the 4 functions involved (sceIoOpen, sceIORead, sceIOClose, sceKernelDcacheInvalidateRange) , you will take your list of imports generated earlier, and replace the “patapon” value with the value for your game. In my example, I replaces 0x08A69854 (the value from the Gripshift exploit) with 0x08C88590 (the value from the patapon exploit) for sceIoClose, and so on.
Additionally, you need to update the address where the filename is stored. The filename is the name of your binary, and is traditionally ms0:/h.bin. You need to add this string in your savefile somewhere around your jump location, and inject this address into the asm code. In my example below, I put the file name “0xF0” bytes after my jump point, so I changed the 0xC0 from the sparta sdk into F0. Don’t forget to put a “0×00″ at the end of your string!! Note that this also means that the length of my compiled binary loader has to be less than F0 bytes, otherwise when I inject it I will overwrite my file name  :’(

That’s pretty much the only things you have to change. We’ll now compile this file and inject it in the SDDATA.BIN.
Compiling assembly is not especially difficult as all the tools for that are provided in the PSPSDK. Again, taking inspiration from the spartaSDK, here are the needed compilation commands:

psp-as loader.s
psp-objcopy -O binary a.out a.bin

The first step compiles the code, and the second step creates a binary version of it.

Inject the binary loader in your Savegame

Once you’ve got a compiled version of your binLoader, it is then easy to inject it into your save file, either manually with a copy paste, or through the scripting language of your choice. The only thing to remember is that you want to inject it at the precise location matching $ra, that you found above in this article.

For the injection, I have a small ruby script that takes a valid SDDATA.BIN file and loads the exploit + the compiled binary loader into it directly. I’m only providing a picture of the code but you can also find the code here. I suggest you go with the method you prefer (and the language you prefer) for injecting your code.

Note that at this step, you already have a way to create your hello world (in asm) and inject it instead of the binary loader. It’s actually not necessarily a bad idea to try much easier code (such as calling sceKernelExitGame) before aiming for a binary loader, to make sure your thing works.

Write a SDK

This sounds difficult. It’s not. Once you have your function imports, writing the SDK is a piece of cake. What you need to do here is take the list of function imports you retrieved through prxtool, and write a file named sdk.S just like the one in the patapon SDK. It’s pretty straight forward. In my example, I replaced 0x08A69854 with 0x08C88590 and so on, just like I did with the .S. This is assembly, but it’s dead simple, as you don’t even need to look for what the functions actually do.

In my case I wrote a simple ruby script that parses my prxtool “functions imports” file into a sdk.S  function, but there probably are some options in prxtool to help you with that task.
You then copy the sdk.h from the patapon exploit, and that’s it. One .S file and one .h, and you’re done!

Write a Hello world

Once you’ve got your sdk, writing a Hello World is extremely simple. Actually, you can even cheat and reuse the sample provided in the patapon SDK. Be sure to start with the smallest file possible, just to make sure your code actually works. A simple proof of concept is just some C code that calls “sceKernelExitGame()“. That’s enough for you to confirm that your SDK is roughly correct and that your binary Loader (coded previously) does its job. Additional samples can also be found in the sparta SDK.

The sparta_sdk makefiles might need a few changes since we are using a different way of writing the SDK(take inspiration from the Makefile from patapon rather than that from sparta in that case)
The output of the compilation should be a h.bin file. If your binary loader loads ms0:/h.bin, then just put that h.bin at the root of your memory stick, put your binaryLoader SDDATA.bin in the correct subfolder of the SAVEPLAIN directory, and fire up your game (with SGDeemer and psplink enabled of course), trigger the exploit…and your hello world should appear :)

TroubleShooting

There might be several reasons for your exploit to fail at that point. Although computer programming IS exact science, it’s extremely easy to do stupid mistakes if your working environment is not “good” enough. At that point, PSPLink is the key to your success. If you get a crash, try to investigate the Ram. Are you actually jumping to the correct location? Is your binary loader really at that location? Is it entirely there or did it get truncated for some weird reason? Add breakpoints to your binary loader to see if it actually runs. When you are sure the binary Loader runs, make sure it really loads your hello world where you expect it to be loaded in Ram. If so, add breakpoints to your hello world as well. Or simplify it.
All these steps can be extremely painful if you don’t automate some of your work. I highly recommend taking the time to write a few scripts that will automate the work for you (compile the binLoader and inject it, etc…). Choose whatever scripting language you’re confident with :)

Reencrypt your savegame

That’s the last step when you want to make sure your exploit works on official firmware. A tool called SED allows you to reencrypt your exploited savegame. To do that though, you need the savegame key. That key can be found in the SAVEPLAIN data given by SGDeemer. It’s at the very bottom of XXXX.bin where XXX is the code of your game . At the bottom of that file, the last 20 bytes should be only zeroes. you want the 16bytes before that. That’s your key :)
For the usage of SED… well, google for it, I’m feeling lazy. If you made it that far, SED shouldn’t be a problem for you :)

Wagic on Mac OS  

J, the incredible mad scientist behind the port of JGE (and therefore Wagic) for Linux is back and made awesome progress on a Mac port. We’re not entirely there yet, but this screenshot shows you that it’s coming :)

wagic_mac

J also fixed a bunch of issues for compilation on 64bits OSes, but some of his changes are not in the SVN yet. They should be there soon though. Thanks a lot Dude :)

JGE++ is the only library that allows you to compile a game for the Sony PSP, Windows and Linux from the same source code. The latest version of JGE is available on our SVN.

I have been working on Wagic for more than 2 years now, and it’s become quite big for a homebrew game. In terms of gameplay and features of course, but also in terms of source code. I use a small application named CLOC to count the number of lines of code in Wagic, and I was amazed at how the source code for Wagic keeps on getting bigger with time.

Wagic now has 70’000 lines of code.

As a comparison, a “standard” Custom Firmware for the PSP has around 25’000 lines of code.

Out of curiosity, I ran CLOC on several open source projects of the PSP community and here are the results:

Project Name Lines of Code 3rd gen Equiv.
Wagic 70’000 (including 40’000 for JGE) 85’000
DSON PSP (DS Emulator) 37’000 (including 28’000 from the PC emulator) 32’000
CFW 3.10 OE 23’000 21’000
Battlegrounds 3D 0.4 (tank game in 3D) 12’000 10’000
PSP Mancala (Mancala Game) 3’200 2’500

(3rd gen equiv. Is CLOC’s attempt at comparing projects written in various languages. It assumes for example that one line of assembly code does way less than one line of C, itself doing less than one line of C++)

What does this show? Well, pretty much nothing, except that the number of lines of code in a project are not directly related to its popularity :P

A “standard” homebrew game with basic features, that is still more than a “proof of concept” will have between 2’000 and 10’000 lines of C/C++ code (I didn’t try any LUA game).

A basic rule of thumb is that a programmer alone can maintain around 20’000 lines of code. The number of lines of code in a program give no specific indication on the quality of the code itself, but there are two obvious things: Maintenance and bugs increase with the number of lines of code. I’m not saying that Wagic has 15 times more bugs than Battleground 3, but that it is highly probable that Wagic has 2 times more bugs than when it had only 35’000 lines of code.

Well, if we want a project to grow bigger, add more features, at some point we have to increase the code size. What are the solutions to deal with it?

  1. Version control. I can’t imagine a project with more than 10’000 lines of code that is not version controlled. We use SVN for Wagic, there’s a free service provided by google code for that. So far it’s great. Other projects use their own SVN servers (such as the PSP SDK at ps2dev). Other solutions such as Git exist. I don’t think any software project can expect to grow without at least a basic version control system
  2. Bugs tracking. I used to write down every single bug on a piece of paper. This works half well when you’re alone on the project, assuming it’s always the same piece of paper. Let me withdraw that: it doesn’t work. You end up forgetting things. Inputting the bugs in a system that will help you keep records is a great idea. I’m happy we progressively shifted to that in the Wagic project. Google code offers this service as well. There are alternatives such as mantis,…
  3. Automated tests. I gave up the idea of having human beings test every single feature in Wagic after the second release I think. There is no way you can get people test thousands of cards in an acceptable amount of time. Depending on the project, automated testing can be hard to implement. Unit tests are fairly easy to implement in languages such as Java, but I haven’t taken the time yet to implement unit tests in Wagic. Wagic does regression tests, which is basically a way of making sure that a new feature does not break old ones. We have lots of progress to do in this area
  4. Reduce the quantity of source code. One other thing I haven’t taken the time to do seriously yet. The best solution to reduce maintenance is of course to reduce the size of stuff to maintain. Wagic has lots of dead code, or code that could be optimized/refactored. We try to do some cleanup regularly, for example by removing hardcoded cards, and instead softcode them (which also improves the parser, that’s a good thing). Often, cleaning up the card codes doesn’t reduce the total amount of code though. But it allows us to code 50 cards when it was only possible to code 1 initially.

These are the four things we use on a daily basis to work on Wagic, and that proved efficient over the months to increase the quality of the game. It is far from perfect of course. We are experimenting with other things (such as a wiki) to improve documentation and communication between devs…we’ll see how it goes.

The Latest SVN Revision of Wagic has 22’000 lines of code.

By comparison, the codebase was around 15’000 lines in November last year.

I don’t believe that code gets better when it grows. Actually, the number of bugs in a program is directly related to the number of lines of code.

Therefore I’m fighting to reduce the code while adding functionality. More than 5500 lines are currently dedicated to specific cards, and this could be dramatically reduced by improving the parser mechanism.

There are also a few areas with some ugly copy/pastes, and of course dead code here and there. The current version of Wagic could definitely fit in less than 20’000 lines if I did some good cleanup!

I’m using CLOC to count the lines of code in Wagic.

PS: I’m not counting the JGE++ library, which does most of the graphics work. JGE++ has around 40’000 lines of code according to CLOC.

The testing suite  

I’ve already talked about the testing suite here, but this thing saved me countless hours of bug chasing, so I think it deserves one more article.

As you may probably know by now, this year’s Magic the Gathering core set, called Magic 2010, comes with a bunch of rules changes, that we wanted to implement in Wagic. Some of these changes were easy to code, such as removing the manaburn effect, others were a real pain in the a## such as the new blockers ordering thing.

Overall the new rules took me a few weeks to implement. Last week I also reworked the parsing engine so that it gets a bit more generic and less painful to work with. The code in MTGAbility.cpp and in AllAbilities.h is still way too big though, as these two files together have around 6500 lines of code. These two files handle the cards specific code, so since Wagic handles more than 3000 cards, you can say that each card requires an average of 2 lines of C++ code. Of course there’s no such thing as an “average” card, but rather hardcoded cards (that can take up to 50 lines of code), and “soft” coded cards, that the parser can handle.

Anyways I digress. I was saying that I had to rewrite the parser entirely last week. To make it clear, I rewrote and reorganized something like 2000 lines of code in a few days. This represents around 10% of the entire Wagic code, and since it’s the code that handles the way the cards act in the game, it is a very important part of Wagic. If I hadn’t the test suite with me, this would mean a painful phase of intense play-testing: create decks with the cards I want to test, hope for the exact situation to happen, confirm the card works, change the code and try again if it doesn’t, etc…

Here’s an example: Imagine I change the code for the First Strike ability, for some reason. How do I test that I didn’t break it? Well, yes, I could create a deck with a bunch of black knights, start a game against the AI (or a 2 players game against myself), and hope that my opponent will put a */2 creature with no first strike into play, then cross fingers and hope my black knight kills it without getting killed himself. If it doesn’t work, I have to fix the code, and test again…each test takes between 2 and 5 minutes, so if I’m really not good, or if the problem’s tougher than I expected, it could easily take me one hour just to make sure first strike works correctly.
Of course, when I changed first strike, maybe I broke double strike as well… So I should test that as well, and so on…
Did I mention Wagic handles more than 3000 cards? Manual test became a nightmare when we reached 300 cards, so I couldn’t even imagine what it would be today…

Enters the testing suite. The Testing suite basically takes a bunch of test files, runs them, and checks if the tests pass or if they don’t.
A test file is a simple text file made of 3 parts: An initial State, a series of actions, and a final state.

INIT

When it loads a test file, the Testing Suite initializes the game with the data from the initial state. So for example:

[INIT]
FIRSTMAIN
[PLAYER1]
hand:Ancestor’s chosen
graveyard:swamp,grizzly bears,dragon engine
manapool:{5}{W}{W}
[PLAYER2]
graveyard:black knight

The above init state is loaded by the testing suite. The Game engine is “forced” to go to the “first main phase” of player 1. Player 1 has
{5}{W}{W} in his manapool, Ancestor’s chosen in his hand, and 3 cards in his graveyard. Player 2 has 1 card in his graveyard.
(Obviously what we want to test here is Ancestor’s chosen ability: When Ancestor’s Chosen comes into play, you gain 1 life for each card in your graveyard)
The obvious advantage is that I don’t have to create a specific deck with Ancestor’s chosen an playtest it in order to to test its ability.

DO

As I said in my other article, the testing suite is like a CPU player, except it is very dumb: Instead of computing the best moves for its cards, it just takes orders from the test file. This is the second part of the test file for Ancestor’s chosen:

[DO]
Ancestor’s chosen

It looks simple? Well it is. What I tell the “dumb AI player” here is: just click on the Ancestor’s chosen card.
And that’s it. Since player 1 has the mana in its mana pool, and it’s his first main phase, clicking on the ancestor’s chosen card will put it into play.

ASSERT

Now that we’ve done the actions needed in the test, what we want is to compare the new state of the Game, with what we expect to happen. We write what we expect to happen in the third part of the test file:

[ASSERT]
FIRSTMAIN
[PLAYER1]
inplay:Ancestor’s chosen
life:23
graveyard:swamp,grizzly bears,dragon engine
manapool:{0}
[PLAYER2]
graveyard:black knight
[END]

So, if you compare this to the initial state we described above, the differences are the following: Ancestor’s chosen is in play, player 1 has no more left mana in his manapool, and he has 23 life (initial life of player 1 wasn’t described in the initial state. In that case the game uses the default value of 20).
Now, this is what the test expects to happen. The testing suite will of course compare this to the actual state of the game. If the expected and the actual states are not equivalent, then the test fails and the program lets me know about the error.

Here is the full test file for ancestor’s chosen:

#Testing Ancestors chosen
[INIT]
FIRSTMAIN
[PLAYER1]
hand:Ancestor’s chosen
graveyard:swamp,grizzly bears,dragon engine
manapool:{5}{W}{W}
[PLAYER2]
graveyard:black knight
[DO]
Ancestor’s chosen
[ASSERT]
FIRSTMAIN
[PLAYER1]
inplay:Ancestor’s chosen
life:23
graveyard:swamp,grizzly bears,dragon engine
manapool:{0}
[PLAYER2]
graveyard:black knight
[END]

you can find the other test files in the SVN repository.

Writing a test takes a few minutes. It is then added to the list of tests, and whenever I change some code, I can make sure that no important card functionality is broken.
Of course, even with a lot of tests, we still test only a very low percentage of the game situations, but the Testing suite allows us to test around 300 cards in 5 minutes. Doing the same thing manually in the game would take a few days for a very dedicated tester!

Last week, I rewrote the parser methods (the piece of code that reads the “_cards.dat” files and converts a text such as “lord(merfolk +1/+1″ into  actual C++ code). The Testing suite helped me catch 59 bugs and 3 memory leaks. I wouldn’t even have considered rewriting the parser if I hadn’t had the testing suite with me!

It sometimes takes a while to get the correct implementation of a specific part for a piece of software. I’ve spent months thinking about Wagic without coding one single line in the hope that my design would be perfect from the start. That was, obviously, very pretentious. After a few months of thinking, I realized that if I didn’t start coding soon, the project would be dead before even being born. So I gave up on my “perfect design” idea and started coding dirty things.

Well the result is not so bad, but there are lots of awful flaws in the initial design of Wagic that need to be solved.

Take the phases of a game, for example. Until now, they were completely hardcoded and managed by the “GameObserver” an object that basically controls all the events of the game.

The main problem is that cards have no control on the GameObserver, and therefore have no control on the way phases work. And this matters because, due to this flaw, cards such as Stasis or Time Stop couldn’t be implemented in Wagic so far.

But I’ve recently had the idea to replace these hardcoded horrors with a nice object called the Phase Ring (Most of my ideas come at very impractical times, when I’m not in front of my computer. And I fight hard to remember them, sometimes writing them down on a conbini receipt because that’s the only thing close to a piece of paper available around me :D)

So basically the phases are now managed with an object that can be manipulated by the “abilities” in the game. It does not mean it’s become easy as pie to add these cards, but at least now it’s possible.

For the programmers out here… yeah, I know, I just replaced an array with a list… it doesn’t make it less cool :D

PS: I’ve spent an awful lot of time on the forums the past few weeks (reading and answering), but unfortunately this has had a bad result on my free time and on the time I actually spend coding. I’ll try to be less active over there, just for my own good, so don’t be offended if I don’t reply to all requests on the forum. I’m sure other members have the answers to most questions, after all that’s what communities are for :)

« Older entries