Angband Forums

Angband Forums (http://angband.oook.cz/forum/index.php)
-   Development (http://angband.oook.cz/forum/forumdisplay.php?f=10)
-   -   C#, Angband and Unity (http://angband.oook.cz/forum/showthread.php?t=7294)

Dean Anderson April 9, 2015 14:56

C#, Angband and Unity
 
I've been looking around and Googling, and I'm surprised at the lack of Angband ports to C# or Java or other languages. There's a half-finished C# port on Google Code, and I've found a few discussions about Java ports but nothing substantial.

So I'm currently working on an experimental C# port of the Cthangband code myself.

What I'm basically doing is this:

1) Take a snapshot of the Cthangband 5.0.0 code (I've done this - it's a simple copy/paste operation!).

2) Strip out all the horrible hacks that make it work on multiple platforms - all the #ifdef business - so we have a simpler codebase which contains only the code that's actually compiled into the Windows Console version. (I've done that, and it compiles and works as normal.)

3) Port the code from C to C#, as-is. (I'm in the process of doing this. Obviously I can't keep it completely as-is, since the file handling, memory management and console code are rather different in C# to C, but I've completed those "tricky" bits now.)

4) Once I have a working C# port, refactor the code to make it properly object oriented. (I'm trying to avoid any of this until I've got the basic port in #3 working, but I'm having to do little bits in order to get the terminal stuff to work - for example instead of using a load of function hooks, the 'term' functions are now in an abstract class which is inherited by the specific 'ConsoleTerm' class containing the Console implementation of a term.)

5) Once I have a fully object oriented C# version of Cthangband running, I want to swap out the Console display for something flashier. I was thinking that Unity would be good because it's nicely portable and handles C# code natively.

Carnivean April 9, 2015 15:25

Have you checked out the work that the Vanilla maintainers and the NPPAngband crew have been doing? They've taken different approaches to splitting the UI from the engine and refactoring the codebase to simplify thing, and in the case of NPP have a working GUI. You might find you only need to reinvent 1/2 the wheel.

Derakon April 9, 2015 15:49

"Rewrite Angband in a nicer language" is basically what you're talking about here. In addition to the 4.0 efforts, I also recommend you look at Pyrel, which is a Python-based rewrite with a radically different design aiming for high extensibility and flexibility. Unfortunately Pyrel's on indefinite hiatus since I'm busy with half a billion other things. Sorry, Magnate and d_m and everyone else who contributed to it. :(

Dean Anderson April 9, 2015 15:53

I haven't actually seen their Qt port in action, but I'm aware of it.

In my case, the thought process wasn't:

I want a new UI
=> Unity looks good
=> Unity needs C#
=> Therefore I need to port it to C# in order for it to work

The thought process was actually:

I've been a professional C# developer since the language came out and the company I work for switched to .NET
=> So why the **** am I messing around doing this in plain old C
=> I should totally convert Cthangband to C# for my own enjoyment/ease of use
=> Cool
=> ...
=> ...
=> Hmmm... I could probably do a graphical front end at some point. I wonder what 2D graphics engines are available for C#?
=> Unity looks good. And it's portable too!

So while the Qt front end sounds interesting, for me it's the C# port that's the important bit, not the addition of a front end. Also, the 3.x based code that the current dev team are working on is very different from the Zangband 2.1.1-based code that Cthangband is built on - so they're starting from a very different place. Just converting Cthangband to use the new Angband code would be a major task in itself, without doing any porting.

Dean Anderson April 9, 2015 15:59

Quote:

Originally Posted by Derakon (Post 100372)
"Rewrite Angband in a nicer language" is basically what you're talking about here. In addition to the 4.0 efforts, I also recommend you look at Pyrel, which is a Python-based rewrite with a radically different design aiming for high extensibility and flexibility. Unfortunately Pyrel's on indefinite hiatus since I'm busy with half a billion other things. Sorry, Magnate and d_m and everyone else who contributed to it. :(

Interesting - although I don't know Python from a hole in the ground!

Derakon April 9, 2015 18:00

Quote:

Originally Posted by Dean Anderson (Post 100374)
Interesting - although I don't know Python from a hole in the ground!

