PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Fragen zum Klassenkonzept


aths
2004-05-19, 13:37:00
Ich überlege, ein Programm zu schreiben, was nach D&D 3.5-Regeln Schätze auswürfelt.

Schätze bestehen aus mehreren Items. Jedes Item hätte folgende Eigenschaften:

Name
Erklärung
Wert

Der Schatzwürfler muss also mehrere Items zurückliefern, und zumindest jeweils Name und Wert ausgeben. Folgendes Problem: Bei einigen Items würde es Sinn machen, zusätzliche Eigenschaften zu speichern. Z. B. könnte man Waffen von Items ableiten und noch Werte wie Schaden etc. speichern. Nur weiß der Schatzwürfler ja nicht im Vorhinein, ob, und wenn welche spezialisierten Typen ausgewürfelt werden. Lässt sich das prinzipiell lösen?

Ansonsten müsste Zusatz-Werte (halt als String) in einer Extra-Eigenschaft "Beschreibung" gespeichert werden.

ScottManDeath
2004-05-19, 14:20:45
Klassischer Fall für Prototype Pattern

Du hast eine Basisklasse für alle Items. Jedes Item hat eine Clone Methode um eine identische Version seiner selbst herzustellen sowie eine Methode um sich zufällig zu machen. Diese Methoden können auch abstract sein und werden von den konkreten Klassen überschrieben. Das geht doch auch in Delphi?


Du initialisierst ein Feld mit Zeigern auf diese Basisklasse mit je genau einem abgeleiteten Typ. Dann Wählst du per random eine dieser aus, klonst sie und machst sie zufällig

Pseudocode:


class Item
{


virtual Item* Clone()
{
Item* the_item;
the_item->Name = "Test";
return the_item;
}

virtual void Random()
{
// zufällig machen
}

virtual string GetText()
{
return this->name;
}


}


class Weapon : Item
{
virtual Item* Clone()
{
Weapon the_item;
the_item->Name = "Test";

// hier alle werte kopieren

return the_item;
}

virtual void Random()
{
// zufällig machen, auch werte die nur waepon hat
}


}

// analog für alle anderen Item subtypen



class TreasureMaker
{


void AddProtoype(Item* p)
{
m_prototypes.add(p)
}

void MakeTreasure()
{
// wähle einen der prototypen zu fällig aus

Item* new_item = prototypes[random]->Clone();
new_item->Random();

print new_item->GetText();
}

list<Item*> prototypes

}





main

{


Treasuremake maker;

maker.AddPrototype(new Weapon());
maker.AddPrototype(new Potion());

while ( ! enough)
maker.MakeTreasure;


}

Corrail
2004-05-19, 14:27:06
Können sich die Eigenschaften pro Objekt ändern? Also, hat z.B. ein "Langschwert" immer die gleiche Erklärung und den gleichen Wert? Wenn ja würde ich jedem Objekt eine Zahl zusweisen (enum) und dann in einem Array alle Daten abspeichern.
Ansonsten eine Basisklasse schreiben und für alle bestimmten Objekte ableiten. Eine abstrakte Funktion wie "ShowDetails", die dann alle speziellen Daten ausgibt.

Gast
2004-05-19, 14:30:33
Du könntest die Eigenschaften doch einfach vererben:

#include <iostream>
#include <stdlib.h>
#include <string>

using namespace std;

class item
{
public:
string name;
string erklaerung;
int wert;
};

class weapon : public item // erbt also auch die Eigenschaften von item
{
public:
int schaden;
int reichweite;
};

class bow : public weapon // erbt also auch die Eigenschaften von weapon und item
{
public:
int pfeile;
};

int main(int argc, char *argv[])
{

bow mybow;

mybow.name="Elfenbogen"; // aus item
mybow.reichweite=2; // aus weapon
mybow.pfeile=100; // aus bow

cout << mybow.name << "\n" ;
cout << mybow.reichweite << "\n" ;
cout << mybow.pfeile << "\n" ;

system("PAUSE");
return 0;
}Ein Bogen ist eine spezielle Waffe. Eine Waffe ist ein bestimmtes Item.

grakaman
2004-05-19, 14:37:50
Ich glaube das Problem von Aths ist, wie er denn alle Eigenschaften _dynamisch_ auslesen kann. Und da fällt mir neben Reflections, wenn möglich, auch nur eine ArrayList etc. ein.

ethrandil
2004-05-19, 14:43:08
Wenns darum geht:


interface item{
/**
* Gibt ein Set mit allen möglichen Properties als String zurück
*/
public Set giveProperties();
public void setProperty(String pName);
public int getProperty(String pName);
}


sowas?

- Eth

EDIT: Das ist so ganz großer Quatsch, ich hab später nochmal das ganze in vernünftig geschrieben ;-)

grakaman
2004-05-19, 14:47:53
Original geschrieben von ethrandil
Wenns darum geht:


interface item{
/**
* Gibt ein Set mit allen möglichen Properties als String zurück
*/
public Set giveProperties();
public void setProperty(String pName);
public int getProperty(String pName);
}


sowas?

- Eth

Und wie soll das funktionieren?

Corrail
2004-05-19, 14:50:47
Naja, man kann es sehr wohl vererben. Ich hätte das so irgendwie gemacht:



class Item
{
virtual void ShowDetails() = NULL;

string Name;
string Erklaerung;
float Wert;
}

class Schwert : public Item
{
virtual void ShowDetails()
{
pirntf("Name: %s\n", Name.c_str());
pirntf("Erklärung: %s\n", Erklaerung.c_str());
pirntf("Wert: %f\n", Wert);
pirntf("Gewicht: %f\n", Gewicht);
}

float Gewicht;
}

class Buch : public Item
{
virtual void ShowDetails()
{
pirntf("Name: %s\n", Name.c_str());
pirntf("Erklärung: %s\n", Erklaerung.c_str());
pirntf("Wert: %f\n", Wert);
pirntf("Seitenzahl: %d\n", Seitenzahl);
}

int Seitenzahl;
}



Und dann eine jeden Gegenstand eindeutig mit einer Zahl verbinden:
Schwert = 0
Buch = 1
...

Und dann einen einfachen Zufallszahlengenerator schreiben. Und abhängig von dem welche Zahl geworfen wurde das Objekt wird dann erstellt. Die Funktion kann dann so ausschauen:



Item * GetRandomItem()
{
int zahl = random_int();

switch (zahl)
{
case 0:
return new Schwert;
case 1:
return new Buch;
};

return NULL;
}

