Lootgenerator für DnD Pathfinder 1 Edition

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
1of7470000000
User
Beiträge: 12
Registriert: Donnerstag 8. Februar 2018, 22:04

Dienstag 3. September 2019, 19:17

Hallo ihr Lieben, :mrgreen:

ich habe mal wieder etwas gebastelt. Wir (Freunde et moi) zocken immer mal DnD (Dungeons & Dragons, eine Art Mmo, bevor es Mmos auf PC's gab. Es geht dabei nicht um Geld oder so etwas).

Das Loot, also die Beute von Gegnern wird dabei nach bestimmten Regeln erzeugt.

Es gibt 3 Lootstufen. Schnell, mittel und langsam.
Encounter können Stufen von 1/3 über 1 bis hin zu 30 haben. Laut Grundregelwerk (GRW).
Wenn man also einen Gegner der Stufe.. 5 besiegt und die Lootstufe "Mittel" hat (wird von der Spielleitung SL vorgegeben) bekommt man einen bestimmten Betrag. Sagen wir: 100 Gold.

Dieser Betrag soll nun zu 75% in Sachgütern und 25% in Rohgold.. "vergeben" werden.

Die SL muss nun ausrechnen welcher Betrag in rohem Geldwert gegeben wird und welcher in Waren.

Bei dem Beispiel müsste die SL nun also aus dem Ausrüstungsteil eines Regelbuches Gegenstände heraussuchen, bis die 75 Gold verbraucht sind. Sowie das Gold zufällig auf Gold- Silber- und Kupfermünzen verteilen.

Das ist eigentlich alles tutti, aber.. es fällt manchmal schwer, dass so wie dargestellt mit 3 bis 6 Personen am Tisch umzusetzten.
Weil es eben Zeit verbraucht.

Hier kommt das Programm "Lootgenerator" zum Einsatz.

Es hat zwei Inputs (wealth-level und Encounter- Level) und als Output gibt es 25% des Beutewertes in zufälligen Münzen wieder und sucht für den Rest eben das Equip zusammen. Es kann sein, das Dinge mehrfach vorkommen, z. B. Saphir 2.

Als letztes hat das Programm eine grafische Bunutzeroberfläche, da die meisten meiner Freunde den Amblick von Programmcode mit nichts Gutem assoziieren ("Kaputt", "Buggy" usw).

Also kurz; ich habe mit tkinter eine GUI dazu gebaut.

So. Um es mit den Worten von L. Torwald zu sagen: Talk is cheap, show me the code" :D.

Code: Alles auswählen


# -*- coding: UTF-8 -*-
# __version__ = “1.0”
# pf loot generator
# no (c) !

import random as r
from tkinter import *

root = Tk()
root.title("Pathfinder Loot Generator")
root.configure(bg = "black")
# root.configure(bg="black")

# REQUIERED DATA
# 3 encounter - and loot tables (values in GOLD)

l_loot = {"1/8" : 20, "1/6" : 30, "1/4" : 40,"1/3" : 55, "1/2" : 85, "1" : 170, "2" : 350, "3" : 550, "4" : 750, "5" : 1000, "6" : 1350, "7" : 1750, "8" : 2200, "9" : 2850, "10" : 3650, "11" : 4650, "12" : 6000, "13" : 7750, "14" : 10000, "15" : 13000, "16" : 16500, "17" : 22000, "18" : 28000, "19" : 35000, "20" : 44000, "21" : 55000, "22" : 69000, "23" : 85000, "24" : 102000, "25" : 125000, "26" : 150000, "27" : 175000, "28" : 205000, "29" : 240000, "30" : 280000}

m_loot = {"1/8" : 35, "1/6" : 45, "1/4" : 65, "1/3" : 85, "1/2" : 130, "1" : 260, "2" : 550, "3" : 800, "4" : 1150, "5" : 1550, "6" : 2000, "7" : 2600, "8" : 3350, "9" : 4250, "10" : 5450, "11" : 7000, "12" : 9000, "13" : 11600, "14" : 15000, "15" : 19500, "16" : 25000, "17" : 32000, "18" : 41000, "19" : 53000, "20" : 67000, "21" : 84000, "22" : 104000, "23" : 127000, "24" : 155000, "25" : 185000, "26" : 220000, "27" : 260000, "28" : 305000, "29" : 360000, "30" : 420000}

f_loot= {"1/8" : 50, "1/6" : 65, "1/4" : 100, "1/3" : 135, "1/2" : 200, "1" : 400, "2" : 800, "3" : 1200, "4" : 1700, "5" : 2300, "6" : 3000, "7" : 3900, "8" : 5000, "9" : 6400, "10" : 8200, "11" : 10500, "12" : 13500, "13" : 17500, "14" : 22000, "15" : 29000, "16" : 38000, "17" : 48000, "18" : 62000, "19" : 79000, "20" : 100000, "21" : 125000, "22" : 155000, "23" : 190000, "24" : 230000, "25" : 275000, "26" : 330000, "27" : 390000, "28" : 460000, "29" : 540000, "30" : 630000}

eqdic = {"1 Leib Knoblauchbrot" : 2, "Glaeschen Honig" : 100, "Glaeschen Kirschmarmelade" : 50, "Beutel mit Kaffee (20g)" : 1, "Kleiner Sack Kaffeepulver (500g)" : 450, "Flaeschen mit Saure" : 1000, "Fläschen Weihwasser mit dem Symbol Pyrons" : 2500, "Streichholz" : 100, "Tonflaeschen Tinte (30ml)" : 2500, "Glasflasche mit Tinte (1L" : 2600, "Tonphiole mit Tinte 1L" : 2600, "Stundenglas mit hoelzernem Rahmen (30min)" : 2500, "Glasflaeschen gefuellt mit Tinte (30ml)" : 2500}



# PROGRAM RUNNIG PART

space0 = Label(root,
	bg = "black")
space0.pack(side = "top")

space1 = Label(root,
	bg = "black")
space1.pack(side = "top")

info_encounter_level = Label(
        root,
        bg = "sky blue",
        text = "Gib das Encounter-Level ein (1-30)"
        )
info_encounter_level.pack(
#fill = "both",
expand = "yes"
)

elvl = Entry(root)
elvl.pack(
#fill = "both",
expand = "yes"
)

space2 = Label(root,
	bg = "black")
space2.pack(side = "top")


info_wealthlevel = Label(
        root,
        bg = "sky blue",
        text = "Gib das wealth  - Level ein (1-3)"
        )
info_wealthlevel.pack(
#fill = "both",
expand = "yes"
)

wlvl = Entry(root)
wlvl.pack(
#fill = "both",
expand = "yes"
)


def action():
    if wlvl.get() == "1":
        loot_worth = l_loot.get(elvl.get())

    if wlvl.get() == "2":
        loot_worth = m_loot.get(elvl.get())
    if wlvl.get() == "3":
        loot_worth = f_loot.get(elvl.get())
    split= 25
    gold_counter = 0
    silver_counter = 0
    copper_counter = 0
    options = ["gold", "silver", "copper"]
    raw = (loot_worth/100) *split
    for i in range(int(raw)):
        rollit = r.choice(options)
        if rollit == "gold":
            gold_counter += 1
        elif rollit == "silver":
            silver_counter += 10
        elif rollit == "copper":
            copper_counter += 100
    coin_dict = {}
    coin_dict["Gold"] = gold_counter
    coin_dict["Silber"] = silver_counter
    coin_dict["Kupfer"] = copper_counter

# -------------------------------
#    SHOW RESULTS
    space3= Label(root,
    	bg = "black")
    space3.pack(side = "top")
    space4 = Label(root,
    	bg = "black")
    space4.pack(side = "top")
    gold_string = "Gold: " + str(gold_counter)
    global goldlabel
    goldlabel = Label(root,
        text = gold_string)
    goldlabel.pack(side = "top")  
    silver_string = "Silber: " + str(silver_counter)
    global silverlabel
    silverlabel = Label(root,
        text = silver_string)
    silverlabel.pack(side = "top")
    copper_string = "Kupfer: " + str(copper_counter)
    global copperlabel
    copperlabel = Label(root,
        text = copper_string)
    copperlabel.pack(side = "top")

    budget = (loot_worth - raw) * 100
    budgetingold = budget / 100

    show_budget_info = Label(root,
        text = "Gesamtwert Equip in Gold:"
        )
    clc = []
    worklist = []
    for equip_pices in eqdic.keys():
        worklist.append(equip_pices)
    loot_list =[]
    while budget > 0:
        pice = r.choice(worklist)
        budget -= eqdic.get(pice)
        loot_list.append(pice)
        budget_toadd = str(budget)
        to_add = pice + " added to loot. Lootworth remain = " + budget_toadd
        clc.append(to_add)
        if budget < 0:
            budget += eqdic.get(pice)
            del loot_list[-1]
            del clc[-1]
        elif budget == 0:
            break
        else:
            continue
    dupli_test = []
    dupli_storage = {}
    for lootitem in loot_list:
        dupli_storage[lootitem] = 1
        dupli_test.append(lootitem)
    for lootitem in loot_list:
        if lootitem in dupli_test and lootitem in loot_list:
            dupli_storage[lootitem] +=1
        else:
            None
    global loot_scrollbar
    loot_scrollbar = Scrollbar(
        root,
        bd = 2,
        jump = 0,
        repeatdelay = 500,
        width = 35
        )
    loot_scrollbar.pack(
        side = RIGHT,
        fill = Y
        )
    global gui_lootlist
    gui_lootlist = Listbox(
        root,
        bd = 1,
        yscrollcommand = loot_scrollbar.set
        )
    for i in dupli_storage.items():
        gui_lootlist.insert(END,
            i
            )
    gui_lootlist.pack( side = TOP, fill = BOTH )
    loot_scrollbar.config(
        command = gui_lootlist.yview
        )


space5 = Label(root,
	bg = "black")
space5.pack(side = "top")

space6 = Label(root,
	bg = "black")
space6.pack(side = "top")


action_button = Button(
        root,
        bg = "sky blue",
        command = action,
        text = "Loot berechnen!"
        )
action_button.pack(
#	fill = "both",
	expand = "yes"
	)

def escape():
    quit()

esc = Button(
    text="Beenden",
    bg = "sky blue",
    command=escape
    )
esc.pack(
	side= "bottom",
#	fill = "both",
	expand = "yes"
	)



def clear():
	elvl.delete(0, END)
	wlvl.delete(0, END)
	gui_lootlist.destroy()
	loot_scrollbar.destroy()
	goldlabel.destroy()
	silverlabel.destroy()
	copperlabel.destroy()
	

new_button = Button(root,
		text = "Neue Berechnung",
		bg = "sky blue",
		command = clear
		)
		
new_button.pack(
		side = "bottom",
#		fill = "both",
		expand = "yes"
		)

mainloop()

# EOF
Ich hoffe echt, es hat den Code nicht beim einfügen zerschossen. Wäre schade.

Obwohl das Showcase heißt, bin ich für jede konstruktive Kritik dankbar.

Ich habe hier schon sehr viel gelernt. Danke an alle, die sich Zeit nehmen.

Ach ja, ich möchte es open source allen zur Verfügung stellen.
Den code gibt es nun an 2 Orten, aber alle Items pflege ich erst ein, wenn ich das "ok" von Paizo habe.
Deshalb steht jetzt nur Trash, wie Knoblauchbrot usw drinnen :lol:

Schönen Abend :D
Sirius3
User
Beiträge: 10347
Registriert: Sonntag 21. Oktober 2012, 17:20

Donnerstag 5. September 2019, 19:23

Es ist schwierig, den Code zu lesen, weshalb wohl hier kaum einer Lust hat, sich da durchzuarbeiten.

Keine Sternchenimporte benutzen und keine unnötigen Abkürzungen. Einrückungen sollten einheitlich sein, keine Tabs, eingerückt wird immer mit 4 Leerzeichen pro Ebene.

Konstanten schreibt man KOMPLETT_GROSS, benutze keine globalen Variablen.
Nicht ausführbaren Code mit Konstanten und Funktionen mischen. Aller Code auf oberster Ebene sollte sowieso in eine Funktion wandern, die man üblicherweise main nennt.

Wenn ein Fenster angezeigt wird, ist es normalerweise vollständig mit allen Elementen bestückt und wird nicht nachträglich erweitert. Labels können vorläufig leer bleiben, andere Elemente deaktiviert werden.

Die Funktion `action` ist viel zu lang und sollte in mehrere aufgeteilt werden. Für nicht-triviale GUI-Programme braucht man Klassendefinitionen.
1of7470000000
User
Beiträge: 12
Registriert: Donnerstag 8. Februar 2018, 22:04

Sonntag 8. September 2019, 09:34

Vielen Dank für dein Feedback!

Ich werde den Programmcode nocheinmal komplett umschreiben und dabei deine Hinweise umsetzen.

Ich will nicht rumdiskutieren, dafür bin ich zu dankbar für das Feedback, nur erklären, was ich mir dabei gedacht habe:
Mit den globalen Variablen hatte ich Bauchschmerzen, aber anders konnte ich (bis jetzt) nicht umsetzen, dass bei dem Button "Neue Berechnung" alles zurück gesetzt wird.

Ich mach' mich dran, nochmal vielen Dank.
Antworten