It has a fairly straightforward syntax, so it's not hard to learn once you wrap your head around the fact that whitespace is significant to the parser. Even if you don't want to work in Python, though, I think its design approach is worth imitating. I put a lot of thought into how to set up a system that could easily be extended without needing to know a lot about the code.

AnonymousHero April 12, 2015 00:02

The difficulty here isn't so much that it's difficult per se. It's just that it's an enormous amount of sustained effort to convert, say, 200k-300k lines of old-school C to a modern language. Many of the idioms of C aren't even available in $LANGUAGE. (Java is particularly terrible here since it has signed bytes. SIGNED bytes! Wtf?)

These things can be overcome with enough effort, but I think the temptation to start changing things as you're porting has claimed many victims...

I sincerely wish you the best of luck, and AFAIK you'll be the first to succeed, if you do succeed.

EDIT: FWIW, I do think your general approach is sensible: Strip down to the bare minimums before starting to port, resist at all cost the temptation to redesign while porting, refactor post-porting -- hopefully in a friendlier language! :)

Dean Anderson April 13, 2015 11:41

Quote:

Originally Posted by AnonymousHero (Post 100413)
The difficulty here isn't so much that it's difficult per se. It's just that it's an enormous amount of sustained effort to convert, say, 200k-300k lines of old-school C to a modern language. Many of the idioms of C aren't even available in $LANGUAGE. (Java is particularly terrible here since it has signed bytes. SIGNED bytes! Wtf?)

Most of the code isn't a big problem when conmverting from C to C#, since the basic syntax is the same.

The main differences are that most of the stuff in Angband that throws around pointers to structs needs to be changed to throw around references to classes instead.

Unfortunately, the bits that require the most alteration are:

1) The terminal/display code (main-xxx.c/Z-term.c)
2) The code for reading/writing files (init1.c/init2.c/files/c)
3) The code for initialising the arrays of structs/objects (variable.c/tables.c)

The reason this is unfortunate is that these are the bits that needed converting first. Converting the LOS code or the code for when someone goes up a level can wait until we're at a point where we need those functions - in other words they can wait until we've got a skeleton program running and starting a game. But those basics need to be run as the program initialises and sets itself up.

My current progress is that I've fully converted the files listed above, and I'm now at the point where I can compile and run the program and it will do all its initialising and be ready to jump into character creation.

Quote:

These things can be overcome with enough effort, but I think the temptation to start changing things as you're porting has claimed many victims...
I think the biggest issue there is that because some things have to be changed, it's tempting to change more "while I'm here".

For example, tables.c needed heavily re-writing because you don't initialise big arrays of objects in C# the same way that you do in C. So as well as simply converting the various structs to classes, I also had to write constructors for them so that I could use those constructors to create the big arrays.

It would be very tempting to start refactoring other bits of the program as I get to them by trying to convert static functions into class methods on those classes.

Quote:

I sincerely wish you the best of luck, and AFAIK you'll be the first to succeed, if you do succeed.
That's a good incentive!

Nick April 13, 2015 14:18

I'm reading this thread with the way we've updated the Vanilla codebase in mind. I don't think there is really anywhere we can really help each other - Angband and Cthangband are really too far apart now - but it's very interesting to see the changes you're making and your reasons for them.

The V changes I'm hoping will be useful for future variants...

the Invisible Stalker April 14, 2015 12:27

Quote:

Originally Posted by AnonymousHero (Post 100413)
(Java is particularly terrible here since it has signed bytes. SIGNED bytes! Wtf?)

Just be glad it doesn't have signed bits.

Derakon April 14, 2015 15:43

Quote:

Originally Posted by the Invisible Stalker (Post 100507)
Just be glad it doesn't have signed bits.

I guess then your options are +0 and -0.

AnonymousHero April 15, 2015 01:40

Quote:

Originally Posted by the Invisible Stalker (Post 100507)
Just be glad it doesn't have signed bits.

Well, let's see what they have to say about the inevitable trail of bodies... BITS! I MEAN BITS!.

Dean Anderson April 17, 2015 10:38