grakaman
2004-05-19, 15:04:31
Gut, aber so bist du freilich nicht flexibel. Vielleicht will man ja darüber hinaus jede Eigenschaft weiterverarbeiten. Da wäre wohl imo eine ArrayList besser geeignet.

MfG

ethrandil
2004-05-19, 15:07:57
Original geschrieben von grakaman
Und wie soll das funktionieren?


interface Item{
/**
* Gibt ein Set mit allen möglichen Properties als String zurück
*/
public Set giveProperties();
public void setProperty(String pName, String value);
public String getProperty(String pName);

public void randomize();
}

class AbstractItem implements Item{
Map properties;

public Item(){
properties = new HashSet();

properties.put("Name", "");
properties.put("Erklärung", "");
properties.put("Wert", "");

initialize();
}

public Set giveProperties(){
return properties.keySet();
}

public void setProperty(String pName, String value){
if(properties.containsKey(pName))
properties.put(pName, value);
}

public String getProperty(String pName){
return properties.get(pName);
}

/**
* Macht etwas wie
* properties.put("Schaden", "");
*/
private abstract void initialize();
public abstract void randomize();
}


so. und dann geht der Generator so vor:


Set items = new HashSet();

for(int i=itemAnzahl; i>0; i--){
Item item = null;
switch((int)(Math.random()*moeglicheItems)){
case 0: //Schwert
item = new Schwert();
item.randomize();
break;
case 1: //Lanze
item = new Lanze();
item.randomize();
break;
[...]
}
items.add(item);
}

//Ausgabe aller Items samt aller Eigenschaften:

for(Iterator i = items.iterator(); i.hasNext();){ //Für jedes Item
Item item = (Item)i.next();
for(Iterator iP = item.giveProperties().iterator(); iP.hasNext();){
String paramName = (String) iP.next();
System.out.println("Das Item "+item+" hat die Eigenschaft "+paramName+
" mit dem Wert "+item.getProperty(paramName));
}
}


hab ich wieder was übersehen? *duck*

grakaman
2004-05-19, 15:19:55
Naja, du benutzt ja auch eine Collection. Habe ja auch neben meiner genannten ArrayList ein "etc." geschrieben.

DocEW
2004-05-19, 15:25:29
Original geschrieben von aths (...) Folgendes Problem: Bei einigen Items würde es Sinn machen, zusätzliche Eigenschaften zu speichern. Z. B. könnte man Waffen von Items ableiten und noch Werte wie Schaden etc. speichern. Nur weiß der Schatzwürfler ja nicht im Vorhinein, ob, und wenn welche spezialisierten Typen ausgewürfelt werden. Lässt sich das prinzipiell lösen?

Ansonsten müsste Zusatz-Werte (halt als String) in einer Extra-Eigenschaft "Beschreibung" gespeichert werden.
Wenn ich dich richtig verstehe, willst du dich vor einer spezialisierten Weiterverarbeitung der ausgewürfelten Items drücken und es geht gar nicht so sehr um die Erzeugung, oder? Das Problem ist also quasi, daß du ein Item kriegst und nicht weißt, was für eins es nun ist, aber trotzdem eine Unterscheidung in der weiteren Behandlung benötigst.
Eine Lösung dafür (außer unschön mit Reflection oder einem kennzeichnenden Attribut) würde mich auch interessieren, da ich zur Zeit ein ähnliches Problem habe. Aber leider schätze ich, daß das irgendwie auf einen Designfehler hindeutet... :-(
Edit:
Solange es nur um die Ausgabe des Items geht müßte Ethrandils Lösung ja funktionieren, oder? Alternativ könntest du auch eine Art print() Funktion für jede Klasse implementieren (finde ich aber nicht so schön wie die Lösung von Eth).

HellHorse
2004-05-19, 15:43:31
Original geschrieben von aths
Nur weiß der Schatzwürfler ja nicht im Vorhinein, ob, und wenn welche spezialisierten Typen ausgewürfelt werden. Lässt sich das prinzipiell lösen?

double dispatch
z.B. visitor pattern

Anmerkung zu ScottManDeath:
Man wird wohl zusätzlich, wenn man einen Prototypen hinzufügt, seine Wahrscheinlichkeit angeben müssen, was die ganze Sache etwas kompilierter macht.

grakaman
2004-05-19, 16:23:47
Will ja nicht nörgeln, aber du solltest dann schon noch ein paar Worte über das Pattern verlieren.

ethrandil
2004-05-19, 16:27:33
ich will ja nicht nörgeln, aber Google verliert da auch ne menge Wörter ;-)

- Eth

HellHorse
2004-05-19, 16:44:37
Original geschrieben von grakaman
Will ja nicht nörgeln, aber du solltest dann schon noch ein paar Worte über das Pattern verlieren.
Sorry, hielt es für Allgemeinbildung. :D
Kommt bei Gelegenheit.

aths
2004-05-19, 16:48:35
Original geschrieben von Corrail
Können sich die Eigenschaften pro Objekt ändern? Also, hat z.B. ein "Langschwert" immer die gleiche Erklärung und den gleichen Wert? Wenn ja würde ich jedem Objekt eine Zahl zusweisen (enum) und dann in einem Array alle Daten abspeichern.
Ansonsten eine Basisklasse schreiben und für alle bestimmten Objekte ableiten. Eine abstrakte Funktion wie "ShowDetails", die dann alle speziellen Daten ausgibt. Das wäre eine Möglichkeit, an die ich noch gar nicht gedacht hatte.

Original geschrieben von grakaman
Ich glaube das Problem von Aths ist, wie er denn alle Eigenschaften _dynamisch_ auslesen kann. Und da fällt mir neben Reflections, wenn möglich, auch nur eine ArrayList etc. ein. Mein Problem ist, erst mal das Problem als solches selbst zu verstehen :)

Angenommen, die Tabelle sieht vor 1D4 mundane Items. Eine Funktion, die aus '1D4' einen Zufallswert 1..4 erzeugt zu schreiben, kriege ich hin. Nun müssen also 1 bis 4 zufällige mundane Items erzeugt werden. Dazu wird zunächst gewürfelt, was für ein Typ (Ausrüstung, Waffen, Potions etc.) und danach (angenommen eine Waffe wurde gewürfelt) was genau für eine Waffe. U. U. müssen hier noch mal weitere Tabellen herangezogen werden.

