Files
Bardioc26 95f0fc0b7a editing and maintenance and user suggestions
* every user has a right of a username and a display name and has the right to change it
* System information page now shows information about database user count, char count, and database schema version
* more maintenance lists
* show the right values in columns and fields
* move similar from inside of frontend component functions to utility js when used multiple times
* display help on mouse over
* add more than one believe to character
* make char name editable with better char info in headline
* GiT Gifttoleranz value not calculated correctly
* Bump backend to 0.2.3, frontend to 0.2.2
2026-02-03 17:21:43 +01:00

517 lines
15 KiB
Go

package models
import (
"bamort/database"
"bamort/user"
"fmt"
"gorm.io/gorm"
)
// Au, Gs, Gw ,In, Ko, Pa, St, Wk, Zt
type Eigenschaft struct {
ID uint `gorm:"index" json:"id"`
CharacterID uint `gorm:"primaryKey" json:"character_id"`
UserID uint `gorm:"index" json:"user_id"`
Name string `gorm:"primaryKey" json:"name"`
Value int `json:"value"`
}
type Lp struct {
ID uint `gorm:"primaryKey" json:"id"`
CharacterID uint `gorm:"index" json:"character_id"`
Max int `json:"max"`
Value int `json:"value"`
}
type Ap struct {
ID uint `gorm:"primaryKey" json:"id"`
CharacterID uint `gorm:"index" json:"character_id"`
Max int `json:"max"`
Value int `json:"value"`
}
type B struct {
ID uint `gorm:"primaryKey" json:"id"`
CharacterID uint `gorm:"index" json:"character_id"`
Max int `json:"max"`
Value int `json:"value"`
}
/*
type Gestalt struct {
models.BamortCharTrait
}
*/
type Merkmale struct {
BamortCharTrait
Augenfarbe string `json:"augenfarbe"`
Haarfarbe string `json:"haarfarbe"`
Sonstige string `json:"sonstige"`
Breite string `json:"breite"`
Groesse string `json:"groesse"`
}
type Erfahrungsschatz struct {
BamortCharTrait
ES int `json:"es"` // Erfahrungsschatz
EP int `json:"ep"` // Erfahrungspunkte
}
type Bennies struct {
BamortCharTrait
Gg int `json:"gg"` // Göttliche Gnade
Gp int `json:"gp"` // Glückspunkte
Sg int `json:"sg"` // Schicksalsgunst
}
type Vermoegen struct {
BamortCharTrait
Goldstuecke int `gorm:"column:goldstuecke" json:"goldstücke"` // GS
Silberstuecke int `gorm:"column:silberstuecke" json:"silberstücke"` // SS
Kupferstuecke int `gorm:"column:kupferstuecke" json:"kupferstücke"` // KS
}
type Char struct {
BamortBase
GameSystem string `gorm:"column:game_system;index;default:midgard" json:"game_system"`
GameSystemId uint `json:"game_system_id,omitempty"`
UserID uint `gorm:"index;not null;default:1" json:"user_id"`
User user.User `gorm:"foreignKey:UserID;references:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"user"`
Rasse string `json:"rasse"`
Typ string `json:"typ"`
Alter int `json:"alter"`
Anrede string `json:"anrede"`
Grad int `json:"grad"`
Gender string `json:"gender"`
SocialClass string `json:"social_class"`
Groesse int `json:"groesse"`
Gewicht int `json:"gewicht"`
Herkunft string `json:"origin"`
Glaube string `json:"glaube"`
Hand string `json:"hand"`
Public bool `json:"public"`
// Static derived values (can increase with grade)
ResistenzKoerper int `json:"resistenz_koerper"`
ResistenzGeist int `json:"resistenz_geist"`
Abwehr int `json:"abwehr"`
Zaubern int `json:"zaubern"`
Raufen int `json:"raufen"`
Lp Lp `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"lp"`
Ap Ap `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"ap"`
B B `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"b"`
Merkmale Merkmale `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"merkmale"`
Eigenschaften []Eigenschaft `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"eigenschaften"`
Fertigkeiten []SkFertigkeit `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"fertigkeiten"`
Waffenfertigkeiten []SkWaffenfertigkeit `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"waffenfertigkeiten"`
Zauber []SkZauber `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"zauber"`
Spezialisierung database.StringArray `gorm:"type:TEXT" json:"spezialisierung"`
Bennies Bennies `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"bennies"`
Vermoegen Vermoegen `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"vermoegen"`
Erfahrungsschatz Erfahrungsschatz `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"erfahrungsschatz"`
Waffen []EqWaffe `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"waffen"`
Behaeltnisse []EqContainer `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"behaeltnisse"`
Transportmittel []EqContainer `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"transportmittel"`
Ausruestung []EqAusruestung `gorm:"foreignKey:CharacterID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" json:"ausruestung"`
Image string `json:"image,omitempty"`
}
type CharList struct {
BamortBase
GameSystem string `gorm:"column:game_system" json:"game_system"`
GameSystemId uint `json:"game_system_id"`
UserID uint `json:"user_id"`
Rasse string `json:"rasse"`
Typ string `json:"typ"`
Grad int `json:"grad"`
Owner string `json:"owner"`
Public bool `json:"public"`
}
type FeChar struct {
Char
Git int `json:"git"` // GiftToleranz
CategorizedSkills map[string][]SkFertigkeit `json:"categorizedskills"`
InnateSkills []SkFertigkeit `json:"innateskills"`
}
func (object *Char) TableName() string {
dbPrefix := "char"
return dbPrefix + "_" + "chars"
}
func (object *Char) GetGiftToleranz() int {
//30+(Konstitution/2)
konstitution := object.GetAttributeValue("Ko")
return 30 + (konstitution / 2)
}
func (object *Char) ensureGameSystem() {
gs := GetGameSystem(object.GameSystemId, object.GameSystem)
if gs == nil {
gs = GetGameSystem(0, "midgard")
}
if gs != nil {
object.GameSystemId = gs.ID
object.GameSystem = gs.Name
}
}
func (object *Char) BeforeCreate(tx *gorm.DB) error {
object.ensureGameSystem()
return nil
}
func (object *Char) BeforeSave(tx *gorm.DB) error {
object.ensureGameSystem()
return nil
}
func (object *Char) First(charName string) error {
gs := GetGameSystem(object.GameSystemId, object.GameSystem)
if gs == nil {
gs = GetGameSystem(0, "midgard")
}
err := database.DB.
Preload("User").
Preload("Lp").
Preload("Ap").
Preload("B").
Preload("Merkmale").
Preload("Eigenschaften").
Preload("Fertigkeiten").
Preload("Waffenfertigkeiten").
Preload("Zauber").
Preload("Bennies").
Preload("Vermoegen").
Preload("Erfahrungsschatz").
Preload("Waffen").
Preload("Behaeltnisse").
Preload("Transportmittel").
Preload("Ausruestung").
Where("(game_system = ? OR game_system_id = ?) AND name = ?", gs.Name, gs.ID, charName).
First(&object).Error
if err != nil {
// zauber found
return err
}
return nil
}
func (object *Char) FirstID(charID string) error {
gs := GetGameSystem(object.GameSystemId, object.GameSystem)
if gs == nil {
gs = GetGameSystem(0, "midgard")
}
err := database.DB.
Preload("User").
Preload("Lp").
Preload("Ap").
Preload("B").
Preload("Merkmale").
Preload("Eigenschaften").
Preload("Fertigkeiten").
Preload("Waffenfertigkeiten").
Preload("Zauber").
Preload("Bennies").
Preload("Vermoegen").
Preload("Erfahrungsschatz").
Preload("Waffen").
Preload("Behaeltnisse").
Preload("Transportmittel").
Preload("Ausruestung").
Where("(game_system = ? OR game_system_id = ?) AND char_chars.id = ?", gs.Name, gs.ID, charID).
First(&object).Error
if err != nil {
// zauber found
return err
}
return nil
}
// FindByUserID finds all characters belonging to a specific user
func (object *Char) FindByUserID(userID uint) ([]Char, error) {
var chars []Char
gs := GetGameSystem(object.GameSystemId, object.GameSystem)
if gs == nil {
gs = GetGameSystem(0, "midgard")
}
err := database.DB.
Preload("User").
Preload("Lp").
Preload("Ap").
Preload("B").
Preload("Merkmale").
Preload("Eigenschaften").
Preload("Fertigkeiten").
Preload("Waffenfertigkeiten").
Preload("Zauber").
Preload("Bennies").
Preload("Vermoegen").
Preload("Erfahrungsschatz").
Preload("Waffen").
Preload("Behaeltnisse").
Preload("Transportmittel").
Preload("Ausruestung").
Where("user_id = ? AND (game_system = ? OR game_system_id = ?)", userID, gs.Name, gs.ID).
Find(&chars).Error
if err != nil {
return nil, err
}
return chars, nil
}
func FindSharedCharList(userID uint) ([]CharList, error) {
var chars []CharList
gs := GetGameSystem(0, "midgard")
err := database.DB.Table("char_chars").
Select("char_chars.id, char_chars.name, char_chars.user_id, char_chars.rasse, char_chars.typ, char_chars.grad, char_chars.public, char_chars.game_system, char_chars.game_system_id, COALESCE(NULLIF(users.display_name, ''), users.username) as owner").
Joins("LEFT JOIN users ON char_chars.user_id = users.user_id").
Joins("INNER JOIN char_shares ON char_shares.character_id = char_chars.id").
Where("char_shares.user_id = ? AND (char_chars.game_system = ? OR char_chars.game_system_id = ?)", userID, gs.Name, gs.ID).
Find(&chars).Error
if err != nil {
return nil, err
}
return chars, nil
}
func FindPublicCharList() ([]CharList, error) {
var chars []CharList
gs := GetGameSystem(0, "midgard")
err := database.DB.Table("char_chars").
Select("char_chars.id, char_chars.name, char_chars.user_id, char_chars.rasse, char_chars.typ, char_chars.grad, char_chars.public, char_chars.game_system, char_chars.game_system_id, COALESCE(NULLIF(users.display_name, ''), users.username) as owner").
Joins("LEFT JOIN users ON char_chars.user_id = users.user_id").
Where("char_chars.public = ? AND (char_chars.game_system = ? OR char_chars.game_system_id = ?)", true, gs.Name, gs.ID).
Find(&chars).Error
if err != nil {
return nil, err
}
return chars, nil
}
// FindCharListByUserID finds all characters belonging to a specific user for listing (minimal data)
func FindCharListByUserID(userID uint) ([]CharList, error) {
var chars []CharList
gs := GetGameSystem(0, "midgard")
err := database.DB.Table("char_chars").
Select("char_chars.id, char_chars.name, char_chars.user_id, char_chars.rasse, char_chars.typ, char_chars.grad, char_chars.public, char_chars.game_system, char_chars.game_system_id, COALESCE(NULLIF(users.display_name, ''), users.username) as owner").
Joins("LEFT JOIN users ON char_chars.user_id = users.user_id").
Where("char_chars.user_id = ? AND (char_chars.game_system = ? OR char_chars.game_system_id = ?)", userID, gs.Name, gs.ID).
Find(&chars).Error
if err != nil {
return nil, err
}
return chars, nil
}
func (object *Char) Create() error {
object.ensureGameSystem()
err := database.DB.Transaction(func(tx *gorm.DB) error {
// Save the main character record
if err := tx.Create(&object).Error; err != nil {
return fmt.Errorf("failed to save Lookup: %w", err)
}
return nil
})
return err
}
func (object *Char) Delete() error {
err := database.DB.Transaction(func(tx *gorm.DB) error {
// delete the main character record
//should cascade for all elements
if err := tx.Delete(&object).Error; err != nil {
return fmt.Errorf("failed to delete char: %w", err)
}
return nil
})
return err
}
func (object *Eigenschaft) TableName() string {
dbPrefix := "char"
return dbPrefix + "_" + "eigenschaften"
}
func (object *Lp) TableName() string {
dbPrefix := "char"
return dbPrefix + "_" + "health"
}
func (object *Ap) TableName() string {
dbPrefix := "char"
return dbPrefix + "_" + "endurances"
}
func (object *B) TableName() string {
dbPrefix := "char"
return dbPrefix + "_" + "motionranges"
}
func (object *Merkmale) TableName() string {
dbPrefix := "char"
return dbPrefix + "_" + "characteristics"
}
func (object *Erfahrungsschatz) TableName() string {
dbPrefix := "char"
return dbPrefix + "_" + "experiances"
}
func (object *Bennies) TableName() string {
dbPrefix := "char"
return dbPrefix + "_" + "bennies"
}
func (object *Vermoegen) TableName() string {
dbPrefix := "char"
return dbPrefix + "_" + "wealth"
}
// DerivedBonuses contains all calculated bonus values
type DerivedBonuses struct {
AusdauerBonus int
SchadensBonus int
AngriffsBonus int
AbwehrBonus int
ZauberBonus int
ResistenzBonusKoerper int
ResistenzBonusGeist int
}
// GetAttributeValue returns the value of an attribute by name
func (char *Char) GetAttributeValue(name string) int {
for _, attr := range char.Eigenschaften {
if attr.Name == name {
return attr.Value
}
}
return 0
}
// CalculateBonuses calculates all derived bonuses from attributes
func (char *Char) CalculateBonuses() DerivedBonuses {
st := char.GetAttributeValue("St")
gs := char.GetAttributeValue("Gs")
gw := char.GetAttributeValue("Gw")
ko := char.GetAttributeValue("Ko")
in := char.GetAttributeValue("In")
zt := char.GetAttributeValue("Zt")
bonuses := DerivedBonuses{
// Ausdauer Bonus: Ko/10 + St/20
AusdauerBonus: (ko / 10) + (st / 20),
// Schadens Bonus: St/20 + Gs/30 - 3
SchadensBonus: (st / 20) + (gs / 30) - 3,
// Angriffs Bonus basierend auf GS
AngriffsBonus: calculateAttributeBonus(gs),
// Abwehr Bonus basierend auf GW
AbwehrBonus: calculateAttributeBonus(gw),
// Zauber Bonus basierend auf Zt
ZauberBonus: calculateAttributeBonus(zt),
}
// Resistenz Bonus Körper
bonuses.ResistenzBonusKoerper = calculateResistenzBonusKoerper(ko, char.Rasse, char.Typ)
// Resistenz Bonus Geist
bonuses.ResistenzBonusGeist = calculateResistenzBonusGeist(in, char.Rasse, char.Typ)
return bonuses
}
// Helper functions for bonus calculation
func calculateAttributeBonus(value int) int {
if value <= 5 {
return -5
} else if value <= 20 {
return -1
} else if value <= 40 {
return 0
} else if value <= 60 {
return 1
} else if value <= 80 {
return 2
} else if value <= 95 {
return 3
} else {
return 4
}
}
func calculateResistenzBonusKoerper(ko int, rasse string, typ string) int {
bonus := 0
if rasse == "Mensch" {
bonus = calculateAttributeBonus(ko)
} else {
switch rasse {
case "Elf":
bonus = 2
case "Gnom", "Halbling":
bonus = 4
case "Zwerg":
bonus = 3
}
}
// Klassenmodifikator
if isKaempfer(typ) {
bonus += 1
} else if isZauberer(typ) {
bonus += 2
}
return bonus
}
func calculateResistenzBonusGeist(in int, rasse string, typ string) int {
bonus := 0
if rasse == "Mensch" || rasse == "Menschen" {
bonus = calculateAttributeBonus(in)
} else {
switch rasse {
case "Elf":
bonus = 2
case "Gnom", "Halbling":
bonus = 4
case "Zwerg":
bonus = 3
}
}
// Klassenmodifikator (nur Zauberer bekommen Geist-Bonus)
if isZauberer(typ) {
bonus += 2
}
return bonus
}
func isKaempfer(typ string) bool {
kaempferClasses := []string{
"Krieger", "Barbar", "Spitzbube", "Assassine",
"Streiter", "Waldläufer", "Krieger Magier",
}
for _, class := range kaempferClasses {
if typ == class {
return true
}
}
return false
}
func isZauberer(typ string) bool {
zaubererClasses := []string{
"Magier", "Hexer", "Thaumaturg", "Krieger Magier",
"Priester", "Druide", "Schamane",
}
for _, class := range zaubererClasses {
if typ == class {
return true
}
}
return false
}