I've been making slow but steady progress, and while I've not found anything near a showstopper, here are my top seven (it was going to be a top five but I thought of a couple more) irritations when porting Cthangband from C to C#...
  1. C# is very finicky about casting. Much more so than C, where you can interchange pretty freely between numbers of different types (byte, int16, int32, signed and unsigned). Where C will generally simply let you copy a value from one type to another and not care if it doesn't fit - you'll just get roll-overs and possibly unexpected results - C# insists on explicit casting. All the time. Not a problem when you're writing new C# code, but a pain when porting old C code.
  2. C doesn't do strings at all well. You have to fiddle around with arrays of characters, pointers, and null terminators. None of that is necessary in C# but again it's very irritating to convert because in C# you don't (explicitly) use pointers (you do actually use them all the time when using reference types, but they're implicit rather than explicit).
  3. C lets you do if statements on numbers, for example if i is an int, you can write "if(i)" and the compiler will understand that you mean "if(i != 0)". C# will throw a compiler error because if statements need to look at a bool rather than an int. So you have to manually add the explicit comparison to zero on each one.
  4. Speaking of bools, C# is very strict that a bool is not a number and a number is not a bool and never the twain shall meet. C doesn't really care, and the C in Angband uses its own preprocessor definitions of TRUE and FALSE which have nothing to do with C#'s true and false.
  5. Did I mention preprocessor definitions? In C# these are all boolean. Either a term is #defined or it isn't. You can't #define a term as a value and then use the term as a macro for that value elsewhere in your code. Well, you can for value types, but the syntax is different. They're declared as variables but with the "const" decorator. You can't do things like "#define SUM(A,B) (A) + (B)" though.
  6. In C#, everything is in an encapsulated unit of code. The majority of the time these are classes but you also have enumerations, interfaces and structs (although the latter are mostly there for API compatibility with C and there's no reason to ever use them in native C# code). There's no such thing as a global scope for variables or functions, they have to be declared inside a class or struct. The closest you can get to a global variable is to give a class a static property or even make the whole class static. In C, of course, every function and variable is global. So that variable in the C code that is declared as being external and then used 269 times in the file as if it were local? Every single one of those 269 times need to be prefixed with the name of the class in which you've declared it in C#.
  7. In C you declare that a variable is an array by decorating the variable's name. In C# you do it by decorating its type. For example in C you have "int x[] = {0, 1, 2, 3, 4}". The equivalent code in C# is "int[] x = new int[] {0, 1, 2, 3, 4}". Similarly where C has "int x[5]", C# has "int[] x = new int[5]". These are functionally identical for the most part, but it's a pain having to change each declaration.

And of course, the thing that's really making me twitch is the fact that I know I'm going to be rewriting most of this code in the next stage of the process where I refactor everything. All the initialisation code I've ported? It's going to be in constructors, not static initialisation functions. All the global stuff? It's going to be in a nice object model, not just scattered around a whole bunch of static classes. All the loading and saving code that reads and writes files byte by byte and has to handle every global variable independently? It's going to just serialize and deserialize the object tree in a half dozen lines of code.

But I have to port it as-is first and get everything working before I start doing the refactoring. Trying to port and refactor the code in a single pass will just end up in a mess.

the Invisible Stalker April 17, 2015 15:15

It's interesting for me, as someone with no C# experience, to see the list of differences. On the whole I prefer C's design choices, but not in all cases. I'd guess that in the course of dealing with point 1 on your list you'll find some bugs that have gone unnoticed for years because the Intel architecture is so ubiquitous. It's easy to get used to assuming things about integer sizes which aren't mandated by the standard.

Dean Anderson April 17, 2015 16:10

They're not the only differences between the languages, of course; but they're the things that are fiddly to convert.

In terms of preference, it's generally a matter of horses for courses. C# is similar to Java in that its designed around you having a virtual machine in which security and safety are paramount. That's why it has so much more emphasis on casting things properly. You can't just throw around pointers and treat any bit of memory as any type of data. Many of the design choices are based around that, including the use of garbage collection based memory management.

Personally, I prefer C# to C but that's mostly because I've been using C# every day at work for over a decade, and other than Angband I've not used C in that time. So these days I'm far more familiar with the way C# works than the way C works. Plain C certainly has its advantages when it comes to places where you need to be close to the hardware. And it gives you fine detailed control over memory and the like. But those advantages can all to often become disadvantages at times when you don't need that closeness or control - at which point you tend to still need the detail and that can get really fiddly. And for OO code I definitely prefer C# or Java - both languages designed to be OO from the ground up - than C++, which has always struck me as a bit of a kludge.