Eigentlich bräuchte ich noch einen Typ, der eine gewisse, vorher nicht festgelegte Anzahl (spezialisierter) Items zurück liefert.

Ich überlege, ob das Problem über eine Art Listeninterpreter nicht einfacher zu lösen wäre.

Xmas
2004-05-19, 18:18:06
Original geschrieben von aths
Ansonsten müsste Zusatz-Werte (halt als String) in einer Extra-Eigenschaft "Beschreibung" gespeichert werden.
Da ich mal davon ausgehe dass die ausgewürfelten Items lediglich angezeigt und nicht weiterverarbeitet werden, ist das IMO die beste Lösung.

ScottManDeath
2004-05-19, 18:25:07
Naja, das könntest du machen indem du meinen Ansatz noch etwas aufborst. Du benötigst eine Factory Klasse dir spezialisierte Objekte eines Typs erschafft. Dieser Factory übergibst du Prototypen für konkrete Objekte einer Klasse (z.b. Langschwert +1, Kurzschwert +3) in der einen Factory ( Rüstung +2, Lederpanzer) in der anderen.und eine Wahrscheinlichkeit für das auftreten

Dann hast du eine Liste mit z.b. 4 Factories, eine für jeden Subtyp. Z.B. einde WeaponFactory, eine PotionFactory, eine ArmorFactory.

Aus dieser wählst du eine zufällig aus (1D4) und sagst dieser Factory dass sie dir ein Objekt ihres Typs erstellen soll. Diese wählt dann entsprechend der Wahrscheinlichkeit einen ihrer Prototypen aus und liefert ihn zurück. Es ist ganz einfach neue Objekttypen zu erstellen indem man der Factory einfach ein neues Prototyp Objekt hinzufügt
Neue Itemklassen bekommst du indem du eine neue Factory machst und mit anderen Protoypen füllst



class Item
{
Item* Clone() =0;
}

class Factory

{
virtual Item* CreateRandomItem()
{
// wahrscheinlichkeit beachten !! zu faul jetzt ;)
return m_protoypes[random()]->Clone();
}
void AddProtoype(Item*, float p);

list<Item*> m_prototypes;

}


class Sword: public Item
{
int AttackModifier;
string Name;
}


class TreasureMaker
{

void AddFactory(Factory* f);
Item* MakeRandomItem()
{
return m_factories[random]->CreateRandomItem();
}


list<Factory*> m_factories;

}

Sword longsword;
longsword.Name = "Langschwert"
longsword.AttackModifier = 1;

Sword shortsword;
shortsword.Name = "Kurzschwert"
shortsword.AttackModifier1;

Factory swordfactory
swordfactory.AddPrototpe(&longsword, 0.2);
swordfactory.AddPrototpe(&shortsword, 0.5);

Factory potionfactory
// mit prototypen füllen .....

TreasureMaker tm;

tm.AddFactory(&swordfactory);
tm.AddFactory(new PotionFactory);



Ist sozusagen eine Kombination aus dem Prototype und dem Factory Pattern.


Ich würde für eine weitergehende Verarbeitung (wie z.b. die Itemeigenschaften im GUI ändern) auch das double dispatch Pattern verwenden.


Das ganze lässt sich beliebig schachteln, z.b. kann die WeaponFactory wieder verschiedene Facories enthalten (LongSwordFactory, DaggerFactory...) die dann erst die konkreten Prototypen enthalten.


Ich hab grade Design Patterns nicht zu hand, viell hilft für das schachteln auch das Decorator pattern.

HellHorse
2004-05-20, 00:46:39
Ok, visitor pattern ahoi

public interface ItemVisitor {
public void visitWeapon(Weapon weapon);
public void visitPotion(Potion potion);
}

public abstract class Item {
public abstract void accept(ItemVisitor visitor);
}

public class Weapon extends Item {
public void accept(ItemVisitor visitor) {
visitor.visitWeapon(this);
}
}

public class Potion extends Item {
public void accept(ItemVisitor visitor) {
visitor.visitPotion(this);
}
}

public class Player implements ItemVisitor{
public void addTresureItem(Item item) {
item.accept(this);
}

public void visitPotion(Potion potion) {
//mach was damit
}

public void visitWeapon(Weapon weapon) {
//mach was damit
}
}


Das Ganze macht nur Sinn, wenn du relativ wenige Item-Klassen hast und die Item Hierarchie stabil ist.

aths
2004-05-20, 17:28:38
Die Sache ist nicht ganz so einfach :)

Es kann sein, dass auch auf magische Eigenschaften gewürfelt wird. Hierfür sind extra-Prüfungen notwendig: Solche Eigenschaften dürfen sich nicht wiedersprechen, und vom Wert her einen Maximalbonus nicht überschreiten.

Was die Weiterverarbeitung angeht, wäre es sinnvoll, wenn man Schätze addieren kann. Das heißt, dass er z. B. das Geld zusammenzählt. Ein einzelner Geldhaufen besteht immer aus einer einzigen Münzenart (Kupfer, Silber, Gold, Platin) und hat eine bestimmte Anzahl Münzen. Für die Schatz-Addition müsste noch der Item-Basistyp gespeichert werden.

Ein Schatz besteht aus bis zu drei Komponenten: Geld, Goods, Items. Items können mundane oder magic Items sein, wobei es minor, normal und major magic gibt. Goods kann entweder gems oder art sein.

Die Sache ist also leider nicht ganz so einfach. Ein genereller Itemtyp würde wohl so aussehen:

- Name
- Beschreibung
- Klasse*
- Grundtyp**
- Wert***
- Magie-Eigenschaften+
- Magie-Bonus++
- Magie-Gesamtbonus+++

* Klasse = Coins oder Goods oder Items
** Grundtyp = Gems/Art, mundane/minor/normal/major
*** Wert besteht aus 4 Integer-Werten: Kupfer, Silber, Gold, Platin.

+ Magie-Eigenschaften speichert (als String) was die Waffe alles kann.
++ Der Bonus gibt an, ob +1, +2, ... +5.
+++ Gesamtbonus: Eigenschaften geben einen "effektiven" Zusatzbonus, der zur Bestimmung des Wertes herangezogen wird. Der Gesamtbonus kann bis +10 gehen, während mehr als +5 an "direktem" Bonus nicht erlaubt ist.

Jedes Würfeln auf eine Grund-Tabelle wird mit einem D100 (Prozentwurf) gemacht. Das heißt z. B., 1-18 = dies, 19-36 = jenes etc.

