Kleiner RPG-Dungeon

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
abgdf

Montag 23. Oktober 2006, 17:38

Hi,

hier allererste Anfänge eines Rollenspiels, ähem, also eigentlich eher mehr ein Test der Bewegungen, ein 5X5-Felder großer Dungeon, noch keine Monster, Schätze oder auch nur Türen :P.

Bisher Steuerung über input, soll aber über curses werden, also "n, e, s, w" drehen bisher die Blickrichtung, "go" bewegt in die Richtung (wie bei Bard's Tale (1986)).

Code: Alles auswählen

#!/usr/bin/env python

from os import system
import sys

DUNGROW = 5

ouch = 0

move = [ [1,2,3,4],
         [1,2,3],
         [2,3,4],
         [1,3,4],
         [1,2,4],
         [2,3],
         [3,4],
         [1,4],
         [1,2],
         [2],
         [1],
         [4],
         [3],
         [2,4],
         [1,3]
         ]


dung = [ 9, 13, 4, 13, 7,
         8, 11, 14, 10, 12,
        14, 15, 14, 12, 10,
        1, 13, 0, 13, 3,
        5, 7, 12, 15, 12 ]

pos = 0
dir_ = 2

def TrDir(a):
    if a == 1:
        return("north")

    if a == 2:
        return("east")

    if a == 3:
        return("south")

    if a == 4:
        return("west")

def SayPos(pos,dir):
    posn = int(pos/5)
    pose = pos % 5
    print
    print "Scry Site says: Your position is:"
    print pose, "step(s) east and"
    print posn, "step(s) north of the entry stairs."
    print
    print "Your direction is", TrDir(dir) +"."
    print


def CheckGo(pos, dir, dung, move):
    
    if dung[pos] == 15:
        return(pos)

    a = move[dung[pos]]
    for i in a:
        if i == dir:
            if dir == 1:
                pos += DUNGROW
            if dir == 2:
                pos += 1
            if dir == 3:
                pos -= DUNGROW
            if dir == 4:
                pos -= 1
    return(pos)

    
def ChDir(dir, com):

    if com == "e":
        dir += 1
        if dir > 4:
            dir = 1

    if com == "w":
        dir -= 1
        if dir < 1:
            dir = 4

    if com == "r":
        if dir < 3:
            dir += 2
            return (dir)
        if dir > 2:
            dir -= 2

    return (dir)


def special(pos):

    if pos == 0:
        print "There are stairs here, going up."
        print "Do you want to take them (y/n) ?"
        print
        comm = raw_input()
        if comm == "y":
            print
            print "You climb out of the dungeon. You're happy to see daylight again."
            print
            sys.exit()
        else:
            system("clear")
            SayPos(pos,dir_)

    if pos == 6:
        print "There are stairs here leading down."
        print "But, alas, the way is blocked."
        print

while(1):

    if ouch == 0:
        system("clear")
        SayPos(pos,dir_)

    ouch = 0

    special(pos)

    comm = raw_input("Your movement (n, e, w, s, r, go): ")

    if comm == "apar":
        pos = 0

    dir_ = ChDir(dir_, comm)

    if comm == "go":
        x = CheckGo(pos, dir_, dung, move)
        if pos == x:
            print "\nOuch !\n"
            ouch = 1
        pos = x
Viele Grüße
Zuletzt geändert von abgdf am Sonntag 15. Juni 2008, 22:19, insgesamt 1-mal geändert.
Benutzeravatar
DatenMetzgerX
User
Beiträge: 398
Registriert: Freitag 28. April 2006, 06:28
Wohnort: Zürich Seebach (CH)

Montag 23. Oktober 2006, 17:45

irgend wo verwendest du den befehl clear, welcher unter win nicht verfügbar ist... Weiss nicht ob python irgend was schon mit sich liefert aber mach noch eine prüfung welche...

if sys.platform == 'win32': os.system('cls')

greez

edit: while musst du die bedinung nicht in klammern setzen

Dies würde ich mit elif lösen (kann ja nur 1 zutreffen)

Code: Alles auswählen

    for i in a:
        if i == dir:
            if dir == 1:
                pos += DUNGROW
            elif dir == 2:
                pos += 1
            elif dir == 3:
                pos -= DUNGROW
            elif dir == 4:
                pos -= 1 
hier auch...

Code: Alles auswählen

    if pos == 0:
        print "There are stairs here, going up."
        print "Do you want to take them (y/n) ?"
        print
        comm = raw_input()
        if comm == "y":
            print
            print "You climb out of the dungeon. You're happy to see daylight again."
            print
            sys.exit()
        else:
            system("clear")
            SayPos(pos,dir_)

    elif pos == 6:
        print "There are stairs here leading down."
        print "But, alas, the way is blocked."
        print 
BlackJack

Montag 23. Oktober 2006, 18:19

Nicht nur bei ``while`` sind die Klammern nicht nötig -- ``return`` ist auch keine Funktion.

Die Namensgebung ist ein bisschen "unpythonisch". Funktionsnamen sind konventionell kleine_worte_mit_unterstrichen. Und Pythonistas legen im Allgemeinen Wert auf lesbarkeit, d.h. das man durchaus `dungeon`, `position` und `direction` ausschreiben kann und nicht abkürzen muss. Gleiches gilt auch für die Funktionsnamen.

Das Dungeon könnte man in verschachtelten Listen speichern, dann ist man unabhängiger von Konstanten im Quelltext und die Position wäre dann ein Tupel aus x- und y-Koordinate.

Wenn man die Zahlwerte für die Richtungen an Konstanten bindet, dann wird der Quelltext etwas verständlicher, also am Anfang sowas wie:

Code: Alles auswählen

NORTH, EAST, SOUTH, WEST = range(4)
Die Funktion `TrDir` könnte man mit einer Liste vereinfachen, oder sogar ganz ersetzen:

Code: Alles auswählen

def direction2str(direction):
    return ['north', 'east', 'south', 'west'][direction]
abgdf

Dienstag 24. Oktober 2006, 21:55

Hallo,

danke für eure Hinweise. Wenn ich noch weiter mache, werde ich sie einarbeiten.

Hier erstmal so noch eine neue Version: Die Steuerung erfolgt jetzt (wie bei Bard's Tale) über die Cursortasten.
Das geht allerdings nur unter Linux, und da auch nur auf solchen Terminals wie meinem (xterm). Das ist leider sehr systemspezifisch. Bitte probiert's halt aus.
In der Nurtextkonsole und Konsole geht's auch:

Code: Alles auswählen

#!/usr/bin/env python
# -*- encoding: latin-1 -*-

from os import system
import sys
import tty
import termios

fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)

DUNGROW = 5

ouch = 0
treas = 0
comm = ""

move = [ [1,2,3,4],
         [1,2,3],
         [2,3,4],
         [1,3,4],
         [1,2,4],
         [2,3],
         [3,4],
         [1,4],
         [1,2],
         [2],
         [1],
         [4],
         [3],
         [2,4],
         [1,3]
         ]


dung = [ 9, 13, 4, 13, 7,
         8, 11, 14, 10, 12,
        14, 15, 14, 12, 10,
        1, 13, 0, 13, 3,
        5, 7, 12, 15, 12 ]

pos = 0
dir_ = 2


def GetOne():
    try:
        tty.setcbreak(sys.stdin.fileno())
        ch = sys.stdin.read(1)
        return(ord(ch))
        
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)