AnonymousHero April 17, 2015 17:18

Quote:

Originally Posted by Dean Anderson (Post 100632)
{#5:} Did I mention preprocessor definitions? In C# these are all boolean. Either a term is #defined or it isn't. You can't #define a term as a value and then use the term as a macro for that value elsewhere in your code. Well, you can for value types, but the syntax is different. They're declared as variables but with the "const" decorator. You can't do things like "#define SUM(A,B) (A) + (B)" though.

This is one of those things where I've just gone through all the C code and changed the macros to functions (well, almost all). It's not like anybody's going to notice any performance difference these days.

Quote:

Originally Posted by Dean Anderson (Post 100632)
{#6}: In C#, everything is in an encapsulated unit of code. The majority of the time these are classes but you also have enumerations, interfaces and structs (although the latter are mostly there for API compatibility with C and there's no reason to ever use them in native C# code). There's no such thing as a global scope for variables or functions{..} Every single one of those 269 times need to be prefixed with the name of the class in which you've declared it in C#.

I actually wondered at one point whether one should just copy everything into a single class (aside from pure struct declarations, etc., obviously), get that working and then just start using IDE refactorings to split things out.

It'd be a monster file (~140SLOC for T2.x on current master), but it really would be "just" a matter of going through from one end to the other. (Of course now you have to deal with clashing compilation-unit local statics, but I bet that's a smaller problem.)

Quote:

Originally Posted by Dean Anderson (Post 100632)
But I have to port it as-is first and get everything working before I start doing the refactoring. Trying to port and refactor the code in a single pass will just end up in a mess.

Agreed. I might consider a similar port in future, but I think I'm going to go a slightly different way and to get rid of some of the absolutely ridiculous amount of code (near-)duplication (using C++ and possibly a little bit of templates) before even attempting it.

AnonymousHero April 17, 2015 19:58

Re: Duplicate code: Case in point: just removed 715 (EDIT: 754) lines of redundant code with completely trivial template use :)

ekolis April 17, 2015 21:03

C# 5.0 will support static imports, much like Java, so instead of saying

import System;
...
Console.WriteLine("hello world");
Console.WriteLine("isn't it annoying to have to type Console all the time?");

you can say

import System.Console;
...
WriteLine("hello world");
WriteLine("yay I don't have to type Console anymore :)");

Dean Anderson April 17, 2015 22:14

Quote:

Originally Posted by AnonymousHero (Post 100654)
I actually wondered at one point whether one should just copy everything into a single class (aside from pure struct declarations, etc., obviously), get that working and then just start using IDE refactorings to split things out.

It'd be a monster file (~140SLOC for T2.x on current master), but it really would be "just" a matter of going through from one end to the other. (Of course now you have to deal with clashing compilation-unit local statics, but I bet that's a smaller problem.)

I should have thought of that. In C# it wouldn't even need to all be in a single monster file because you can use the "partial" keyword when defining a class to indicate that it's split between files.

Dean Anderson April 20, 2015 22:23

Interesting things I learned today #1: The Angband code still contains "goto" statements!

Interesting things I learned today #2: "goto" statements will compile under C# without error.

Those things are so coming out when I do the refactoring!

Derakon April 20, 2015 23:08

GOTO isn't innately bad. In particular, it's basically the only clean way I'm aware of to do "exception handling" in pure C. That doesn't mean that C# doesn't have a better construct to do whatever Angband's GOTOs are doing, of course.

Dean Anderson October 17, 2017 14:06

So, it's two (and a bit) years later. What's the current situation?

Well, as you might have guessed from the fact that I stopped posting on the thread, I gave up. I'd been approaching the project in the wrong way and it wasn't really progressing.

However, a couple of months ago - in the Summer - I decided to revisit Cthangband again. I started by making some changes to the program - I've got a Cthangband 5.1.0 sitting on my hard drive now with various fixes and enhancements. But then I decided to have another go at porting it to C#.

