Storing and processing RPG Weapons, Spells, Classes C# - c#

So I've got a text-based RPG I've been brainstorming over with a few of my friends. We've got the whole principle down. Here's how it'll work:
Spells will be split between classes (Warrior, Mage, Rogue etc)
Each player can select let's say 3 spells to use in battle of the class specific let's say 10.
The problem that I've got at the moment is that I have no clue how to store spells. I could just do this:
public interface IClass {
string name {get; set;}
//some other features
}
public class Warrior : IClass {
public string name {get; set;} = "Warrior";
//again, some other features
}
public interface ISpell {
string name {get; set;}
IClass classReq {get; set;}
string description {get; set;}
double CalculateDamage(double d);
}
public class SlamAttack : ISpell {
public string name {get; set;} = "Slam Attack";
public IClass classReq {get; set;} = new Warrior();
public string description {get; set;} = "Do 10 damage, then Stun the target";
double CalculateDamage(double d) {} //Do what description says
}
public class MagicBoltThing : ISpell {
public string name {get; set;} = "Magic Bolt";
public IClass classReq {get; set;} = new Wizard(); //Just another class
public string description {get; set;} = "Do 12-15 damage";
double CalculateDamage(double d) {} //Do what description says
}
//and soooo on
This last part is what I would've loved to avoid (defining 40 different classes just so each CalculateDamage() does something different), but I gave up on that. So I decided to something with a database, but I never worked with databases before, so I just did somethin like this, and use it with reflection:
SpellID | SpellName | SpellClass
---------------------------------------
1 | Magic Bolt | MagicBoltThing
2 | Slam Attack | SlamAttack
Reflection part looks somethin like this:
var type = Type.GetType($"MyNameSpace.Spells.{spellNanme}"); //spellName is just the name of the spell I got from the user by typing into the console for example
dynamic spell = Activator.CreateInstance(type); //dynamic is the only way I managed to get this to work, because for some reason casting, broke the entire thing
So is there a better way of going about this... entire thing. It feels extremely clunky and it's kinda hard to work with + apparently a lot of ppl here say that to not use reflection. So is there a better way to store and proccess spells (either a better way of storage than a database, or better way to get classes from a string other than reflection)?

Related

.NET Generic classes within IOptions with Dependency Injection