def TrDir(a):
    if a == 1:
        return("north")

    if a == 2:
        return("east")

    if a == 3:
        return("south")

    if a == 4:
        return("west")

def SayPos(pos,dir):
    posn = int(pos/5)
    pose = pos % 5
    print
    print "Scry Site says: Your position is:"
    print pose, "step(s) east and"
    print posn, "step(s) north of the entry stairs."
    print
    print "Your direction is", TrDir(dir) +"."
    print


def CheckGo(pos, dir, dung, move):
    
    if dung[pos] == 15:
        return(pos)

    a = move[dung[pos]]
    for i in a:
        if i == dir:
            if dir == 1:
                pos += DUNGROW
            if dir == 2:
                pos += 1
            if dir == 3:
                pos -= DUNGROW
            if dir == 4:
                pos -= 1
    return(pos)

    
def ChDir(dir, com):

    if com == "C":
        dir += 1
        if dir > 4:
            dir = 1

    if com == "D":
        dir -= 1
        if dir < 1:
            dir = 4

    if com == "B":
        if dir < 3:
            dir += 2
            return (dir)
        if dir > 2:
            dir -= 2

    return (dir)


def special(pos, treasure):

    if pos == 0:
        print "There are stairs here, going up."
        print "Do you want to take them (y/n) ?"
        print
        comm = GetOne()
        if chr(comm) == "y":
            print
            print "You climb out of the dungeon."
            if treasure == 1:
                print "The treasure feels really good in your hands."

            print
            sys.exit()
        else:
            system("clear")
            SayPos(pos,dir_)

    if pos == 6:
        print "There are stairs here leading down."
        print "But, alas, the way is blocked."
        print

    if pos == 24 and treas == 0:
        print "Congratulations !"
        print "You found a valuable treasure."
        print
        treasure = 1

    return(treasure)