I approached it from a different angle this time. This time my method was:
  1. Remove all the redundant code and code for other systems (I'd already done this in Cthangband 5.1.0).
  2. Wrap all the code so that it is all in one big stonking class spanning 51 files (using C#'s ability to declare "partial" classes that span files).
  3. Comment out all the C code.
  4. Go through the code a function at a time replacing each commented out C function with a C# equivalent.
  5. Only after getting everything converted and working would I then start refactoring things to make it object oriented.

This might look similar to the prior attempt, but the significant difference is in #2. Previously I'd made each .c file into a static class while doing the conversion (so object1.c would be a class called Object1 and cmd4.c would be a class called Cmd4, and so forth). This meant that everything was static and I needed to reference between the classes constantly.

This time, by making it all one single class, I could just create an instance of the class and all the functions and variables could see each other just like in the C version of the code.

Similarly, in #4, I haven't been going through it file by file. Instead I've been going though it commenting out calls to functions that haven't yet been re-written and then slowly putting them back in in the right order to always have a runnable program.

So I started out with a program that would compile and run. Then one that would show the splash screen. Then one that would show the splash screen and let you start a new game. Then one that would also take you through character generation. Then one that would create a town. Then one that would have monsters. Then one that had objects. Then one in which the monsters also moved. And so forth.

And that made all the difference in the world. I now have a fully working and fully featured C# version of Cthangband 5.1.0 as well as a C version. There's no remaining un-ported code left.

So I've finished #4 and I'm currently most of the way through step #5 - refactoring everything to make it object oriented. Of the 51 original source files that I converted, I've moved all the code out of 38 of them and partially out of the remaining 13; moving the code into a proper object model and doing further re-writes as I go.

I haven't gone with Unity or anything as a front end for the moment. It's effectively just a Console app (I say "effectively", because technically it doesn't actually use the console window - they're horribly slow to update, so it uses a custom window that looks like a console but is actually just user-drawn graphics).

Pete Mack October 17, 2017 19:08

Quote:

Originally Posted by Derakon (Post 100804)
GOTO isn't innately bad. In particular, it's basically the only clean way I'm aware of to do "exception handling" in pure C. That doesn't mean that C# doesn't have a better construct to do whatever Angband's GOTOs are doing, of course.

There is another way, but it's not a lot better than GOTO:
Code:

do {
    ...
    return;
} while (false);
Handle exception here


kandrc October 17, 2017 22:56

Quote:

Originally Posted by Pete Mack (Post 125013)
There is another way, but it's not a lot better than GOTO:
Code:

do {
    ...
    return;
} while (false);
Handle exception here


By "not a lot better", I think you mean "a lot worse". This solution doesn't handle the fall-through, reverse-ordered de-init that is (sometimes, often) necessary. Goto gets you that for free. I (and most old systems programmers like myself) would argue that this is not only an "acceptable" or "not bad" use of goto, but that it's actually good and elegant. It's certainly more elegant than exception handling in C++ or other "modern" languages unwinding my stack (yes, I know that many will argue with this claim, but not old systems programmers).

ITT: "Fixing" things that aren't broken.

Pete Mack October 17, 2017 23:22

Oh I agree that GOTO is generally preferred to "do...while false."
That said, long jump GOTO really is horrible; I have to disagree on this one.

Dean Anderson October 18, 2017 00:00

Of course, C# has the classic:

Code:

try
{
    code here
}
catch (Exception e)
{
    exception code for handling exceptions here
}
finally
{
    clean-up code here
}

In the above, the catch can handle some types of exception or all exceptions (and you can have more than one catch block for different types of exception), and any that are not caught will be propagated back to the code that called the function - which is what will also happen if you don't put a try/catch block in at all and an exception happens.

The code in the finally block is guaranteed to execute before the function returns regardless of whether or not there is an exception and regardless of whether or not it is handled.

Having said that, I've not got any exception handling code in there yet. At the moment, I want exceptions to be unhandled and to be caught by the debugger so that I can see what's going wrong.

Gwarl October 18, 2017 01:22

Does this mean real consoles are no longer supported?

Pete Mack October 18, 2017 03:43

@Dean:
Yes, I agree C# is a nice language, tho I think it's biggest contributions are ad hoc functional programming (LINQ) and a cleaner implementation of attributes than Java has. The task library is very well done, too. MSR deserves real kudos for these contributions.

Dean Anderson October 18, 2017 17:18

Quote:

Originally Posted by Gwarl (Post 125028)
Does this mean real consoles are no longer supported?

Hmm... I'm not quite sure what you mean here.

The current architecture is that it's a Windows desktop application. The main window uses WPF to display its contents.

That's not ideal (I'm not a big fan of WPF) but - at least initially - I wanted something that would work "out of the box" rather than something that relied on third party libraries and complex install sequences.

That left me with three options:

1) Console
2) WinForms
3) WPF