I'm trying to setup generic classes within IOptions settings.
Example:
public class MyClass<T> : IMyClass
{
public MyClass(IOption<MyOptions<T>> options)
{
}
}
To be able to dynamically use settings from appsettings.json. While the set of options is the same, they should be under different schemas, so that each type defines its set of options i.e.:
"MyOptionsForClass1":
{
"Option1" : 1,
"Option2" : 1
},
"MyOptionsForClass2":
{
"Option1" : 2,
"Option2" : 2
},
And the options classes should look like this:
public class MyOptions<T>
{
public const string ConfigSchemaName = "DefaultOptions"
public int Option1 {get; set;}
public int Option2 {get; set;}
}
public class OptionsForClass1 : MyOptions<MyClass1>
{
public new const string ConfigSchemaName = "MyOptionsForClass1"
public int Option1 {get; set;}
public int Option2 {get; set;}
}
And then in ConfigureServices to add something like:
.AddScoped<MyOptions<MyClass1>, OptionsForClass1>();
...
.AddOptions<MyOptions<MyClass1>>()
.Bind(configuration.GetSection(OptionsForClass1.ConfigSchemaName);
Is this how this should be done?
Are there any other more clean approaches for this?
Thank you!
You definitely should not do it this way.
Note the squiggly lines under Option1 and Option2? It's telling you that you are hiding the members in the parent class, but you forgot to use the new keyword.
But using new is bad for hiding (shadowing) members. Try this code:
var ofc1 = new OptionsForClass1();
ofc1.Option1 = 42;
Console.WriteLine(ofc1.Option1);
var my = (MyOptions<MyClass1>)ofc1;
Console.WriteLine(my.Option1);
That outputs:
42
0
Note that I've only created one instance of OptionsForClass1 but when I cast to MyOptions<MyClass1> I get a different value for Option1.
Your approach kills the value of inheritance.
If you do want this kind of structure, your code should look like this:
public class MyOptions<T>
{
public virtual string ConfigSchemaName => "DefaultOptions";
public virtual int Option1 { get; set; }
public virtual int Option2 { get; set; }
}
public class OptionsForClass1 : MyOptions<MyClass1>
{
public override string ConfigSchemaName => "MyOptionsForClass1";
}
You could avoid a const string by using an attribute.
Try this:
public class ConfigSchemaNameAttribute : Attribute
{
public string Name { get; init; }
public ConfigSchemaNameAttribute(string name)
{
this.Name = name;
}
}
[ConfigSchemaName("MyOptionsForClass1")]
public class OptionsForClass1 : MyOptions<MyClass1>
{
}
Then you can write this:
Console.WriteLine(
typeof(OptionsForClass1)
.GetCustomAttribute<ConfigSchemaNameAttribute>()
.Name);
That outputs:
MyOptionsForClass1
Is this how this should be done?
It's definitely how it can be done. Should be done? That's pretty subjective, once the solution meets a certain standard of quality.
Your solution falls into the over-engineered category, where you have a very intricate solution to a problem that has simpler solutions. For example, you could have a simple dictionary of settings you inject in every object, and each object takes out what settings it wants. Very simple and efficient, both at run-time and while coding -- no boilerplate at all, one class for all cases!
But if you have a lot of options, maybe you need the over-engineered solution. There's definitely nothing wrong with it, besides it being over-engineered. Though I'd suggest separate settings files if you have such an incredible amount of settings that you need separate classes for all of them.

Best way to filter over object's property by value

class Sample
{
public type1 Prop1 {get; set;}
public type2 Prop1 {get; set;}
public type3 Prop1 {get; set;}
.
.
.
.
.
public typen Propn {get; set;}
}
The problem is as follows:
Say I have a list of Sample objects. I have to provide funcionality to filter objects over a specific property by a specific value.
The method should be as follows:
List<Sample> filter(string propertyName, string value)
{
...
}
The intuitive approach I came up with is to switch case on propertyName, try to parse the input value to its real type
and then make the comparison. What disturbs me is that if the object has many properties, this solutions results with
logic duplication. My question is whether do you know of a better solution that doesn't use reflection.
Thanks.

Format XML containing List for Deserialization

I have some data which I need to bring from an XML file to a PostgreSQL database through a C# application I am currently working on.
The problem I face now is that I need to add some kind of list to the XML now. Currently it looks something like this:
<itemOne>stuff</itemOne>
<itemTwo>otherStuff</itemTwo>
<listOfItems>
<listItem>
<partOne>123</partOne>
<partTwo>abc</partTwo>
</listItem>
<listItem>
<partOne>123</partOne>
<partTwo>abc</partTwo>
</listItem>
</listOfItems>
So I found quite some threads which handle the topic of how to deserialize lists. But all of them start by saying that the proposed XML should be modified. As I have all the freedom that I could wish for in that regard, I'd like to first create an optimal XML format at first.
Well, and afterwards deserialize it. So if there is any format for lists which is automatically processed by XmlSerializer.Deserialize() it would be amazing.
That data will already be processed automatically by XmlSerializer, as long as the class is shaped to match. For example:
[XmlRoot("yourRootName")]
public class Foo {
[XmlElement("itemOne")] public string A {get;set;}
[XmlElement("itemTwo")] public string B {get;set;}
[XmlArray("listOfItems")]
[XmlArrayItem("listItem")]
public List<Bar> Bars {get;set;}
}
public class Bar {
[XmlElement("partOne")] public int C {get;set;}
[XmlElement("partTwo")] public string D {get;set;}
}
Of course, if it was me I'd be tempted to be terse, with:
<betterName foo="stuff" blab="otherStuff">
<nameMe a="123" b="abc"/>
<nameMe a="456" b="def"/>
</betterName>
which just requires some minimal changes:
[XmlRoot("betterName")]
public class Foo {
[XmlAttribute("foo")] public string A {get;set;}
[XmlAttribute("bar")] public string B {get;set;}
[XmlElement("nameMe")]
public List<Bar> Bars {get;set;}
}
public class Bar {
[XmlAttribute("a")] public int C {get;set;}
[XmlAttribute("b")] public string D {get;set;}
}

Interface and exposing class as property

I am looking at this code from a peer of mine and am a little confused as to how this makes sense:
public class CA
{
public CurrType CT {get; set;}
}
public interface ICharge
{
CA a {get; set;}
CurrType CT {get; set;}
}
public enum CurrType {X=0, Y=1}
public class Ch : ICharge
{
public CA a {get; set;}
public CurrType CT {get; set;}
}
I understand that interfaces contain properties but cant make any sense of the above code specifically:
1) Can the interface ICharge contain a class as a property?
2) Isnt some circular reference going on?
Isnt some circular reference going on?
No, there is no circular reference going on.
Can the interface ICharge contain a class as a property?
It sure can - why did you think it couldn't? This is perfectly normal usage - the interface is specifying that that property will contain an instance of that class (i.e. an object of that type). Why a class? Because classes are everywhere - even a lot of the basic .Net types are classes. A string is a class. You can't do everything with primitive value types :)
I can't answer why there is two different usages of CurrType in Ch (one at the root level, one in the CA instance), that's for you to figure out.

Clean way to Compare / Copy between Similar Classes

We have two classes that have the exact same public accessors (and many of them) but are from different areas in the object hierarchy; we have a need to copy and compare between these two objects. We could manually write a copy constructor and a comparison operator which compares the values of the same-named accessors, but it seems as though there's got to be a better way to do this using reflection and LINQ.
Example: we have class ClassA which has 70 accessors; we also have class ClassB which has 70 accessors, which are defined to be the same name and type as ClassA's accessors.
public class ClassA
{
int OneInt {get; set;}
int TwoInt {get; set;}
...
string OneString {get; set;}
string AnotherString {get; set;}
}
public class ClassB
{
int OneInt {get; set;}
int TwoInt {get; set;}
...
string OneString {get; set}
string AnotherString {get; set;}
}
What I'd like is a simple way of using reflection to discover all of the public accessors of ClassA, and use those names to set the values of the respective accessor on ClassB to the value of the accessor on ClassA. Roughly, in psuedocode:
foreach (string accName in ClassA.Accessors[])
BInstance.Accessors[accName].Value = AInstance.Accessors[accName].Value;
And, of course, the same thing could be used for testing equality between the two classes. My knowledge of C# reflection and LINQ is not good enough to know how to get this done, but I'd swear it's something relatively simple.
How about using AutoMapper:
Mapper.CreateMap<ClassA, ClassB>();
and then:
ClassA classA = ...
ClassB classB = Mapper.Map<ClassA, ClassB>(classA);
It's basically an implementation of your pseudo-code.
your rough pseudocode is somewhat accurate. Let me clean it up a little:
foreach (var propertyA in typeof(ClassA).GetProperties())
{
var propertyB = typeof(ClassB).GetProperty(propertyA.Name);
var valueA = propertyA.GetValue(objectA, null);
propertyB.SetValue(objectB, valueA, null);
}
Obviously, this doesn't do error checking, and stuff like that, but it should do the job.
You could do it in Linq, but I don't think it would be cleaner.

Categories

Resources