zeckensack
2004-05-20, 18:50:35
Oh weh, die OOP-Kavallerie in voller Aktion. Als nächstes schlägt dir noch jemand vor, das alles erstmal in UML zu modellieren und use cases aufzustellen :freak:

Ich bekomm's hier echt mit der Angst zu tun, wenn ich den bisherigen Verlauf des Threads lese.

Ich sehe folgende Anforderungen an das Design:
Ein konkretes Objekt muss einer evtl vorhandenen Gameplay-Mechanik mitteilen können, was es überhaupt ist, was es kann, welche Anforderungen es hat, was es wert ist usw.

Das Objekt muss in gewissen Grenzen parametrisch sein. IMO sollte man eine Deterministik anstreben, dh dass zwei Objekte mit exakt gleichen Eigenschaften auch in ihrer Datenrepräsentation exakt gleich sind, und umgekehrt.

Daraus ergibt sich auch, dass ich es für keine gute Idee halte, eine menschenlesbare Beschreibung als String innerhalb des Objekts zu speichern. Die kann man immer wenn sie gebraucht wird generieren, und dann kann man sich überlegen, ob man diese Information in einen Cache packen will.

Ich weiss jetzt nicht genau woran dein Vorhaben scheitert.
Ich würde aber generell einen Weg ohne Hierarchien von abstrakten Klassen, Interfaces, Reflection etc gehen. Stattdessen würde ich Metadaten vorschlagen, damit erreicht man exakt das selbe, hat aber viel weniger Kopfschmerzen bei der Implementation, alles ist am richtigen Platz, Debuggen funktioniert wie erwartet, man braucht viel weniger Speicher (konstanter Speicherverbrauch pro Objekt!), und die Performance ist auch noch besser.

Das sähe dann so aus, dass du einige Datenfelder innerhalb der Klasse für "unbekannte" Zwecke reservierst. Deren Sinn ergibt sich über ein anderes, übergeordnetes Datum, und da sind wir auch schon bei den Metadaten.

ZB ein byte für einen Grundtyp. Waffe, Rüstung, Schriftstück, Alchimie-Zutat etc.
Beim Grundtyp Waffe würden die "unbekannten" Daten nun eine andere Bedeutung tragen als beim Grundtyp Alchimie-Zutat.
Wenn die Gameplay-Mechanik das konkrete Objekt nun fragt, ob es im linken Waffenslot des Charakters angelegt werden kann, würde man beim Grundtyp AZ "nö" antworten, beim Grundtyp Waffe müsste man dann tiefer graben.

Das Konzept mit den Metadaten kann man selbstverständlich auch schachteln.

aths
2004-05-21, 00:53:12
Theoretisch könnte man das auch mit einer SQL-Datenbank lösen :)

zs, wäre es nicht besser, statt Byte eine Enumeration zu verwenden? Obwohl, das Problem ist, ersten will ich nicht unbedingt jedes Unteritem mit einer eigenen ID versehen, andererseits weiß man ja erst wenn der 1. Enum-Typ bekannt ist, welcher Typ als 2. kommt ... hm.

In der Liste oben vergaß ich eine wichtige Property: Anzahl. Oft werden mehrere Items (z. B. Phiolen mit brennbarer Flüssigkeit etc.) auf einen Schlag generiert. Ich habe noch keine D&D-Regel gefunden, ob im Fall 1D4 mundane Items, und angenommen 2x wird das gleiche gewürfelt, dann erneut gewürfelt wird oder man die Items einfach zusammenzählt. Jedenfalls ist es so oder so günstig, am Ende gleichartige Items zusammenfassen zu können.

Erstes Problem hier: Soll man die Items in einem Typ speichern, oder in Ober- und Untertyp?

Zweites Problem: Beschreibung. Dort steht eigentlich der Typ. Beispiel: Name: Breitschwert, Beschreibung: Waffe. Soll hier nun gleich "Nahkampfwaffe" spezifiziert werden?

Das muss ich erst mal gründlich durchdenken. Die Tabellenhierarchie wäre so: Mundane - Weapon - Masterwork - Common (oder eben uncommon) - Mellee

Dabei ist "Common" oder "Uncommon" nur ein Verweis auf jeweils eine andere Tabelle, aber Masterwork "effektiv" ein magisches Präfix (obwohl Masterwork alleine noch nicht magisch ist, allerdings jede magische Waffe ist gleichzeitig Masterwork. Nur hat eine Masterwork-Waffe bereits einen Bonus, weshalb man es für die Mechanik als Präfix nehmen kann.

Lustig wird es mit den Suffixen. Sofern magisch, wird "Masterwork" nicht mehr geschrieben, stattdessen minor, medium oder major. Nun gibt es nicht nur Suffixe, sondern auch Präfixe für Suffixe :freak: Möglich wäre z. B. ein Major Broadsword of Greater Acid Resistance. (Greater ist das Präfix vom Suffix.)

Richtig lustig wird es aber erst jetzt: Ein Item kann bis zu zwei Suffixe haben. Die meisten Suffixe haben einen Bonus. Man muss darauf achten, dass der Gesamtbonus der Waffe +10 nicht überschreitet. Wird 2x das gleiche Suffix gewürfelt, zählt nur eins. (Dann gibt es kein zweites.) Wird einmal Acid Resistance und einmal Improved Acid Resistance gewürfelt, zählt nur Improved Acid Resistance.

Coins und mundane Items zu generieren wäre kein Problem. Auch normale Waffen gingen noch. Dieses Magie-Zeug scheint mir jedoch etwas aufwändiger.

zeckensack
2004-05-21, 02:16:37
Original geschrieben von aths
Theoretisch könnte man das auch mit einer SQL-Datenbank lösen :)Aber du musst zwingend COM+ benutzen, am besten über VBScript. Oder als Winamp-Plugin. Die Musik könnte man dann als "Zufallszahlen" benutzen :D
zs, wäre es nicht besser, statt Byte eine Enumeration zu verwenden?Äh, ja, da gibt's ja auch keine wirklich wichtigen Unterschiede.
Obwohl, das Problem ist, ersten will ich nicht unbedingt jedes Unteritem mit einer eigenen ID versehen, andererseits weiß man ja erst wenn der 1. Enum-Typ bekannt ist, welcher Typ als 2. kommt ... hm.1. Solltest du die Anzahl an Grundtypen auf ein Minimum beschränken. Dadurch wird das ganze automatisch überschaubar.
Andererseitsconst int base_type_weapon=0;
const int base_type_armor=1;
<...>