I experimented with making it a pure console application, but the C# console implementation isn't all that quick, and things like wiping the screen and displaying a page full of text are noticeably slow. It's also restricted in colour usage to a pre-defined set of sixteen colours, and that set doesn't match the default Angband colours, so you have to put up with some merging and

So that left me options 2 and 3. Given that WPF takes advantage of hardware acceleration, and WinForms doesn't, I went for that. It has the advantage that you can easily change the font and window size on the fly without needing a re-start, and all the text takes advantage of ClearType font rendering so it looks nice and crisp regardless of resolution.

However, I do say that this is the current front end. I've deliberately abstracted the front end so I can switch to a different technology or library very easily.

Pete Mack October 18, 2017 18:30

Quote:

Originally Posted by Dean Anderson (Post 125043)

So that left me options 2 and 3. Given that WPF takes advantage of hardware acceleration, and WinForms doesn't, I went for that. It has the advantage that you can easily change the font and window size on the fly without needing a re-start, and all the text takes advantage of ClearType font rendering so it looks nice and crisp regardless of resolution.

However, I do say that this is the current front end. I've deliberately abstracted the front end so I can switch to a different technology or library very easily.

This sounds great. Too bad it's so difficult to do the same thing with Angband, because of the C++mangling in WPF. The benefit of Win32 is that at least it's compatible with gcc/mingw. Installing MSVS is a big, big deal: rather too much for the casual angband hacker. Your effort is pretty impressive in any case. Hand porting from onr language to another is non-trivial.

Mars October 19, 2017 00:33

Quote:

Originally Posted by Dean Anderson (Post 125001)
So, it's two (and a bit) years later. What's the current situation?

[snip]

And that made all the difference in the world. I now have a fully working and fully featured C# version of Cthangband 5.1.0 as well as a C version. There's no remaining un-ported code left.

Wow! Color me impressed :D!

IMO, things like this are quickly underestimated. Probably why these attempts usually die an early death most of the time.

jupiter999 October 19, 2017 02:10

I wonder if you still go for Unity or not?
I once worked on my roguelike using Unity, but shortly can't tolerate the fact that the game generated by Unity needs to spend quite some CPU resources even just idle :rolleyes: Sorry...

Gwarl October 19, 2017 15:59

Quote:

Originally Posted by Dean Anderson (Post 125043)
Hmm... I'm not quite sure what you mean here.

The current architecture is that it's a Windows desktop application. The main window uses WPF to display its contents.

That's not ideal (I'm not a big fan of WPF) but - at least initially - I wanted something that would work "out of the box" rather than something that relied on third party libraries and complex install sequences.

That left me with three options:

1) Console
2) WinForms
3) WPF

I experimented with making it a pure console application, but the C# console implementation isn't all that quick, and things like wiping the screen and displaying a page full of text are noticeably slow. It's also restricted in colour usage to a pre-defined set of sixteen colours, and that set doesn't match the default Angband colours, so you have to put up with some merging and

So that left me options 2 and 3. Given that WPF takes advantage of hardware acceleration, and WinForms doesn't, I went for that. It has the advantage that you can easily change the font and window size on the fly without needing a re-start, and all the text takes advantage of ClearType font rendering so it looks nice and crisp regardless of resolution.

However, I do say that this is the current front end. I've deliberately abstracted the front end so I can switch to a different technology or library very easily.

There's a cthangband shaped hole at http://angband.live/ - I compile a lot of variants and I can't remember exactly what my guess was as to why I couldn't get cthangband working, but in most cases for Z variants it's related to a bug in Z where gcc wouldn't recognise a 64bit compiler flag and the RNG got a wrong number of bits. The fact it's in active development again motivates me to give it another try, but it needs to run in a terminal and to compile without the aid of microsoft.