while(1):

    if ouch == 0:
        system("clear")
        SayPos(pos,dir_)

    ouch = 0

    treas = special(pos, treas)

    a = GetOne()

    if a == 27:
        a = GetOne()
        if a == 91:
            a = GetOne()

    comm = chr(a)
    
    if comm == "c":
        comm = raw_input("Command: ")

    if comm == "apar":
        pos = 0

    dir_ = ChDir(dir_, comm)

    if comm == "A":
        x = CheckGo(pos, dir_, dung, move)
        if pos == x:
            print "\nOuch !\n"
            ouch = 1
        pos = x
Viele Grüße
abgdf

Sonntag 15. Juni 2008, 22:20

Hallo,

jetzt auch mit Grafik in einem Tkinter-Canvas (bitte von Konsole aus starten):

Code: Alles auswählen

#!/usr/bin/env python

import Tkinter

from Tkconstants import *

import tkMessageBox
import sys


class Main:

    def __init__(self):

        self.dung = Dungeon(self)

        self.mainwin = MainWindow(self)

        self.dung.getFields()

        self.evhdl = EventHandler(self)

        a = ("<Left>", "<Down>", "<Right>", "<Up>", "<o>", "<c>", "<Escape>")

        for i in a:
            self.evhdl.bindKey(self.mainwin.mw, i)

        del a

        self.party = Party(self)

        self.arrow = Arrow(self)

        self.party.doLook()
        self.party.checkBeen()

        self.mainwin.runMainLoop()


class MainWindow:

    def __init__(self, main):

        self.main = main

        self.clearjob = None

        self.hlinel = 50
        self.vlinel = self.hlinel * 3 / 4

        self.hborder = 200
        self.vborder = 150

        # self.winwidth = self.main.dung.dungrow * self.hlinel + 2 * self.hborder
        self.winwidth = 450
        self.winheight = self.winwidth  * 3 / 4

        self.xscroll = 25
        self.yscroll = int(self.xscroll * 3 / 4)
        self.scrollincrement = 2

        self.mw = Tkinter.Tk()
        self.mw.title("Dungeon")
        # self.mw.option_add("*font", "Arial 15 normal")
        self.mw.geometry("+250+200")

        self.cv = Tkinter.Canvas(self.mw,
                                 bg = "white",
                                 width = self.winwidth,
                                 height = self.winheight,
                                 xscrollincrement = self.scrollincrement,
                                 yscrollincrement = self.scrollincrement)

        self.cv.pack()

        self.sbar = StatusBar(self.mw)
        self.sbar.pack(side = BOTTOM, fill = BOTH)


    def scrollCanvas(self, movedir):

        if movedir == 0:
            self.cv.xview_scroll(-self.xscroll, what = "units")

        if movedir == 1:
            self.cv.yview_scroll(-self.yscroll, what = "units")

        if movedir == 2:
            self.cv.xview_scroll(self.xscroll, what = "units")

        if movedir == 3:
            self.cv.yview_scroll(self.yscroll, what = "units")

    def runMainLoop(self):

        self.mw.after(50, self.showInstructions)

        self.mw.mainloop()

    def showInstructions(self):

        a = 'Welcome to the dungeon.\n\nUse the cursor-keys to turn around and walk.\n\nPress "o" to open a door and "c" to close it again.\n\nYou can use "Esc" to exit.\n'

        tkMessageBox.showinfo("Instructions", a)

    def show(self, text):

        try:
            self.mw.after_cancle(self.clearjob)
        except:
            pass

        self.sbar.set(text)
        self.clearjob = self.mw.after(ms = 1500, func = self.sbar.clear)


class EventHandler:

    def __init__(self, main):
        self.main = main

    def bindKey(self, widget, key):
        widget.bind(sequence = key, func = self.bindFunc)

    def bindFunc(self, a):

        key = a.keysym

        if key == "Left":
            self.main.party.turnLeft()

        if key == "Right":
            self.main.party.turnRight()

        if key == "Down":
            self.main.party.turnAround()

        if key == "Up":
            self.main.party.moveForward()

        if key == "o":
            self.main.party.manipulateDoor(3)

        if key == "c":
            self.main.party.manipulateDoor(2)

        if key == "Escape":
            self.main.mainwin.mw.destroy()

