Bay 12 Games Forum
Other Projects => Curses => Topic started by: misterTwister on June 01, 2018, 09:47:08 pm
-
Hi,
I am aware that there once was an android port (a few years ago?) for LCS. I'm pretty sure it was abandoned and buggy. Now, the Google Play page is completely nonexistent.
Is there any chance of the creation of an updated android port? I would be happy to embark on this endeavor myself, but my programming experience is mostly limited to C++, visual studio, and a tiny bit of Java and the android developer IDE. I would be glad to learn more, though.
Could anybody with actual knowledge give some advice to make this dream a reality?
-
I'd be willing to help, but I have no experience with making Android apps and my programming knowledge is insufficient. Maybe get into contact with the creator of the Android port for help?
-
I've thought about it, and I think the most viable idea is to actually port the concept into some existing engine that supports Android, e.g. Game Maker or Unity. This is one reason I was supportive of IsaacG's efforts to move more of the stuff into data files. A ported engine can then just support the data files, so that it can be updated more easily along with the PC game.
It's fairly likely the old Android port won't actually be that much help in making a new, updatable one.
I've built stuff in Unity for Android before and I'd be willing to have a stab at getting something running, however the current project structure is an absolute mess and would be just horrible to replicate in C#. There are way too many hard-coded object hierarchies and virtual functions and hard-coded strings in the whole object/weapon/armor system. Just that one sub-system is a goddamn nightmare from any rational design perspective.
So yeah, I would not be willing to port any of it unless it was in the context of completely redesigning the inventory system and god know's what else needs fixing. Reconstructing the current horrible design of the inventory system into a different language would just suck, then you'd be locked into supporting that, which is a no-go. Fix it first, then port it.
There are also interface issues with properly supporting the thing on something like a smartphone. Currently, you have keyboard letters and those allow you to do different things. Pulling up a keyboard on a smartphone for something like that is a deal-breaker in terms of interface design, but you really don't want to redesign the whole thing for phones. So it would make sense to regularize the interface of LCS on PC to have highlight letters to show what's clickable at all times. This would have the side-benefit of ensuring that the entire game was playable with mouse. Then, the android port would make the highlight letters into touch-screen buttons, unifying the interface for both classes of device, rather than having a completely re-envisioned interface such as the current Android version had. So, make the game entirely mouse-playable (by having clickable highlighted letters on screen), then a touch-screen port makes a lot more sense and can be ported in a 1:1 interface fashion.
-
I do have a bit of experience with Unity and c#, but overall, my object oriented program design is preeeetty awful. Basically, we would have to rewrite the entire structure from ground up again, right? Perhaps you could help with design, idk. This also includes having the current unofficial maintainers of the game to migrate to using the unity engine, which I'm not sure what their reaction would be.
Another point is that if we wanted to publish it to the play store, don't you think it would have to be more... kid friendly? Im not sure of the exact regulations google puts on published apps, but satarizing certain acts of terrorism, (which the game surely does) is almost certainly a no-go.
Ill make a github project some time today...
-
It can be just a .pkg file and could be hosted on itch.io or somewhere like that. Most people who want to play it would probably be ok with self-install.
However, Google doesn't really require you to be "kid friendly". Especially since the game will be 100% text, nobody is going to care. The previous version was on the Play store, nobody cared. It's way too fringe to get that sort of attention.
If you start anything, definitely coordinate it with IssacG rather than going at it alone.
-
It can be just a .pkg file and could be hosted on itch.io or somewhere like that. Most people who want to play it would probably be ok with self-install.
However, Google doesn't really require you to be "kid friendly". Especially since the game will be 100% text, nobody is going to care. The previous version was on the Play store, nobody cared. It's way too fringe to get that sort of attention.
If you start anything, definitely coordinate it with IssacG rather than going at it alone.
I agree with what Reelya said.
-
Alright, I created a repository:
https://github.com/vijashu/Liberal-Crime-Squad-Unity
Give me your names to add to the collaborators.
-
Alright, I created a repository:
https://github.com/vijashu/Liberal-Crime-Squad-Unity
Give me your names to add to the collaborators.
https://github.com/King-Drake
You've already done more than the last one who wanted to collaborate. Though not yet as much as the one before that one.
I guess it's time for me to download Unity.
Interesting choice, Unity is mostly for 3-D games (that's part of why 2-D games in Unity tend to be so buggy, though a full explanation involves a detailed analysis of the evolution of software design especially compiler optimization, and the pro/con arguments of barrier to entry with its implications on the philosophical and practical basis for Sturgeon's Law).
-
In this case we wouldn't be making a full 2D Unity game, but creating a text canvas mimicking the curses screen then using an interface to write to than from our main code. There would be a "layer" of Unity related stuff but it would be a lot thinner than a typical Unity game.
A typical Unity game has many GameObjects which represent things in your game such as sprites and screen objects, and each one has a script attached. We wouldn't be doing something like making a GameObject for each character or anything like that since that wouldn't make sense. LCS characters are not Sprites.
What I think the best way to start would be a minimal demo here. Get a text canvas working which can do just the character-creation section of the game. Make the whole game as a single Unity scene.
In fact, a raw C# port would probably be much less painful that trying to make the game directly in Unity. Port the whole game to C# then port that into Unity, replacing the console module with a Unity canvas. Hell, if we could make it so that there's a version playable in a web browser, that would be even better.
-
In this case we wouldn't be making a full 2D Unity game, but creating a text canvas mimicking the curses screen then using an interface to write to than from our main code. There would be a "layer" of Unity related stuff but it would be a lot thinner than a typical Unity game.
A typical Unity game has many GameObjects which represent things in your game such as sprites and screen objects, and each one has a script attached. We wouldn't be doing something like making a GameObject for each character or anything like that since that wouldn't make sense. LCS characters are not Sprites.
What I think the best way to start would be a minimal demo here. Get a text canvas working which can do just the character-creation section of the game. Make the whole game as a single Unity scene.
In fact, a raw C# port would probably be much less painful that trying to make the game directly in Unity. Port the whole game to C# then port that into Unity, replacing the console module with a Unity canvas. Hell, if we could make it so that there's a version playable in a web browser, that would be even better.
One of the earlier changes I made was the creation of cursesAlternative.cpp
It's a wrapper for the pdcurses library, meant to help facilitate the move away from pdcurses entirely.
If the header file cursesAlternative.h https://github.com/King-Drake/Liberal-Crime-Squad/blob/master/src/cursesAlternative.h can have its implementation in C#/Unity that would amount to the majority of the work that can't be automated.
Related, most instances of getkey() have been replaced with pressAnyKey(), which would help give mouse/touchscreen support.
I'm excited at the prospect of someone else doing the work. It makes me feel important.
tldr;
If the functions in this header file are implemented in Unity, I could have a port ready almost immediately. (Without sound, at least)
https://github.com/King-Drake/Liberal-Crime-Squad/blob/master/src/cursesAlternative.h
52 functions. Most of them are variations on one another.
-
Well that header would be the easy part, the hard part would be that C# is fairly restrictive of a lot of things than C++. Syntax is similar but plenty of little tricks possible in C++ plain won't work.
For example, there's no multiple inheritance in C#, so any multiple inheritance in C++ would probably be best to just refactor away before the port.
Another thing is global variables. You just don't get to have them in C#, you'd need a class with static variables in it. The C++ can be refactored to have that.
Also, C# doesn't have C++ style pointers and references, it has a single reference type that can be null, similar to C++ pointers.
C# also has several more Java-like requirements that almost everything needs to be inside a class in C#, and that could impact some things too.
Those kinds of changes can lead to ripple-on effects throughout the codebase, so I'd think that trying a tentative C# port of some subsystems might indicate potential problems.
Rather than doing a super-hacky one-off C#/Unity port, it would be far better to do the needed fixes on the C++ side of things. The entire point of the C#/Unity exercise would be to end up with a truly cross-platform version that can in fact be maintained.
-
Interesting. Reelya, do you have a github so I can give you collaborator access? This is the first time I'm actually participating in a collaborative project, so pardon my amateur-ness.
Basically, the beginning choices would be entirely UI elements on the canvas. We should keep in mind that options should be linked to a button press, but also a mouseclick for when we switch to an android platform. Would it be easier to do certain text and the infiltrations, like the beginning founder backstory, as separate scenes rather than the whole game being one scene? That way you don't have to keep destroying or hiding gameObjects when you don't want to display them. Instead, each screen will load the next scene as soon as you choose an option. It might simplify things a bit.
I am not well versed on the C++ code, not sure how much I can contribute to that.
Try to keep the project file hierarchy organized if you are gonna work on it. Some of the folders were not included in the github for some reason, but you can see their meta files.
-
Don't go for separate Unity scenes, that way lies madness and unmaintainabilty. The thing is, C++ has no concept of "scenes" with different code in them, so trying to add that to the existing, working game would be months worth of screwing around for really no benefit.
But trust me, there's a ton of work that would need to be done on the C++ before it's even viable to think about porting it.
For example there are a ton of functions that take pointers or references to things like creatures, when they should in fact take an int which is the index of the creature in the global table of creatures. That would fix a lot of things and be more portable (since C# only has references, and they don't work like C++ references, whereas passing an int is totally portable without needing to rewrite every single function header).
Also, remember, virtually everything in LCS is separate global functions and plenty of global variables. None of which is even allowed in C#. Every single header file in LCS would need to be replaced by an object that includes the functions as static functions. Luckily, that'll work in both C++ and C#, so it should just be done on the C++ side to start with. All the "extern" shit needs to go as well then.
and there is the use of "char *" raw strings everywhere. That would need to be changed so that they use a string class consistently, preferably one with a compatible interface to C# native strings. That's do-able, but should definitely just be refactored into the C++ to start with.
-
Well that header would be the easy part, the hard part would be that C# is fairly restrictive of a lot of things than C++.
True enough. That said, the header file is a list of most of the new code that needs writing, as opposed to old code that needs to be rewritten. By that I mean it's the work that can be done without requiring specific experience with the LCS codebase. Any progress people make on implementation of those functions can be made without fear of stepping on my toes.
For example, there's no multiple inheritance in C#, so any multiple inheritance in C++ would probably be best to just refactor away before the port.
Not to worry, there's no multiple inheritance in LCS. Barely any inheritance at all, since LCS uses so little object oriented programming, which is a problem in the other direction.
Also, C# doesn't have C++ style pointers and references, it has a single reference type that can be null, similar to C++ pointers.
That... that is going to be a pain. Not going to lie.
C# also has several more Java-like requirements that almost everything needs to be inside a class in C#, and that could impact some things too.
Certainly. Most of the refactorings I've done over the past year have been to help porting to Java. It will be a lot easier to port 4.12 than 4.10. There was this duplicate line of code that an object's pointer was subtracted from another pointer in order to get its index for a global array. It made me cry. It's gone now, and it won't hurt anyone ever again.
Those kinds of changes can lead to ripple-on effects throughout the codebase, so I'd think that trying a tentative C# port of some subsystems might indicate potential problems.
Rather than doing a super-hacky one-off C#/Unity port, it would be far better to do the needed fixes on the C++ side of things. The entire point of the C#/Unity exercise would be to end up with a truly cross-platform version that can in fact be maintained.
Glad to see someone else thinking ahead.
Basically, the beginning choices would be entirely UI elements on the canvas. We should keep in mind that options should be linked to a button press, but also a mouseclick for when we switch to an android platform. Would it be easier to do certain text and the infiltrations, like the beginning founder backstory, as separate scenes rather than the whole game being one scene? That way you don't have to keep destroying or hiding gameObjects when you don't want to display them. Instead, each screen will load the next scene as soon as you choose an option. It might simplify things a bit.
In theory a multiple scene setup could work, but LCS as written in C++ only has one 'scene'. KISSOM, Keep It Simple, Sir Or Madam.
But trust me, there's a ton of work that would need to be done on the C++ before it's even viable to think about porting it.
ABSOLUTELY TRUE.
Another thing is global variables. You just don't get to have them in C#, you'd need a class with static variables in it. The C++ can be refactored to have that.
All the "extern" shit needs to go as well then.
and there is the use of "char *" raw strings everywhere.
I've done a fair amount of refactoring to reduce the scope of global variables. Each variable is declared exclusively in the file it is used, and the only usage of the externs keyword is within individual functions. It was an enormous pain. I have dreams about "extern". No matter how many I kill, they keep coming.
And char*. My C++ book says not to use char*, and that's all it has to say about that.
Throughout the LCS codebase there are instances of:
string str = ...;
someFunction(str.data(), ...);
someOtherFunction(str.c_str(), ...);
.data() and .c_str() are functions that convert a string to a char*. (.data() and .c_str() are the same function, but they used to be different, so there's controversy in the C++ community about which one to use, but they are mostly interchangeable with modern compilers)
LCS is so tightly connected with char* that many strings have to be converted to char* in order to be used. Much work to be done.
-
What might be a good idea for the strings is to add a home-brew string class to LCS in C++, but replicate the needed functionality of the C# String in it.
Internally, the LCS-String could then use std::string, but by hiding it inside a wrapper it will be more portable.
-
What might be a good idea for the strings is to add a home-brew string class to LCS in C++, but replicate the needed functionality of the C# String in it.
Internally, the LCS-String could then use std::string, but by hiding it inside a wrapper it will be more portable.
That's probably overkill.
The only functionality of strings that LCS uses are reassignment and concatenation. The functionality of char* it uses (that are incompatible with std::string) are fixed length null termination and pass by reference reassignment. Fixed length is fine, but the pass by reference reassignment is undesirable. It overcomplicates the code and makes debugging a chore.
Creating a wrapper class would have its merits, but I'd rather isolate and ultimately remove the instances where LCS uses functionality of strings that are not already universally supported.
-
It's not overkill. You only implement a minimum feature set, but you make the interface compatible with String as it exists in both C# and Java. There's no need to implement stuff that's never used.
But it needs to be done for portability, otherwise you'd need to go through and change each and every line of code that uses strings whenever it's ported.
Well planned refactoring doesn't need to happen all at once however, so a good start would just be to wrap a "char*" inside a thing called String then use that to pass char* around. By using String to mean char* you can probably make it so you never have to explicitly mention char* anywhere else except inside the String class. Then after that, you gradually move needed functionality inside the String class, keeping in mind that you want it to end up having the same interface as String in C#/Java. So, for a while you'd have your program using both the custom String and std::string, but you then phase out the latter, too, once the String class is functional enough.
-
It's not overkill. You only implement a minimum feature set, but you make the interface compatible with String as it exists in both C# and Java. There's no need to implement stuff that's never used.
But it needs to be done for portability, otherwise you'd need to go through and change each and every line of code that uses strings whenever it's ported.
Well planned refactoring doesn't need to happen all at once however, so a good start would just be to wrap a "char*" inside a thing called String then use that to pass char* around. By using String to mean char* you can probably make it so you never have to explicitly mention char* anywhere else except inside the String class. Then after that, you gradually move needed functionality inside the String class, keeping in mind that you want it to end up having the same interface as String in C#/Java. So, for a while you'd have your program using both the custom String and std::string, but you then phase out the latter, too, once the String class is functional enough.
That's a good point. I just looked over some of the char*, and several explicitly cannot be replaced with strings because they interact with external libraries. A string wrapper would solve most of those.
-
Another thing I'll mention is that char isn't portable anymore. C# and Java have 16-bit chars.
The 8-bit value in both languages is called "byte". For small numeric values it thus might be better to replace char with byte and use a typedef.
It'll also be less ambiguous. "char" for things that aren't chars is in fact a problematic programmer short-cut. Just like it's good practice to name variables meaningful things, you can typedef inbuilt types and use enums as types everywhere possible, so that your typenames convey the semantic purpose.
EDIT: I also noticed the use of "short" type. This isn't a good type to use since it's ambiguous and implementation-specific.
EDIT2: Another thing is that we could use is to wrap all the curses calls up in our own class so that we don't have to have those crappy calls everywhere. A custom interface would be a lot cleaner.
-
PTWing, although will also say that I have the (apparently disappeared from Play) LCS install on an older tablet, which I might be able to get the..pkg of out of if I specifically do a bit of digging up of the necessary old hardware, then digging into its file system.
Seems like you're already doing it properly, though.
-
The current code is so far different from what's in the pkg though and we'd need the source code anyway. But sure if some people want to play around with that version then it's cool that someone kept it, you could put it on mediafire or something.
-
Another thing I'll mention is that char isn't portable anymore. C# and Java have 16-bit chars.
The 8-bit value in both languages is called "byte". For small numeric values it thus might be better to replace char with byte and use a typedef.
My plan is to replace just about every single usage of char, short, and byte with int. If it is meant to represent a character, it will use a Character. If it is meant to represent a number, it will use an Integer. The way LCS interchanges char and short is a nightmare of ambiguity. There was a safehouse bug that used ">= char" that broke when converted to a short or an int, because certain characters have a negative value when interpreted as a short. Any number with an absolute value smaller than 1 Billion can be represented with a 32 bit integer, and it is unnecessary to use a special data type just because its value allows for more compact storage. Modern compilers are optimized for usage of 32 bit integers and 64 bit floating points. That brief moment of confusion a programmer feels when trying to remember if a function returns its value as a char, byte, short, or int is too high a price for the possible benefits of using an 8 or 16 bit value. The only reason we even have shorts and bytes as standard datatypes are because C++ was first used 33 years ago, because back then it made a difference. 95% of the filesize for LCS is located in its music files. We don't need to use shorts, bytes, or chars. And as long as I have a say, we won't.
o.o;
Sorry, got a little carried away there.
Another thing is that we could use is to wrap all the curses calls up in our own class so that we don't have to have those crappy calls everywhere. A custom interface would be a lot cleaner.
That's what that header file I kept going on about was.
Nowhere in the LCS source code, except cursesAlternative.cpp, is usage of the curses library.
Heck, most instances of
move(x,y);
addstr(...);
have been replaced with
mvaddstr(x,y, ...);
Just to avoid having to implement move(x,y);
If move(x,y) and addstr(...) can be replaced with mvaddstr(...) entirely, it will make mvaddstr(...) easier to implement, as it will no longer need to store current position. It's one of the reasons I've been replacing char* with string.
PTWing, although will also say that I have the (apparently disappeared from Play) LCS install on an older tablet, which I might be able to get the..pkg of out of if I specifically do a bit of digging up of the necessary old hardware, then digging into its file system.
Seems like you're already doing it properly, though.
The current code is so far different from what's in the pkg though and we'd need the source code anyway. But sure if some people want to play around with that version then it's cool that someone kept it, you could put it on mediafire or something.
http://www.mediafire.com/file/7zykkvii3cd19su/lcsagame%20Android.zip
Includes source.
-
Any number with an absolute value smaller than 1 Billion can be represented with a 32 bit integer
+- 2 billion actually, or 0 ~ 4 billion for unsigned int
Another choice is to declare things as enum types, when it makes sense. Then, the C++ compiler gets to make the optimization choice for you.
Though, I wouldn't do this for current enums of things that are data which could change often. For example, i'd avoid using creaturetype enums in this way, since it would be a good idea to move all creaturetype-specific code into the data files. It's really bad to have the game hardcoded to know what an Acrobat or a Dancer are.
But for something like an entity's gender, you can use an enum as the data type.
bools should be used more often too, whenever a value can only be true/false. enums for anything with a small range of allowed values, then rather than using numeric codes, the values can be compared to the allowed enum choices, which makes code much easier to read.
-
Any number with an absolute value smaller than 1 Billion can be represented with a 32 bit integer
+- 2 billion actually, or 0 ~ 4 billion for unsigned int
-2_147_483_648 ~ 2_147_483_647
0 ~ 4_294_967_295
Same difference. Different precision. The first statement lists a subset of numbers capable of being represented by a 32 bit integer, the second statement increases the size of the subset. The third statement lists the exact subset.
In LCS most numbers never exceed 65_535, (or even 255, for that matter) that's why it has so many shorts and chars.
Another choice is to declare things as enum types, when it makes sense. Then, the C++ compiler gets to make the optimization choice for you.
Though, I wouldn't do this for current enums of things that are data which could change often. For example, i'd avoid using creaturetype enums in this way, since it would be a good idea to move all creaturetype-specific code into the data files. It's really bad to have the game hardcoded to know what an Acrobat or a Dancer are.
Yeah, that's a long-term concern. There's a line where an NPC has a death message "what about my children", unless that NPC is a mutant or a dog, where it changes to "what about my offspring". There are a ton of fringe cases that delay the migration to data-driven development.
But for something like an entity's gender, you can use an enum as the data type.
bools should be used more often too, whenever a value can only be true/false. enums for anything with a small range of allowed values, then rather than using numeric codes, the values can be compared to the allowed enum choices, which makes code much easier to read.
There are a frightening number of times that chars/shorts are used in place of bools. It's just those blasted cases where some have >= conditionals. Three or four if statements, each one adds a value to the char, then at the end of it, it's treated as a bool anyway. It was not designed with porting in mind.
-
(You people with your Short Billions. A billion meant something when I was young, unlike today when it's just trivially bigge than a million.... :P)
-
How long would you estimate that the c++ code can be adequately fixed before porting it to c#? Is there anyway I can currently help?
-
How long would you estimate that the c++ code can be adequately fixed before porting it to c#? Is there anyway I can currently help?
The trick is that I'm doing this in my free time. It's difficult to estimate accurately, but months at least.
Helping is fairly straightforward at this time.
https://github.com/King-Drake/Liberal-Crime-Squad/blob/master/src/cursesAlternative.h
This is the cursesAlternative interface. One of the main obstacles to porting LCS is its reliance on PDCurses. cursesAlternative encapsulates all interactions with the PDCurses library. Constructing an implementation of cursesAlternative in C# and/or Unity would help in the long term.
There are several instances of "char*" in cursesAlternative, but they can be treated as strings.
In theory, cursesAlternative is the only completely new code that needs to be written. It shouldn't be hard, but it is likely to be time consuming.