Where's the download for the C version of 5.1.0?

Pete Mack October 19, 2017 19:06

@Gwarl--if you're really ambitious, you can install Mono and use the C# port. But it really is ambitious: if the port is pure C#, it won't work with your Term_xxx port, so it'd need some bridge code. MS did a good job with C#. It's a better language than Java. But I don't know how much of the latest language features Mono has implemented (Tasks and LINQ are the big ones.)

Dean Anderson October 24, 2017 13:38

Quote:

Originally Posted by jupiter999 (Post 125060)
I wonder if you still go for Unity or not?
I once worked on my roguelike using Unity, but shortly can't tolerate the fact that the game generated by Unity needs to spend quite some CPU resources even just idle :rolleyes: Sorry...

I really don't know, to be honest.

I might give it a go, but I want to get the code cleaner and more stable first.

Dean Anderson October 24, 2017 14:04

Quote:

Originally Posted by Gwarl (Post 125089)
There's a cthangband shaped hole at http://angband.live/ - I compile a lot of variants and I can't remember exactly what my guess was as to why I couldn't get cthangband working, but in most cases for Z variants it's related to a bug in Z where gcc wouldn't recognise a 64bit compiler flag and the RNG got a wrong number of bits. The fact it's in active development again motivates me to give it another try, but it needs to run in a terminal and to compile without the aid of microsoft.

Where's the download for the C version of 5.1.0?

There isn't one, at the moment.

To be honest, it might be more effort than it's worth anyway. As part of the 5.1.0 changes, I stripped out much of the non-Windows code in preparation for the C# port.

So although there is a C version of 5.1.0 on my hard drive, it's explicitly coded for Windows; compiling inside VS2017 and incorporating a built-in front end.

To explain the front end: As part of my attempt to drag it kicking and screaming into the twenty first century, I've got rid of the whole thing where you load a character by starting the program with a command line argument and then when you die or save-and-quit the program dumps you back to the desktop. Instead, when you start the game you get to the main menu which includes multiple save slots like most modern games, and a character dying (or you doing a save-and-quit) brings you back to that menu rather than exiting the program.

It makes the game much more user-friendly, but probably means there's very little compatibility with what you're doing. Since it's been nearly 20 years (give it another six months or so) since Cthangband was forked from
Zangband, I figured that trying to maintain any sort of compatibility with other Angband versions was a fool's errand, and I may as well make wholesale changes.

That's in both the C and C# versions, and both of them automatically put your save games in the Windows user folders.

I should put the game online somewhere for people to download, though. I was thinking of waiting till I'd finished the refactoring (because some of it might break save files, which could be frustrating) but if I sort myself out a website for it I can stick it up as a beta.

johnretroreload February 5, 2018 18:52

Unreal VR angband?
 
I always wanted a VR angband style SAO game with 100 floors of a tower like Druaga with portable items between the two games. Past vs future kind of thing.
I did just find this on neverwinter 1
ftp://neverwintervault.org/rolovault...754/index.html

PowerWyrm February 6, 2018 09:42

Quote:

Originally Posted by johnretroreload (Post 127820)
I always wanted a VR angband style SAO game with 100 floors of a tower like Druaga with portable items between the two games. Past vs future kind of thing.
I did just find this on neverwinter 1
ftp://neverwintervault.org/rolovault...754/index.html

If you can plug your VR device on your PC, try PWMAngband with NotEye in FPP mode :)

Dean Anderson August 14, 2018 00:32

So, it's been a few months but I'm back again.

And this time I have a link!

Link

Basically, I finished the intitial pass at re-factoring the code; and although it worked and was a fully functional version of the game, it wasn't very "nice" C#. It had a small number of classes and most of them were thousands and thousands of lines of code. The fifty thousand lines of C# code worked but weren't very well organised.

So since then, I've been alternately playing it and doing more re-factoring to make it nicer and cleaner.