static const char*
base_weapon_description[]=
{
"Kurzschwert",
"Breitschwert",
"Krummschwert",
"Flachschwert",
"Geradeschwert",
"Schmalschwert",
"Fettschwert"
};

static const char*
armor_material_description[]=
{
"Ziegenleder",
"Plüsch",
"Leopardenfell",
"Katzendarm",
"Brokat"
};

static const char*
armor_part_description[]=
{
"mantel",
"röckchen",
"caprihose",
"handschuhe",
"stiefeletten"
};


void
Item::describe(char* output_buffer)
{
char temp_prefix_buffer[256];
char temp_suffix_buffer[256];
int sub_type=data0;

switch(base_type)
{
case(base_type_armor):
int material=data1;
get_armor_prefix(temp_prefix_buffer);
get_armor_suffix(temp_suffix_buffer);
sprintf(output_buffer,"%s %s%s %s",
temp_prefix_buffer,
armor_material_description[material],
armor_part_description[sub_type],
temp_suffix_buffer);
break;

case(base_type_weapon):
int bonus_to_hit=data1;
get_weapon_prefix(temp_prefix_buffer);
get_weapon_suffix(temp_suffix_buffer);
if (bonus_to_hit!=0)
{
sprintf(output_buffer,"%s %s %+d %s",
temp_prefix_buffer,
weapon_description[material],
bonus_to_hit,
temp_suffix_buffer);
}
else
{
sprintf(output_buffer,"%s %s %s",
temp_prefix_buffer,
weapon_description[material],
temp_suffix_buffer);
}
break;
default:
sprintf(output_buffer,"kaputt");
break;
}
}uswusf
Es sind quasi alles nur Indizes in irgendwelche Tabellen. Auch Waffenschaden, Rüstungsschutz und diverse Boni durch Prä- und Suffixe kann man mit Tabellen lösen. Wenn man diese Tabellen aus (Text-)dateien liest, wird das ganze extern konfigurierbar, man muss den Code dann nicht mehr anfassen.

In der Liste oben vergaß ich eine wichtige Property: Anzahl. Oft werden mehrere Items (z. B. Phiolen mit brennbarer Flüssigkeit etc.) auf einen Schlag generiert. Ich habe noch keine D&D-Regel gefunden, ob im Fall 1D4 mundane Items, und angenommen 2x wird das gleiche gewürfelt, dann erneut gewürfelt wird oder man die Items einfach zusammenzählt. Jedenfalls ist es so oder so günstig, am Ende gleichartige Items zusammenfassen zu können.Das dürfte keine grosse Schwierigkeit darstellen :)
Leg einfach ein Datenfeld "Anzahl" in der Klasse an, typunabhängig. Ob gewisse Items nicht stapelbar sein sollen, kannst du später noch entscheiden, ggf auch wieder über Tabellen.
Erstes Problem hier: Soll man die Items in einem Typ speichern, oder in Ober- und Untertyp?Designfrage: gibt es voraussichtlich viele Stellen in deinem System, an dem du nur das eine, nicht aber auch gleichzeitig das andere brauchst? IMO eher nicht, aber das wirst du selbst am besten wissen.
Zweites Problem: Beschreibung. Dort steht eigentlich der Typ. Beispiel: Name: Breitschwert, Beschreibung: Waffe. Soll hier nun gleich "Nahkampfwaffe" spezifiziert werden?IMO ist diese Information nur für die Spielmechanik interessant, kaum aber für den User - ein Minimum an Phantasie vorausgesetzt ;)
Mir gefällt das bei NWN ganz gut, dort wird man nicht mit dem offensichtlichen genervt. Die "Beschreibung" dient hier mehr als Hintergrundinfo oder Lokalkolorit ala "Diese Art von Schwertern wurde zuerst von der Mannschaft um Ignaz den Breiten popularisiert, der damit zur Zeit der Vegetareliten die berüchtigten Dünnmaden aus dem Speck vertrieb. Diese Schwerter sind in der Tat breiter als ihr Anblick vermuten lässt, was vor allem dann offensichtlich wird, wenn man orthogonal auf die Schneidfläche blickt."
Das muss ich erst mal gründlich durchdenken. Die Tabellenhierarchie wäre so: Mundane - Weapon - Masterwork - Common (oder eben uncommon) - MelleeIst IMO wieder intern unwichtig. Intern wichtig ist Grundtyp, Menge und Art(en) von Schaden, Menge und Art(en) von Schutz, Attributs-Boni, Anforderungen zum Anlegen (wenn überhaupt), wo kann es angelegt werden, was passiert wenn man es isst/trinkt, was passiert wenn man es wirft, etc. Jedoch nicht wie es heisst.
<gekürzt>
Richtig lustig wird es aber erst jetzt: Ein Item kann bis zu zwei Suffixe haben. Die meisten Suffixe haben einen Bonus. Man muss darauf achten, dass der Gesamtbonus der Waffe +10 nicht überschreitet. Wird 2x das gleiche Suffix gewürfelt, zählt nur eins. (Dann gibt es kein zweites.) Wird einmal Acid Resistance und einmal Improved Acid Resistance gewürfelt, zählt nur Improved Acid Resistance.
<gekürzt>Du kannst beim zufälligen Erzeugen von zusätzlichen Eigenschaften ein Budget "anlegen", und für jeden gewürfelten Bonus von diesem Budget einen Betrag abziehen (der natürlich dem relativen "Wert" dieses Bonus entsprechen sollte). Geht das Budget dadurch unter Null, kann sich der Item-Generator diesen Bonus schlicht nicht leisten. Er muss entweder einen Kredit aufnehmen :freak:
*hust*
... oder er muss es bleiben lassen :)

Crushinator
2004-05-21, 11:46:23
Wenn ich mir die Anforderungen dessen durchlese, was aths eigentlich möchte, muß ich Zecki und anderen zustimmen, daß hierfür Tabellen bzw. arrays mehr taugen, weil sie einfacher zu implementieren sind. Es stellt sich nur die Frage, ob Ziel der Übung "das bessere Verstehen" des Klassenkonzeptes sei oder "technisch gesehen möglichst beste Art der Anforderungserfüllung".

aths
2004-05-21, 13:19:15
Hallo zecki,

das Programm sollte zunächst folgendes können:

- Würfele mir soundso viele Schätze der Herausforderungstufe XYZ (1-20.)

Oder, angenommen ich brauche einfach 4 mundane tools&gears:

- Würfele mir 4 mundane tools&gears.

Oder einfach: Würfele mir 20 beliebige mundane Items. (Wobei hier als Item auch Itemhaufen zählen, so werden bestimmte Phiolen lt. Tabelle immer mit einer Zufalls-Anzahl vergeben.)

Geld und mundane Items wären recht einfach zu implementieren. Ich überlege, vielleicht einfach mal damit anzufangen, es ist ja egal wie diese Items erzeugt werden, sofern der Datentyp für Items ausreicht, um später ggf. mit einer anderen Methode noch magische Items zu erwürfeln.

Du hast recht, dass minor, medium und major überflüssig zu speichern ist. Dem Item selbst merkt man das nämlich gar nicht mehr an :) Das +2 Langschwert hat keine Ahnung mehr, ob es über minor oder medium erwürfelt wurde.

Was die magischen Eigenschaften angeht, halte ich die Regeln für unklar, da informiere ich mich jetzt erst mal in einem D&D-Forum.

aths
2004-05-21, 13:24:48
Original geschrieben von crushinator
Wenn ich mir die Anforderungen dessen durchlese, was aths eigentlich möchte, muß ich Zecki und anderen zustimmen, daß hierfür Tabellen bzw. arrays mehr taugen, weil sie einfacher zu implementieren sind. Es stellt sich nur die Frage, ob Ziel der Übung "das bessere Verstehen" des Klassenkonzeptes sei oder "technisch gesehen möglichst beste Art der Anforderungserfüllung". Mir kam die Idee, dass "Items" i. A. toll für ein Klassenkonzept geeigent sein müssten. Die meisten Beispiele legen los mit Tisch

- Höhe
- Breite
- Länge
- Anzahl Beine

oder so. Eine generelle Item-Klasse mit Ableitungen wäre imo interessanter, zumal der Praxisbezug zur D&D 3.5 besteht.

zeckensack
2004-05-21, 16:08:22
aths,
Wenn Sinn und Zweck dieser Übung tatsächlich die Übung in OOD sein soll, dann habe ich dich bisher sowieso grob missverstanden. Sorry ;(

Deine Würfel-Logik soll also garnicht Teil eines übergeordneten Spiels werden, sondern bestenfalls ein Werkzeug für Pen&Paper AD& D? Also pur die Anzeige des gewürfelten Schatzes. Korrekt?


Welche Sprache möchtest du eigentlich verwenden? Ist das deine angedrohte (;)) Einarbeitung in C++?

aths
2004-05-21, 18:10:56
Original geschrieben von zeckensack
Deine Würfel-Logik soll also garnicht Teil eines übergeordneten Spiels werden, sondern bestenfalls ein Werkzeug für Pen&Paper AD& D? Also pur die Anzeige des gewürfelten Schatzes. Korrekt?Reine Anzeige ist das Primärziel, ja.

Vielleicht könnte man das dereinst zu einem generellen D&D-Outfit-System erweitern, wo man die Sachen auch anziehen bzw. im Rucksack/Gürtel verstauen kann.

Irgendwann schrieb ich mal einen Würfler, der die 6 Primärwerte STR, DEX, CON, INT, WIS und CHA nach den Regeln auswürfelt. Jetzt braucht man nur noch auf diese fertige Tabelle zu würfeln und hat sofort Werte nach den Rules :)
Original geschrieben von zeckensack
Welche Sprache möchtest du eigentlich verwenden? Ist das deine angedrohte (;)) Einarbeitung in C++? Nach dem ich versucht habe, Klassen in Delphi zu verwenden, denke ich dass der C++Builder das optimale Werkzeug sein dürfte. Da habe ich zu Ein/Ausgabe VCL, und brauche auf echte, gekapselte Klassen nicht zu verzichten.

Beispielsweise braucht ein Item die Methode "Beschreibe dich selbst".

Oder eine Methode "Addiere eine gegebene magische Eigenschaft". Die Methode prüft dann, ob das überhaupt zulässig ist.

aths
2004-05-23, 23:15:52
Puh, das ist aufwändiger als ich dachte, denn es gibt deutlich mehr Items, als ich dachte. Zum Beispiel massenhaft Scrolls (Schriftrollen.) Wenigstens ist hier das Auswürfeln einfach: Man würfelt einfach auf eine Tabelle, wobei mit "minor, medium oder major" die Auswahl getroffen wird, welche Rollen wie häufig kommen bzw. welche überhaupt im Schatz sein können.

Es wäre ja zu überlegen, in diesem Zuge eine Art Itemdatenbank zu ermöglichen. Deshalb werde ich wohl eine Eigenschaft "ID" einführen, die sich zusammensetzt aus der Tabelle und welcher Wurf für das Item erforderlich ist. Bei magischen Items geht das mit der ID nicht so einfach, das heißt das Item könnte eine ID kriegen, diese berücksichtigt dann aber nicht die magischen Eigenschaften.

Vermutlich werde ich irgendwann mal einen kompletten Plan schreiben, wie gewürfelt wird. Und dann abschätzen, ob man das programmieren kann.

PH4Real
2005-02-18, 13:05:34
*alten Thread ausgrab*
Habe durch Zufall diesen Thread gefunden und musste feststellen, dass hier ja noch irgendwie keine richtige Lösung gefunden wurde ;). Ich hatte vor einiger Zeit mal versucht ein Item System ähnlich von Diablo zu bauen. Vielleicht ist ja deine Arbeit noch einigermaßen aktuell und das kann Dir vielleicht helfen...

Deswegen mach ich auch mal ein Vorschlag, damit es auch eine OO-Lösung gibt:
DECORATOR-PATTERN (jaja ich weiss, noch ein Pattern)

Grundidee:

Es gibt verschiedene Waffen, die alle von der abstrakten Klasse "Weapon" erben.
Dazu kann man die Waffen "decorieren". Zum Beispiel mit FireDamage, Lightning Damage ... , so dass alle zusätzlichen Eigenschaften in Decorators gekapselt sind.


Auch wenn nicht gerne gesehen poste ich einfach mal ein UML-Klassendiagramm zur Verdeutlichung:
http://www-public.tu-bs.de:8080/~y0018468/java/defaultpackage.jpg

Also eine abstrakte Klasse "Weapon", von der der abstrakte Decorator erbt und auch eine Weapon als Bestandteil hat. Dieser überschreibt auch fast alle Methoden der BasisWeapon Klasse. Warum? Sehen wir gleich.