class Arrow:

    def __init__(self, main):

        self.main = main
        self.arrowdir = self.getArrowDirection()
        self.arrow = self.drawArrow()

    def updateArrow(self):

        if self.arrow:
            self.main.mainwin.cv.delete(self.arrow)

        self.arrowdir = self.getArrowDirection()

        self.arrow = self.drawArrow()


    def drawArrow(self):

        coords = self.getArrowCoordinates()

        if self.main.party.dir == 0 or self.main.party.dir == 2:
            nr = 0
        else:
            nr = 1

        arrow = self.main.mainwin.cv.create_line(coords[nr][0], coords[nr][1], coords[nr][2], coords[nr][3], arrow = self.arrowdir)

        return arrow


    def getArrowCoordinates(self):

        # x and y are the coordinates of the down-left-corner of a field.

        x = self.main.party.partyfield.nr % self.main.dung.dungrow * self.main.mainwin.hlinel + self.main.mainwin.hborder
        y = self.main.mainwin.winheight - self.main.mainwin.vborder - self.main.party.partyfield.nr // self.main.dung.dungrow * self.main.mainwin.vlinel

        coords = ((x + self.main.mainwin.hlinel / 4, y - self.main.mainwin.vlinel / 2, x + self.main.mainwin.hlinel * 3 / 4, y - self.main.mainwin.vlinel / 2), (x + self.main.mainwin.hlinel / 2, y - self.main.mainwin.vlinel / 4, x + self.main.mainwin.hlinel / 2, y - self.main.mainwin.vlinel * 3 / 4)) 

        return coords


    def getArrowDirection(self):

        if self.main.party.dir == 1 or self.main.party.dir == 2:
            arrowdir = LAST
        else:
            arrowdir = FIRST

        return arrowdir


class Dungeon:

    def __init__(self, main):

        self.main = main

        self.dungrow = 5

        self.dungdesc = ["1101", "0101", "0001", "0101", "0011",
                         "1001", "0211", "1010", "1011", "1110", 
                         "1010", "1112", "1010", "1120", "2011", 
                         "1000", "0101", "0000", "0101", "0010", 
                         "1100", "0111", "1110", "1121", "2110"]

        self.fields = []

    def getFields(self):

        for i in range(len(self.dungdesc)):
            self.fields.append(Field(self, self.main, i, self.dungdesc[i]))


class Field:

    def __init__(self, dung, main, nr, description):

        self.main = main
        self.nr = nr
        self.description = description

        # x and y are the coordinates of the down-left-corner of a field.

        self.x = self.nr % dung.dungrow * self.main.mainwin.hlinel + self.main.mainwin.hborder
        self.y = self.main.mainwin.winheight - self.main.mainwin.vborder - self.nr // dung.dungrow * self.main.mainwin.vlinel

        self.drawings = []

        self.grey = self.hideField()

    def drawField(self):

        coords = ((self.x, self.y, self.x, self.y - self.main.mainwin.vlinel),
                  (self.x, self.y - self.main.mainwin.vlinel, self.x + self.main.mainwin.hlinel, self.y - self.main.mainwin.vlinel),
                  (self.x + self.main.mainwin.hlinel, self.y, self.x + self.main.mainwin.hlinel, self.y - self.main.mainwin.vlinel),
                  (self.x, self.y, self.x + self.main.mainwin.hlinel, self.y))

        opts = (('black', None),
                ('red', None),
                ('green', 'gray50'))

        for u in range(3):

            for i in range(4):

                if self.description[i] == str(u + 1):

                    self.drawings.append(self.main.mainwin.cv.create_line(coords[i][0], coords[i][1], coords[i][2], coords[i][3], fill = opts[u][0], stipple = opts[u][1])) 

    def hideField(self):

        a = self.main.mainwin.cv.create_rectangle(self.x, self.y, self.x + self.main.mainwin.hlinel, self.y - self.main.mainwin.vlinel, outline = 'grey', fill = 'grey')

        return a

    def revealField(self):

        self.main.mainwin.cv.delete(self.grey)
        self.drawField()
        self.grey = None


    def clearField(self):

        if len(self.drawings) > 0:

            for i in self.drawings:
                self.main.mainwin.cv.delete(i)

            self.drawings = []

    def changeDescription(self, pos, to): 

        a = []

        for i in self.description:
            a.append(i)

        a[pos] = to

        self.description = "".join(a)