A classic example is the spell code. In the original C, that code consisted of a global array containing the level/cost/failure/xp for each spell for each class (and Cthangband inherited Zangband's spell system, so there are far more spells than there are in Vanilla - as well as many more classes - so that array was *huge*) and another global array containing their names. Then, there are a few global uints that contain bit-flags for which spells are known, forgotten, and have been cast at least once. Finally, the code for casting spells is a huge switch statement with 256 cases for the 256 spells, and the code for displaying spell info is also a huge switch statement with 256 cases for the 256 spells.

While all this works, having the spell data separated in this way is really awkward to maintain. Everything is done by look-ups, offsets, and indices; and cross-referencing a spell to see how much mana it should cost requires going through the tables by hand counting off the spell numbers.

The first re-factoring pass that I did converting this from C to C# basically left the structure unchanged. The spell names and spell numeric info were still global data (although since C# doesn't really have the concept of global data they were technically static members of a "GlobalData" class). The bit flags were still there, albeit now represented by a "FlagSet" class that wrapped the underlying uint with some user-friendly functions (and no longer global - the FlagSet objects for them were properties of the "Player" class). The humongous switch statements were still there too, although one of them was now contained in a function within the "Player" class and the other was now contained in a function within the "CastingHandler" class.

Again, it still worked - and it was C# code rather than C code - but it was still a mess.

So the second re-factoring pass that I've been doing (and which is still ongoing, although this particular part is finished) has cleaned it up.

Now there's a "Spell" class that holds the basic information for a spell, and there is a class for each individual spell that inherits from "Spell" (for example "SorcerySpellZap" and "NatureSpellHerbalHealing"). The data for the name of each spell is held in the class, and when it's initialised it's passed a reference to the player so that it can set its own level/mana/fail/exp values based on the player's character class. The spell info code is no longer in a switch statement but in a function within the "Spell" class (that uses a property that each inherited class overrides), and similarly the code for casting each spell is held in an abstract function within the "Spell" class that each individual spell class overrides. The flag sets are now individual flags belonging to the "Spell" class, so each individual spell inherits them.

When a character is created, there's a function that simply fills their spell book (an array of "Spell" objects - which, thanks to polymorphism, can therefore hold any object derived from "Spell" too) with a copy of each of the relevant spell objects.

So all the info for any individual spell is contained in a nice logical unit. Adjusting a spell or swapping one out for something different can be done trivially, without having to search through huge tables of numbers to find the right values to tweak. Displaying the spell info just requires a simple look through the spell book asking each spell in it to display its own info, and casting a spell just requires a simple call to the spell to cast itself.

So that's the sort of thing that I'm doing now. I'm breaking up the overly large and linear classes that I got in the first pass into more logical and usable smaller classes, and actually developing a proper object model as I go.

I'm in no way finished (although I've done most of the really big ones), but I am far enough through that I'm happy to release a new version to the world, especially since the end of July was Cthangband's 20th birthday (I released the first version - Cthangband 2.1.0 - back in July 1998).

So here it is. The link above is for the installer for Cthangband 6.0. It's fully C#, and should run on any Windows machine running Vista or more recent - although if you're running on Windows Vista to Windows 8 you might not already have the .NET Framework 4.5.2 installed, in which case you can download that directly from Microsoft here).

Pete Mack August 14, 2018 20:00

Wait, angband no longer has a giant case statement for spellcasting. It does call by name (m9re ore less) using generated code from a .h file. You must be running off an older codebase.

AnonymousHero August 14, 2018 20:40

Quote:

Originally Posted by Pete Mack (Post 132865)
Wait, angband no longer has a giant case statement for spellcasting. It does call by name (m9re ore less) using generated code from a .h file. You must be running off an older codebase.

Read the OP.

(This is a really impressive amount of work, btw.)

Dean Anderson August 14, 2018 22:10

Quote:

Originally Posted by Pete Mack (Post 132865)
Wait, angband no longer has a giant case statement for spellcasting. It does call by name (m9re ore less) using generated code from a .h file. You must be running off an older codebase.

Cthangband was originally based on the Zangband 2.1.0 codebase. Both it and Angband have drifted quite far apart over the last two decades (although the big switch statement is something that is left over from the Zangband code, not something new).


All times are GMT +1. The time now is 04:29.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2019, vBulletin Solutions Inc.