Den Decorator "wrappt" man dann quasi über seine normalen Items. Dieser Überschreibt entsprechend Prefix und Postfix des Namens und fügt den Damage Schaden hinzu (das ist natürlich nur beispielhaft).

Also erstmal als Beispiel die Klasse "Axe":



public class Axe extends Weapon {

public Axe() {
// base damage between 12 and 20
this.baseDamage = 12 + rand.nextInt(8);
}

public Axe(int baseDamage) {
this.baseDamage = baseDamage;
}

/* (non-Javadoc)
* @see weapon.Weapon#getName()
*/
public String getName() {
return "Axe";
}
}

Klasse "WeaponDecorator":

public abstract class WeaponDecorator extends Weapon {

protected Weapon weapon;


/* (non-Javadoc)
* @see weapon.Weapon#getName()
*/
public String getName() {
return weapon.getName();
}

/* (non-Javadoc)
* @see weapon.Weapon#getBaseDamage()
*/
public int getBaseDamage() {
return weapon.getBaseDamage();
}

[...]
}


Und zuletzt z.B. das Deco "Lightning":

public class Lightning extends WeaponDecorator {

private int lightningDamage;

public Lightning(final Weapon weapon, final int lightningDamage) {
this.weapon = weapon;
this.lightningDamage = lightningDamage;
}

public Lightning(final Weapon weapon) {
// Damage between 1-20
this(weapon, 1 + rand.nextInt(19));
}

/* (non-Javadoc)
* @see weapon.Weapon#getLightningDamage()
*/
public int getLightningDamage() {
return weapon.getLightningDamage() + lightningDamage;
}

/* (non-Javadoc)
* @see weapon.Weapon#getPostfix()
*/
public String getPostfix() {
String postfix = "";
if (lightningDamage <= 6)
postfix = "of Shock";
else if (lightningDamage > 6 && lightningDamage <= 10)
postfix = "of Lightning";
else
postfix = "of Thunder";
return " " + postfix;
}
}


So bevor Du jetzt denkst, dass es viel viel zu kompliziert ist, jetzt die Benutzung. Diese ist nämlich wirklich schön einfach :) :


public static void main(final String[] args) {
// Create a normal Axe and print it
Weapon normalAxt = new Axe();
System.out.println(normalAxt.toString());

// Create a Sword
Weapon fireSword = new Sword();
// Add 25% fire damage and voila :)
fireSword = new Fire(fireSword, .25);
System.out.println(fireSword.toString());

// Create another axe
Weapon veryGoodAxe = new Axe(20);
// Add 80% fire damage
veryGoodAxe = new Fire(veryGoodAxe, .80);
// plus 17 lightning damage points
veryGoodAxe = new Lightning(veryGoodAxe, 17);
// our almighty axe is done with wicked 53 overall damage :)
System.out.println(veryGoodAxe.toString());

// Create 10 random items:
System.out.println("\n10 Random Weapons:");
for (int i = 0; i < 10; i++) {
System.out.println(WeaponFactory.createRandomWeapon());
}
}


Da kommt dann zum Beispiel sowas raus:

Weapon: Axe DMG: 19 BASE: 19: LGT: 0 FIR: 0
Weapon: Flame Sword DMG: 13 BASE: 11: LGT: 0 FIR: 2
Weapon: Flaming Axe of Thunder DMG: 51 BASE: 19: LGT: 17 FIR: 15

10 Random Weapons:
Weapon: Sword DMG: 6 BASE: 6: LGT: 0 FIR: 0
Weapon: Flame Sword DMG: 8 BASE: 6: LGT: 0 FIR: 2
Weapon: Flame Sword of Thunder DMG: 21 BASE: 6: LGT: 13 FIR: 2
Weapon: Flaming Axe DMG: 27 BASE: 14: LGT: 0 FIR: 13
Weapon: Axe DMG: 12 BASE: 12: LGT: 0 FIR: 0
Weapon: Axe of Lightning DMG: 23 BASE: 13: LGT: 10 FIR: 0
Weapon: Fire Axe DMG: 22 BASE: 13: LGT: 0 FIR: 9
Weapon: Flaming Sword DMG: 13 BASE: 7: LGT: 0 FIR: 6
Weapon: Fire Axe DMG: 21 BASE: 12: LGT: 0 FIR: 9
Weapon: Fire Sword DMG: 12 BASE: 7: LGT: 0 FIR: 5

Dabei macht die WeaponFactory quasi nichts anderes als immer zu würfeln und dann die entsprechende Klasse zu nehmen also sowas:
public class WeaponFactory {

public static Weapon createRandomWeapon() {
double decisionMaker = Math.random();

// Choose Weapon (33% sword, 77% axe)
Weapon weapon;
if (decisionMaker <= 0.33)
weapon = new Sword();
else
weapon = new Axe();

// Add weapon decorators

decisionMaker = Math.random();
// Fire damage: 50% probability
if (decisionMaker <= .5)
weapon = new Fire(weapon);

decisionMaker = Math.random();
// Lightning damage: 20% probability
if (decisionMaker <= .2)
weapon = new Lightning(weapon);

return weapon;
}
}

Wenn das jetzt alles zu schnell bzw. zu kompliziert war, es gibt ein sehr anschauliches Beispiel dafür: Decorator Pattern (Kapitel 3) O'Reilly (http://www.oreilly.com/catalog/hfdesignpat/chapter/ch03.pdf)

Die behandeln zwar Kaffee, Tee mit den "Decoratorn" Milch und Sahne, aber es kommt das gleiche bei heraus ;).

Ansonsten einfach nachfragen. Hoffe es ist einigermaßen klar geworden. Viel Spass damit.

zeckensack
2005-02-20, 10:13:45
PH4Real,
an deinem Beispiel sieht man schön warum eine Klassenhierarchie so richtig shice ist ;)

Du schreibst nun also die Klasse Axe. Und die Klasse Sword. Und die Klassen Mace, Club, Staff, Hammer, Whip, Spear, Bow, Sling, Crossbow, Nunchaka, etc pp.

Größere RPGs haben mindestens 50 verschiedene Waffengrundtypen und mindestens 30 verschiedene optionale Boni. Nach oben offen.

Du musst nun für jeden Scheiß den Code erweitern und einige "dumme" Zeilen schreiben um eine neue Klasse einzufügen.

Da kommt dann zum Beispiel sowas raus:

