* I believe the lvl1[(x,y)] = getattr(__import__('mapTiles'), tile_name)(x, y) is causing the problem, I changed it to a direct import with the same problem and the circular import here is... mapTiles imports world and inside the load_tiles() function, it imports mapTiles. I'm not sure how to restructure this to stop the circles, any ideas? *
I'm making a text RPG game, I have all the tiles coded and able to be interacted with but when I go to run the game, it gives me the error below. I can't see a circular import anywhere so I don't understand what's going on. (Please tell me if you need any other code)
Error: (caused by load_tiles() which is called in play)
getattr(__import__('mapTiles'), tile_name)(x, y)
AttributeError: module 'mapTiles' has no attribute 'PlainsTile'
Only showing the base class and the one tile because there are about ten different ones
mapTiles.py
import actions, items, enemies, actions, world
class MapTile:
def __init__(self,name, x, y, intro, description):
self.x = x
self.y = y
self.intro = intro
def intro_text(self):
return self.intro
def terrain_interacts(self, terrain):
# For elemental effects
pass
def randomize_interactions(self, tile):
# Randomize enemy and loot spawns
pass
# Default actions
def adjacent_moves(self):
# Returns all move actions for adjacent tiles
moves = []
if world.tile_exists(self.x + 1, self.y):
moves.append(actions.MoveEast())
if world.tile_exists(self.x - 1, self.y):
moves.append(actions.MoveWest())
if world.tile_exists(self.x, self.y - 1):
moves.append(actions.MoveNorth())
if world.tile_exists(self.x, self.y + 1):
moves.append(actions.MoveSouth())
moves.append(actions.ViewInventory)
return moves
def available_actions(self):
# Returns all available actions for the current tile
moves = self.adjacent_moves()
return moves
class PlainsTile(MapTile):
def __init__(self, x, y):
self.name = "PlainsTile"
self.intro = "You enter a plains"
self.description = "A span of clear land, with the tall grass waving in the wind"
super().__init__(
name=self.name,
intro=self.intro,
description=self.description,
x=self.x,
y=self.y
)
play.py
#play.py
import character, world
def play_game(player):
world.load_tiles()
print("Called")
#These lines load the starting room and display the text
tile = world.tile_exists(x=player.location_x, y=player.location_y)
if tile != None:
print(tile)
while player.is_alive() and not player.victory:
tile = world.tile_exists(player.location_x, player.location_y)
# Check again since the room could have changed the player's state
if player.is_alive() and not player.victory:
print("Choose an action:\n")
last[0] = player.location_x
last[1] = player.location_y
if tile != None:
available_actions = tile.available_actions()
if tile.name == 'BossTile' and player.victory:
tile.modify_character(player)
for action in available_actions:
print(available_actions.name)
for action in available_actions:
action_input = input("Choose an action ya prick: ")
if action_input == "quit":
quit()
if action_input == action.keyword:
player.do_action(action, **action.kwargs)
break
else:
print("Please choose one of the listed actions")
else:
print("You cannot go that way")
world.py
# No imports
lvl1 = {}
def load_tiles():
"""Parses a file that describes the world space into the _world object"""
with open('m1.txt', 'r') as f:
rows = f.readlines()
x_max = len(rows[0].split('\t')) # Assumes all rows contain the same number of tabs
for y in range(len(rows)):
cols = rows[y].split('\t')
for x in range(x_max):
tile_name = cols[x].replace('\n', '') # Windows users may need to replace '\r\n'
if tile_name == 'StartingRoom':
global starting_position
starting_position = (x, y)
if tile_name == '':
lvl1[(x, y)] = None
else:
getattr(__import__('mapTiles'), tile_name)(x, y)
def tile_exists(x,y):
return lvl1.get((x,y))
imports from all other files
#items.py
#No imports
#actions.py
from play import player
import items
#enemies.py
import random
#character.py
import world, items, random
I fixed it, the error was the import from getattr. I merged the two files, added a try/except to catch index errors and added a bunch of conditionals that instantiate the tile name at whatever coordinates.
Related
My problem is that when I change the content of the variable "XO" inside the if statement, the one in the condition of the If statement becomes undefined.
from ursina import *
import time
app = Ursina()
window.title = "Game"
window.borderless = False
window.exit_button_visible = False
XO = "X"
class Tile(Button):
def __init__(self, position = (-5, -5, 0), texture = "assets/tile.png"):
super().__init__(
parent = scene,
model = "quad",
color = color.lime,
position = position,
texture = texture
)
def input(self, key):
if self.hovered:
if key == "left mouse down":
if XO == "X":
Tile(position = self.position, texture = "assets/tile_x")
XO = "O"
else:
Tile(position = self.position, texture = "assets/tile_0")
XO = "X"
time.sleep(.005)
destroy(self)
for x in range(3):
for y in range(3):
block = Tile((x-1, y-1))
app.run()
The biggest issue here is that you seem to be trying to reference a global variable inside of a some inner context. In general, this is not encouraged for a lot of reasons. One of the biggest in my opinion is that global code is difficult to maintain. Instead, I would encourage you refactor your code so that all of your code resides inside of a class, and work on encapsulating your code.
That being said, sometimes it is necessary to use the global scope. In order to do that, you simply use the global keyword before the name of the variable you want global scope for. In your case, you would simple do global XO at the start of your input function.
I have been trying to add traps to my TextRPG I have something I think will work with a little bit of debugging but the first bug that I ran into is.
TypeError: init() should return None, not 'str'
the error is coming from this.
class TrapRoomTile(MapTile):
def __init__(self, x, y):
r = random.randint(1,2)
if r == 1:
self.trap = items.PitFall()
self.tripped_text = "The open hole of a Pit Fall trap obstructs the tunnel."
self.set_text = "The floor in this hallway is unusually clean."
else:
return"""
Looks like more bare stone...
"""
super().__init__(x, y)
def modify_player(self,player):
if not self.trap.is_tripped():
player.hp = player.hp - self.items.damage
print("You stumbled into a trap!")
time.sleep(1)
print("\nTrap does {} damage. You have {} HP remaining.".
format(self.items.damage, player.hp))
def intro_text(self):
text = self.tripped_text if self.items.is_tripped() else self.set_text
time.sleep(0.1)
return text
when i comment out this block of code everything runs as it should. I'm at a loss as to what to do about it. ill post a link to the github repo the code is in world.py starts on line 146.
https://github.com/GusRobins60/AdventureGame.git
The __init__ method in python should only used be used to initialize the class variables. You are returning a string from it, which you should not do.
You can either remove the return statement or set the string to another variable. Here is an example of what you can probably do:
class TrapRoomTile(MapTile):
def __init__(self, x, y):
r = random.randint(1,2)
if r == 1:
self.trap = items.PitFall()
self.tripped_text = "The open hole of a Pit Fall trap obstructs the tunnel."
self.set_text = "The floor in this hallway is unusually clean."
else:
self.set_text = "Looks like more bare stone..."
super().__init__(x, y)
def modify_player(self,player):
if not self.trap.is_tripped():
player.hp = player.hp - self.items.damage
print("You stumbled into a trap!")
time.sleep(1)
print("\nTrap does {} damage. You have {} HP remaining.".
format(self.items.damage, player.hp))
def intro_text(self):
text = self.tripped_text if self.trap.is_tripped() else self.set_text
time.sleep(0.1)
return text
I am trying to create a textgame in Python, and it is relying pretty heavily upon .json files. The most current problem is how to handle the picking up and dropping of items in the game. Conceptually, I think I can create a .json file containing player information (among them an inventory), update the inventory with the item's keyword, and delete that keyword from the room's .json file. I would do the opposite if the player were dropping an item.
My game.py:
import cmd
from room import get_room
from item import get_item
import textwrap
class Game(cmd.Cmd):
def __init__(self):
cmd.Cmd.__init__(self)
#Start the player out in Room #1 by calling the get_room() function
#passing the integer '1' with it, and reading 1.json.
self.loc = get_room(1)
# Displays the room to the player.
self.showRoom()
def move(self, dir):
#Grabs the direction the layer is moving, runs it through the
#_neighbor function within the room module. Determines if there
#is a room in that direction. If so, update to that new room an
#display it.
newroom = self.loc._neighbor(dir)
if newroom is None:
print("You can't go that way.")
else:
self.loc = get_room(newroom)
self.showRoom()
def showRoom(self):
#Displays the current room the player is in, as well as any other
#Objects within it. (Items, enemies, NPCs, etc)
print(self.loc.name)
print("")
#Wraps the text up to 70 words per line.
for line in textwrap.wrap(self.loc.description, 70):
print(line)
print("")
#Looks in the room's .json file to see if the room has a key to
#an item. If it does. Display it on the ground.
if self.loc.haveItem != "None":
print(self.loc.onGround)
print("")
#Looks to the room's .json file to see if the room has a key to
#'showNeighbors.' If it does, display the possible exits of that
#room.
print("Possible Exits:")
print(self.loc.showNeighbors)
def do_look(self, args):
#An exact copy of the showRoom function. There has to be a way to
#point do_look to showRoom() to keep from executing duplicate form.
#This is just bad form.
"""Reprints the rooms description and available exits."""
print(self.loc.name)
print("")
for line in textwrap.wrap(self.loc.description, 70):
print(line)
print("")
if self.loc.haveItem != "None":
print(self.loc.onGround)
print("")
print("Possible Exits:")
print(self.loc.showNeighbors)
def do_get(self, args):
#A function that handles getting an item off the room's ground.
#Currently not functioning as intended. Is meant to find the item
#In the room's .json file, use it to open the items .json file,
#and grab the 'keywords of said item. Then checks to see if
#get <keyword> was typed. If so, edit room's .json file to remove
#item from room, and add it to player inventory. Removing the
#.json file is neccessary to ensure item does not diplay in room
#once picked up.
"""type 'pick <item>' to pick up item in room."""
itemToTake = args.lower()
keywords = self.loc.keywords
if itemToTake == "":
print ("Take what? Type 'look' to see the items to take.")
return
if (itemToTake == keywords[0]) or (itemToTake == keywords[1]):
if self.loc.canTake == "True":
print("You have picked up a " + self.loc.itemName + ".")
#code should be placed here to wipe current room's .json
#file to ensure room is devoid of any items.
else:
print("That item is not here.")
def do_drop(self, args):
#A function that will handle dropping an item from a player's
#inventory. If an item is dropped in inventory, it will be removed
#from players .json inventory file, and added as a key:value
#to the room's .json file, so that should the player return, the
#item will load up as intended.
pass
def do_inv(self):
"""Opens up your inventory"""
#Hasen't been implimented yet. Would like to have it done through a
#.json file. The file would hold other players attributes such as:
#Hit Points, Strength, Dexterity, Armor Class, and the like.
#Self explainatory functions:
def do_quit(self, args):
"""Quit the game"""
print("Thank you for playing.")
return True
def do_n(self, args):
"""Goes North"""
self.move('n')
def do_s(self, args):
"""Goes South"""
self.move('s')
def do_e(self, args):
"""Goes East"""
self.move('e')
def do_w(self, args):
"""Goes West"""
self.move('w')
if __name__ == "__main__":
play = Game()
play.cmdloop()
my room.py:
import json
from item import get_item
"""
This module handles all of the rooms in the game. It searches for a .json file with the approriate id number, then opens it, reads it line by line, and stores it in a dictonary. The Room class then takes the information and sorts it out in to the corrisponding variables. If there is no string listed, it will list the default strings declared in the parameters.
"""
def get_room(id):
ret = None
with open(str(id) + ".json", "r") as file:
jsontext = file.read()
dictonary = json.loads(jsontext)
dictonary['id'] = id
ret = Room(**dictonary)
return ret
class Room():
def __init__(self, id = 0, name="A Room", description = "An Empty Room", neighbors = {}, showNeighbors = "None", haveItem = "None"):
#This is a mess. Has to be a better way to do all this rather than
#place it all in the initiate function. Function assigns .json file
# dictonaries to seperate variables. This is fine.
self.id = id
self.name = name
self.description = description
self.neighbors = neighbors
self.showNeighbors = showNeighbors
self.haveItem = haveItem
#This checks to see if room has an item. If so, grab it's .json file
#and assign it's values to variables. Fell that this SHOULD NOT be
#in __init__. Unsure how to do it any other way.
if haveItem != "None":
item = get_item(haveItem)
self.onGround = item.onGround
onGround = self.onGround
self.keywords = item.keywords
keywords = self.keywords
self.canTake = item.canTake
canTake = self.canTake
self.itemName = item.itemName
itemName = self.itemName
def modifiyRoom(self, id = 0, haveItem = "None"):
pass
#Function used to modify room .json files. uses include:
#Adding a dropped item.
#Removing an item dropped within room.
#Detect if enemy entered or left room.
#At least it would do all of that. If I can get it to work.
def _neighbor(self, direction):
if direction in self.neighbors:
return self.neighbors[direction]
else:
return None
def north(self):
return self._neighbor('n')
def south(self):
return self._neighbor('n')
def east(self):
return self._neighbor('n')
def west(self):
return self._neighbor('n')
and item.py:
import json
"""
This module handles all of the items in the game. It searches for a .json file with the approriate id, then opens it, reads it line by line, and stores it in a dictonary. The Item class then takes the information and sorts it out in to the corrisponding variables. If there is no string listed, it will list the default strings declared in the parameters.
"""
def get_item(itemName):
ret = None
with open(itemName + ".json", "r") as file:
jsontext = file.read()
item = json.loads(jsontext)
item['itemName'] = itemName
ret = Item(**item)
return ret
class Item():
def __init__(self, itemName = "", name = "An Item", onGround = "On ground", description = "A sweet Item", canTake = "False", keywords = "None", value = 0):
#Handles all of the variables found in an item's .json file.
#Feel this will have to act as a super class for more unique
#items, such as weapons, which will hold a damage value.
self.itemName = itemName
self.name = name
self.onGround = onGround
self.description = description
self.canTake = canTake
self.keywords = keywords
self.value = value
A sample room.json:
{"name" : "Disposal Room",
"description" : "A powerful burst of pain in your head wakes you up from your brief slumber, the intense throbbing causing you to see everything in a dull haze. It takes a few moments for the pain to die down, and with it your vision eventually returns to normal. After some examining you discover that you are in some sort of forgotten cell, the prison door to the west of you unlocked and slightly ajar.",
"neighbors" : {"s" : 2},
"showNeighbors" : "south",
"haveItem" : "sword"
}
I am trying to use Nodebox Graph on Ubuntu and python 2.7.
So I got Nodebox OpenGL: http://www.cityinabottle.org/nodebox/
Nodebox Graph: https://www.nodebox.net/code/index.php/Graph
I tried to run their basic example 1 :
graph = ximport("graph")
size(500, 500)
g = graph.create()
# Create some relations.
g.add_edge("roof" , "house")
g.add_edge("garden" , "house")
g.add_edge("room" , "house")
g.add_edge("kitchen" , "room")
g.add_edge("bedroom" , "room")
g.add_edge("bathroom" , "room")
g.add_edge("living room" , "room")
g.add_edge("sofa" , "living room")
g.add_edge("table" , "living room")
# Calculate a good layout.
g.solve()
# Apply default rules for node colors and size,
# for example, important nodes become blue.
g.styles.apply()
# Draw the graph, indicating the direction of each relation
# and the two nodes that get the most traffic.
g.draw(
directed=True,
traffic=1
)
That doesn't work because ximport is not defined, it is only define by nodebox, so instead I tried two things, first doing a normal import
import graph
second putting the ximport function from nodebox in my code:
def ximport(library):
from sys import modules
library = __import__(library)
library._ctx = modules[__name__]
return library
graph = ximport("graph")
size(500, 500)
g = graph.create()
# Create some relations.
g.add_edge("roof" , "house")
g.add_edge("garden" , "house")
g.add_edge("room" , "house")
g.add_edge("kitchen" , "room")
g.add_edge("bedroom" , "room")
g.add_edge("bathroom" , "room")
g.add_edge("living room" , "room")
g.add_edge("sofa" , "living room")
g.add_edge("table" , "living room")
# Calculate a good layout.
g.solve()
# Apply default rules for node colors and size,
# for example, important nodes become blue.
g.styles.apply()
# Draw the graph, indicating the direction of each relation
# and the two nodes that get the most traffic.
g.draw(
directed=True,
traffic=1
)
That still doesn't work because now the function size is not recognized. If I just comment size out, I get the following error:
self.x = _ctx.WIDTH - max.x*self.d - min_.x*self.d
AttributeError: 'NoneType' object has no attribute 'WIDTH'
What do I do?
This question could be similar:
Pydev Nodebox: "AttributeError: 'NoneType' object has no attribute 'WIDTH'"
but the given answer is not helpful at all to me.
The code in https://www.nodebox.net/code/index.php/Graph
doesn't work for nodebox opengl, it is only compatible for nodebox 1 for mac so there is no easy fix. However, nodebox opengl has it's own interactive functions to it is possible to use them instead. Here is partial example code:
from nodebox.graphics import *
from nodebox.graphics.physics import Node, Edge, Graph
class GUI():
"""Draw and interact with the damn thing.
"""
def __init__(self):
"""rawgraph is an instance of the RawGraph class
The GUI allows to interact with it, i.e. view it and change it.
"""
#Layer.__init__(self) # unknown thing, might be necessary.
#Buttons
self.EXPLORE_F = "Explore Further"
self.EXPLORE_D = "Explore Deeper"
self.DELETE = "Delete"
self.CLOSE = "CLOSE"
self.BUTTONS = False #Are buttons showing?
self.CBUTTON = False #Is Close button showing?
self.nodes = []
self.g = Graph()
self.dragged =None
self.clicked = None
def add_node(self, name, root = False):
self.nodes.append(name)
self.g.add_node(id=name, radius = 5, root = root)
def add_edge(self,n1,n2,*args,**kwargs):
self.g.add_edge(n1, n2, *args,**kwargs)
def explore_further(self,node_name):
"""action of explore further button
"""
pass
def explore_deeper(self,node_name):
"""action of explore deeper button
"""
pass
def delete_node(self,node_name):
"""action of delete button
"""
pass
def close_canvas(self):
"""Close the canvas
"""
canvas.clear()
canvas.stop()
def add_close(self):
"""Add close button
"""
self.g.add_node(self.CLOSE, radius = 10, fill=(1,0,0,0.2))
self.g.add_edge(self.rawg.root, self.CLOSE, length=0.6)
self.CBUTTON=True
def remove_close(self):
"""Remove the close button
"""
try:
self.g.remove(self.g.node(self.CLOSE))
except:
pass
self.CBUTTON=False
def add_buttons(self,node_name):
"""Add the buttons to change the graph
"""
self.g.add_node(self.EXPLORE_F, radius = 6, fill=(0,1,0,0.5))
self.g.add_node(self.EXPLORE_D, radius = 6, fill=(0,0,1,0.5))
self.g.add_node(self.DELETE,radius=6, fill=(1,0,0,0.5))
self.g.add_edge(node_name, self.EXPLORE_F, length=0.2)
self.g.add_edge(node_name, self.EXPLORE_D, length=0.2)
self.g.add_edge(node_name, self.DELETE, length=0.2)
self.BUTTONS = True
def remove_buttons(self):
"""Remove the buttons to change the graph
"""
try:
self.g.remove(self.g.node(self.DELETE))
self.g.remove(self.g.node(self.EXPLORE_D))
self.g.remove(self.g.node(self.EXPLORE_F))
except:
pass
self.BUTTONS=False
def draw(self):
canvas.clear()
background(1)
translate(500, 500)
# With directed=True, edges have an arrowhead indicating the direction of the connection.
# With weighted=True, Node.centrality is indicated by a shadow under high-traffic nodes.
# With weighted=0.0-1.0, indicates nodes whose centrality > the given threshold.
# This requires some extra calculations.
self.g.draw(weighted=0.5, directed=False)
self.g.update(iterations=10)
# Make it interactive!
# When the mouse is pressed, remember on which node.
# Drag this node around when the mouse is moved.
dx = canvas.mouse.x - 500 # Undo translate().
dy = canvas.mouse.y - 500
#global dragged
if canvas.mouse.pressed and not self.dragged:
self.dragged = self.g.node_at(dx, dy)
old_clicked = self.clicked
try:
self.clicked = self.dragged.id
except:
self.clicked = None
if self.clicked != None:
if self.clicked == self.DELETE:
if old_clicked != None:
self.delete_node(old_clicked)
self.remove_buttons()
elif self.clicked == self.EXPLORE_D:
if old_clicked != None:
self.explore_deeper(old_clicked)
self.remove_buttons()
elif self.clicked == self.EXPLORE_F:
if old_clicked != None:
self.explore_further(old_clicked)
self.remove_buttons()
elif self.clicked == self.CLOSE:
self.remove_buttons()
self.remove_close()
self.close_canvas()
else:
self.remove_buttons()
self.remove_close()
self.add_buttons(self.clicked)
else:
if self.BUTTONS:
self.remove_buttons()
elif self.CBUTTON:
self.remove_close()
else:
self.remove_buttons()
self.add_close()
if not canvas.mouse.pressed:
self.dragged = None
if self.dragged:
self.dragged.x = dx
self.dragged.y = dy
def start(self, distance = 30, force = 0.01, repulsion_radius=30):
"""Starts the GUI
"""
#self.g.prune(depth=0) # Remove orphaned nodes with no connections.
self.g.distance = distance # Overall spacing between nodes.
self.g.layout.force = force # Strength of the attractive & repulsive force.
self.g.layout.repulsion = repulsion_radius # Repulsion radius.
canvas.draw = self.draw
canvas.size = 1000, 1000
canvas.run()
if __name__ == '__main__':
gui = GUI()
gui.add_node("a")
gui.add_node("b")
gui.add_edge("a","b")
gui.start(distance=30, repulsion_radius=30)
For an online course in python, I'm making a basic text-based adventure game in python.
Right now, I have a rudimentary inventory system that works through booleans for if the user has an object or not, and integers for limited items, such as ammo and whatnot.
Here is the code for the inventory system
def Inventory(self): #The inventory for the game. I don't know how to program it properly, so this is my testing ground.
#This will hold the boolean values to if the player has the items or not. Another will be used to show the user the items
street_clothes = False
pistol = False
ammo = 0
phone = False
And this is the code where I am trying to modify the inventory function above
#Eric's apartment
def Eric_Apartment(self):
print "type in grab your gun"
action = raw_input("> ")
if action == "grab":
self.Inventory(CR97) = True
# self.CR97_ammo += 15
# print CR97_ammo
# print self.CR97_ammo
exit(1)
Attempting to run this program gets me this error:
python ex43.py
File "ex43.py", line 78
self.Inventory(CR97) = True
SyntaxError: can't assign to function call
Is there something else I'm supposed to do? I'm very new to python, and this is my first project on my own.
Here is the entire code, for reference
from sys import exit #allows the program to use the exit(1) code
from random import randint #allows the program to use a random number
class Game(object):
#quotes that pop up if the person dies, and also defines the start and self variables
def __init__(self, start):
self.quips = [
"You lose!"
]
self.start = start
def Inventory(self): #The inventory for the game.
#This will hold the boolean values to if the player has the items or not.
street_clothes = False
pistol = False
ammo = 0
phone = False
#this function launches the game, and helps with the room transfer
def play(self):
next = self.start
while True:
print "\n---------"
room = getattr(self, next)
next = room( )
#if the user dies, or fails at the game, this is the function that is ran
def death(self):
print self.quips[randint(0, len(self.quips)-1)]
exit(1)
#Welcome screen to the game
def welcome_screen(self):
print " place holder"
return 'intro_screen'
#Intro screen to the game
def intro_screen(self):
print "place holder"
action = raw_input("> Press any key to continue ")
return 'Eric_Apartment'
#Eric's apartment
def Eric_Apartment(self):
print "type in grab your gun"
action = raw_input("> ")
if action == "grab":
self.Inventory(CR97) = True
# self.CR97_ammo += 15
# print CR97_ammo
# print self.CR97_ammo
exit(1)
a_game = Game("welcome_screen")
a_game.play()
That's an amazingly perverse way to go about it. Why are you using a function to store data?
Just have a player object, with an inventory array.
I'd recommend using objects to model items too. Good use for for a class hierarchy. COuld have a base Item, with Subclasses SingleItem and StackableItem, etc.
Instead of using a function, try using a class -
class Player:
def __init__(self):
self.street_clothes = False
self.pistol = False
self.ammo = 0
self.phone = False
def give_street_clothes(self):
self.street_clothes = True
# etc
But personally, instead of using each item as a boolean, I'd use a list of items:
class Player:
def __init__(self):
self.inventory = []
# add code for ammo/pistol
def has_item(self, item):
return item in self.inventory
def give_item(self, item):
self.inventory.add(item)
def remove_item(self, item):
self.inventory.remove(item)
# etc