class Party:

    def __init__(self, main):

        self.main = main

        self.pos = 0
        self.dir = 2
        self.sight = 1

        self.partyfield = self.main.dung.fields[self.pos]
        self.been = []

        self.hastreasure = False
        self.stepsfound = False

    def turnLeft(self):

        self.dir -= 1
        if self.dir < 0:
            self.dir = 3

        self.updatePosition()

    def turnRight(self):
        self.dir += 1
        if self.dir > 3:
            self.dir = 0

        self.updatePosition()

    def turnAround(self):
        self.dir = self.counterDirection()

        self.updatePosition()

    def moveForward(self):

        fieldside = int(self.partyfield.description[self.dir])

        if fieldside == 0 or fieldside == 3:

            self.pos = self.fieldAhead()
            self.partyfield = self.main.dung.fields[self.pos]
            self.main.mainwin.scrollCanvas(self.dir)

        if fieldside == 1:
            self.main.mainwin.show("Ouch: Wall.")

        if fieldside == 2:
            self.main.mainwin.show("Ouch: Locked Door.")

        self.updatePosition()


    def doLook(self):

        for i in range(self.sight):

            if self.fieldAhead(i) == -1:
                break

            fieldside = self.main.dung.fields[self.fieldAhead(i)].description[self.dir]
            fieldside = int(fieldside)

            if self.main.dung.fields[self.fieldAhead(i)].grey:
                self.main.dung.fields[self.fieldAhead(i)].revealField()

            if fieldside == 1 or fieldside == 2:
                break


    def manipulateDoor(self, a):

        fieldside = int(self.partyfield.description[self.dir])

        if a == 3 and fieldside != 2:
            return

        if a == 2 and fieldside != 3:
            return

        self.partyfield.changeDescription(self.dir, str(a))
        self.main.dung.fields[self.fieldAhead()].changeDescription(self.counterDirection(), str(a))

        self.partyfield.clearField()
        self.partyfield.drawField()

        self.main.dung.fields[self.fieldAhead()].clearField()
        self.main.dung.fields[self.fieldAhead()].drawField()

        if a == 3:
            self.main.mainwin.show("Door opened !")

        if a == 2:
            self.main.mainwin.show("Door closed again.")


    def updatePosition(self):

        self.doLook()
        self.main.arrow.updateArrow()

        # print self.pos

        if self.pos == 8 and not self.hastreasure:
            tkMessageBox.showinfo("Treasure !", "You found a valuable treasure !\n")
            self.hastreasure = True

        if self.pos == 11 and not self.stepsfound:
            tkMessageBox.showinfo("Stairs", "\nThere are stairs here leading down.\n\nBut, alas, the way is blocked.\n\n")
            self.stepsfound = True

        self.checkBeen()

    def checkBeen(self):

        if self.pos not in self.been:
            self.been.append(self.pos)

        if len(self.been) == len(self.main.dung.fields):

            if tkMessageBox.askyesno(title = 'Dungeon explored', message = "Congratulations !\n\nYou've explored the whole dungeon !\n\nDo you want to quit now ?\n"):
                self.main.mainwin.mw.destroy()
                sys.exit()
            else:
                self.been = []


    def fieldAhead(self, step = 1):

        # Returns the fieldnumber of fields in the neighbourhood,
        # in the given direction or -1 if the field is out of range.

        a = (self.pos - step,
             self.pos + self.main.dung.dungrow * step,
             self.pos + step,
             self.pos - self.main.dung.dungrow * step)

        for i in range(4):

            if self.dir == i:

                if i < 2 and a[i] < 0:
                    return -1

                if i > 1 and a[i] >= self.main.dung.dungrow ** 2:
                    return -1

                return a[i]


    def counterDirection(self):

        if self.dir < 2:
            return self.dir + 2

        if self.dir > 1:
            return self.dir - 2


class StatusBar(Tkinter.Frame):

    def __init__(self, master):

        Tkinter.Frame.__init__(self, master)

        self.sbartext = Tkinter.Variable()

        self.label = Tkinter.Label(self,
                                   bd = 1,
                                   anchor = W,
                                   textvariable = self.sbartext)

        self.label.pack(side = LEFT, fill = BOTH)

        self.button = Tkinter.Button(self,
                                     text = "Exit",
                                     command = master.destroy)
        self.button.pack(side = RIGHT)
        self.button.focus()

    def set(self, format):
        self.sbartext.set(format)

    def clear(self):
        self.sbartext.set("")


if __name__ == "__main__":
   app = Main()
Viele Grüße
Antworten