Weapon: Axe DMG: 19 BASE: 19: LGT: 0 FIR: 0
Weapon: Flame Sword DMG: 13 BASE: 11: LGT: 0 FIR: 2
Weapon: Flaming Axe of Thunder DMG: 51 BASE: 19: LGT: 17 FIR: 15

10 Random Weapons:
Weapon: Sword DMG: 6 BASE: 6: LGT: 0 FIR: 0
Weapon: Flame Sword DMG: 8 BASE: 6: LGT: 0 FIR: 2
Weapon: Flame Sword of Thunder DMG: 21 BASE: 6: LGT: 13 FIR: 2
Weapon: Flaming Axe DMG: 27 BASE: 14: LGT: 0 FIR: 13
Weapon: Axe DMG: 12 BASE: 12: LGT: 0 FIR: 0
Weapon: Axe of Lightning DMG: 23 BASE: 13: LGT: 10 FIR: 0
Weapon: Fire Axe DMG: 22 BASE: 13: LGT: 0 FIR: 9
Weapon: Flaming Sword DMG: 13 BASE: 7: LGT: 0 FIR: 6
Weapon: Fire Axe DMG: 21 BASE: 12: LGT: 0 FIR: 9
Weapon: Fire Sword DMG: 12 BASE: 7: LGT: 0 FIR: 5Mit solchen Listen (nun, natürlich nicht mit exakt dieser ...) sollte man IMO so ein System antreiben, und nicht mit hunderten Zeilen Code.

Demirug
2005-02-20, 10:31:12
PH4Real,
an deinem Beispiel sieht man schön warum eine Klassenhierarchie so richtig shice ist ;)

Du schreibst nun also die Klasse Axe. Und die Klasse Sword. Und die Klassen Mace, Club, Staff, Hammer, Whip, Spear, Bow, Sling, Crossbow, Nunchaka, etc pp.

Größere RPGs haben mindestens 50 verschiedene Waffengrundtypen und mindestens 30 verschiedene optionale Boni. Nach oben offen.

Du musst nun für jeden Scheiß den Code erweitern und einige "dumme" Zeilen schreiben um eine neue Klasse einzufügen.

Mit solchen Listen (nun, natürlich nicht mit exakt dieser ...) sollte man IMO so ein System antreiben, und nicht mit hunderten Zeilen Code.

Da muss ich dir recht geben. Sinnvollerweise in diesem Fall mit zwei Tabellen. Eine für die Grundwaffen und eine für die Boni. Unter Umständen noch eine Dritte wenn es komplexere Regelen für das Verknüpfen von Grundtypen mit den Bonies gibt.

Der Vorteil ist das die Designer das System um neue Waffen erweitern können und auch das Balencing verändern können. Gibt man ihnen diese Freiheit nicht stehen sie einem nämlich ständig auf der Matte wenn sie was geändert haben wollen.

Von Vorteil in einem echten Spiel ist es wenn die Designer das alles Online verändern können ohne jedesmal komplett neu starten zu müssen. Aber das ist ein anderes Thema.

PH4Real
2005-02-20, 13:02:03
@zeckensack und Demirug:

Nachteil dieses Patterns: ClassExplosion ;)

Bei ca. 50 Waffen und 30 Boni gibt es wahrscheinlich mehr Unübersicht als Klarheit. Und es ist natürlich wirklich nicht besonders schön, wenn man die Grundtypen von "außerhalb" ändern will.

Wobei man dieses Problem auch mit anderen Designs hat:

Du musst nun für jeden Scheiß den Code erweitern und einige "dumme" Zeilen schreiben um eine neue Klasse einzufügen.

Da muss mann sich natürlich fragen was besser ist. Wenn ich die Typen in enums oder sowas speicher, und damit auf Tabellen zugreife, müsste ich beim Hinzufügen eines neuen Grundtypen sogar bestehenden Code modifizieren und kann nicht einfach irgendwo eine neue Klasse generieren, und den alten Code unangetastet lassen.

Die Klassenhierachie dient hier natürlich nur der Typisierung (sprich, es steckt nicht viel zusätzliche Logik zum Beispiel in der Klasse "Axe" drin). Das Decorator Pattern würde ich aber dennoch anwenden wollen, jedoch bei dutzenden Items und Boni abgewandelt:

Statt Sword, Axe usw. eine einzige Weapon (oder sogar Item) Klasse benutzen, die dann quasi Platzhalter für alle spielt. Oder eine Platzhalterklasse für jeden Grundtyp.
Statt den verschiedenen Decoratorn auch hier wieder einen Generischen.


Dann hat man nur noch drei Klassen und könnte sich dann die konkreten Waffen und Boni aus irgendwelchen Tabellen erzeugen (und man dann drei Tabellen benutzt, wie Demirug vorschlägt, wobei dann die dritte die Beziehungen zwischen Waffen und Boni angibt, können wir gleich den Kram in eine Mini DB hauen). Quasi wird dann die Klassenhierchie "outgesourct".

Die Klassifizierung muss dann natürlich anders laufen (am Besten wahrscheinlich über enums) und man hat dann ein Problem bzgl. der konkreten Felder und Methoden der Basisklassen.

Man kommt dann eventuell zu den "leeren" Platzhalterfelder, wie von zeckensack vorgeschlagen, die je nach Typ eine andere Funktion haben.

Meiner Meinung nach ein bißchen unschön, da man dann besonders aufpassen muss, weil dann schon ein "kleiner" Fehler im Programm schwer zu findene Bugs produzieren kann.

Nach dem Motto: "Geil meine Waffe macht 65535 Schaden!" (uups, "reserviertes" Rüstungsfeld ausgelesen; Debug-Session started ;) ).

Trap
2005-02-20, 15:19:59
Passt auf dass ihr keine Klasse Zahl schreibt von der ihr dann die Klasse eins, zwei und so weiter ableitet und dann passende Operationen definiert ;)

Ein Typ ist zwar auch ein Wert, aber nur Typen als Werte zu benutzen ist keine gute Idee. Besonders wenn man eine Programmiersprache benutzt, bei der man nur in einem Parameter polymorph nach dem Typ unterscheiden kann.

Ich würde Schadenstypen oder vielleicht auch alle Eigenschaften als Vektoren behandeln, dann kann man lineare Algebra mit machen. Also ohne Fallunterscheidung addieren, man hat Matrizen zum linearen Transformieren, Skalarprodukte und so weiter.
Das ist zwar nicht die effizienteste Möglichkeit, aber eine sehr generelle.