separated Entity creation and connection to room; improved loot system; improved inventory item stuff; added plantable seeds as loot

This commit is contained in:
troido 2017-10-22 14:22:45 +02:00
parent 0a16221a99
commit 051e1bfae5
12 changed files with 117 additions and 67 deletions

View File

@ -15,6 +15,27 @@ A lot of functionality is actually working.
- Fighting
- NPC's
## Installation/starting instructions
Requires python3, tested to work on at least python 3.5.2 in linux
Because of the use of NCURSES and Unix Domain Sockets, it probably won't work on windows (will be fixed later)
Not tested on mac. If anyone could test this for me this would be much appreciated.
Run `hostrooms.py` from the same directory to start the server.
Run `rooms` or `playgame.py` to start the client
## Playing instructions
Use the arrow keys or wasd to move around.
Use 'e' to add an item from the ground into your inventory.
Use 'q' to drop the top item in your inventory.
Use 'r' to use/interact with the top item in your inventory.
Use 'f' to attack an enemy in the same square as you.
Use WASD to attack enemies in adjacent squares.
## Vision/ideas
@ -32,13 +53,14 @@ The idea is to make 3 different kind of areas:
- procedurally generated dungeons, where players can explore and fight for loot
* like roguelikes
* if the player dies they can not return to the same dungeon
* group dungeons would be great too
## TODO first
- better code documentation
- test server robustness
- make items usable
- farming
- farming (only harvest left to do)
- more content (rooms, objects etc)
- equipment
- configurable keybindings

View File

@ -21,6 +21,7 @@
"goblin": "g",
"seed": ":",
"plant": "Y",
"youngplant": "v",
" ": " "
}
}

View File

@ -18,5 +18,5 @@ class Build:
def use(self, user):
obj = gameobjects.makeEntity(self.buildType, self.roomData, *self.buildArgs, **self.buildKwargs)
obj.place(user.getGround())
self.owner.remove()
self.owner.trigger("drop")

View File

@ -8,6 +8,7 @@ class InputController:
def attach(self, obj, roomData):
self.owner = obj
self.roomData = roomData
for dep in {"inventory", "move", "fighter", "alignment"}:
if not obj.getComponent(dep):
@ -40,13 +41,14 @@ class InputController:
if kind == "take":
for obj in self.owner.getNearObjects():
if obj.getComponent("item") != None and self.inventory.canAdd(obj):
obj.remove()
self.inventory.add(obj)
obj.unPlace()
break
if kind == "drop":
for obj in self.inventory.getItems():
self.inventory.drop(obj)
obj.construct(self.roomData)
obj.place(self.owner.getGround())
break

View File

@ -20,6 +20,5 @@ class Inventory:
return list(self.items)
def onItemUpdate(self, item, action, *data):
if action == "remove":
print(item, action)
if action == "drop":
self.drop(item)

View File

@ -19,8 +19,18 @@ class Loot:
def dropLoot(self, obj, action, *data):
if action == "die":
for item, chance in self.items:
for itemData in self.items:
item = itemData[0]
chance = 1
args = []
kwargs = {}
if len(itemData) > 1:
chance = itemData[1]
if len(itemData) > 2:
args = itemData[2]
if len(itemData) > 3:
kwargs = itemData[3]
if chance > random.random():
# todo: args and kwargs
obj = gameobjects.makeEntity(item, self.roomData)
obj = gameobjects.makeEntity(item, self.roomData, *args, **kwargs)
obj.place(self.owner.getGround())

View File

@ -7,7 +7,11 @@ class Portal:
self.destRoom = destRoom
self.destPos = destPos
def onEnter(self, obj):
def attach(self, obj, roomData):
obj.addListener(self.onObjEvent)
obj.trigger("changeroom", self.destRoom, self.destPos)
def onObjEvent(self, owner, action, obj=None, *data):
if action == "objectenter":
obj.trigger("changeroom", self.destRoom, self.destPos)

View File

@ -14,7 +14,7 @@ class Entity:
Remove methods are for cleanup, like unsubscribing from events.
"""
def __init__(self, roomEvents, sprite=' ', solid=False, height=0, name=None, components={}):
def __init__(self, sprite=' ', solid=False, height=0, name=None, components={}):
self.sprite = sprite # the name of the image to display for this entity
self.solid = solid
self.height = height # if multiple objects are on a square, the tallest one is drawn
@ -22,13 +22,16 @@ class Entity:
self.components = components
self.observable = event.Event()
for component in components.values():
if hasattr(component, "attach"):
component.attach(self, roomEvents)
self.ground = None
self.roomData = None
pass
def construct(self, roomData):
self.roomData = roomData
for component in self.components.values():
if hasattr(component, "attach"):
component.attach(self, roomData)
def hasComponent(self, name):
return name in self.components
@ -42,18 +45,15 @@ class Entity:
ground.addObj(self)
self.ground = ground
def unPlace(self):
def remove(self):
if self.ground:
self.ground.removeObj(self)
self.ground = None
def remove(self):
self.unPlace()
for component in self.components.values():
if hasattr(component, "remove"):
component.remove()
self.trigger("remove")
self.roomData = None
def addListener(self, callback, key=None):
self.observable.addListener(callback, key)

View File

@ -22,81 +22,92 @@ from components.build import Build
entities = {}
def makeWall(roomData):
return Entity(roomData, sprite="wall", height=2, solid=True)
def makeWall():
return Entity(sprite="wall", height=2, solid=True)
entities["wall"] = makeWall
def makeRock(roomData):
return Entity(roomData, sprite="rock", height=10, solid=True)
def makeRock():
return Entity(sprite="rock", height=10, solid=True)
entities["rock"] = makeRock
def makeTree(roomData):
return Entity(roomData, sprite="tree", height=3, solid=True)
def makeTree():
return Entity(sprite="tree", height=3, solid=True)
entities["tree"] = makeTree
def makeStone(roomData):
return Entity(roomData, sprite="stone", height=0.2, components={"item": Build("wall")})
def makeStone():
return Entity(sprite="stone", height=0.2, components={"item": Build("wall")})
entities["stone"] = makeStone
def makePebble(roomData):
return Entity(roomData, sprite="pebble", height=0.2, components={"item": Item()})
def makePebble():
return Entity(sprite="pebble", height=0.2, components={"item": Item()})
entities["pebble"] = makePebble
def makeGrass(roomData):
return Entity(roomData, sprite=random.choice(["ground", "grass1", "grass2", "grass3"]), height=0.15)
def makeGrass():
return Entity(sprite=random.choice(["ground", "grass1", "grass2", "grass3"]), height=0.1)
entities["grass"] = makeGrass
def makeFloor(roomData):
return Entity(roomData, sprite="floor", height=0.1)
def makeFloor():
return Entity(sprite="floor", height=0)
entities["floor"] = makeFloor
def makeGround(roomData):
return Entity(roomData, sprite="ground", height=0.1)
def makeGround():
return Entity(sprite="ground", height=0)
entities["ground"] = makeGround
def makeWater(roomData):
return Entity(roomData, sprite="water", height=0.1, solid=True)
def makeWater():
return Entity(sprite="water", height=0, solid=True)
entities["water"] = makeWater
def makeRoomExit(roomData, destRoom, destPos=None, char="exit", size=1):
return Entity(roomData, sprite=char, height=size, components={"collision": Portal(destRoom, destPos)})
def makeRoomExit(destRoom, destPos=None, char="exit", size=1):
return Entity(sprite=char, height=size, components={"collision": Portal(destRoom, destPos)})
entities["roomexit"] = makeRoomExit
def makeRabbit(roomData):
return Entity(roomData, sprite="rabbit", height=1, components={"move": Move(slowness=4), "controller": RandomWalkController(moveChance=0.05)})
def makeRabbit():
return Entity(sprite="rabbit", height=1, components={"move": Move(slowness=4), "controller": RandomWalkController(moveChance=0.05)})
entities["rabbit"] = makeRabbit
def makeDummy(roomData):
return Entity(roomData, sprite="dummy", height=1, components={"fighter": Fighter(maxHealth=20, strength=0), "alignment": Alignment(faction.EVIL)})
def makeDummy():
return Entity(sprite="dummy", height=1, components={"fighter": Fighter(maxHealth=20, strength=0), "alignment": Alignment(faction.EVIL)})
entities["dummy"] = makeDummy
def makeSpikeTrap(roomData):
return Entity(roomData, sprite="spikes", height=1, components={"fighter": Fighter(maxHealth=25, strength=25), "collision": Trap()})
def makeSpikeTrap():
return Entity(sprite="spikes", height=1, components={"fighter": Fighter(maxHealth=25, strength=25), "collision": Trap()})
entities["spiketrap"] = makeSpikeTrap
def makeGoblin(roomData):
return Entity(roomData, sprite="goblin", height=1.2, components={
def makeGoblin():
return Entity(sprite="goblin", height=1.2, components={
"move": Move(slowness=4),
"fighter": Fighter(maxHealth=25, strength=5, slowness=3),
"alignment": Alignment(faction.EVIL),
"controller": MonsterAi(viewDist=5, moveChance=0.01),
"loot": Loot([("stone", .5), ("pebble", .5)])
"loot": Loot([("seed", .5), ("seed", .1)])
})
entities["goblin"] = makeGoblin
def makeGoblinSpawner(roomData): # I should probably generalize this...
return Entity(roomData, sprite="portal", height=1, name="goblinspawner", components={"spawn": Spawner("goblin", 2, 20)})
def makeGoblinSpawner(): # I should probably generalize this...
return Entity(sprite="portal", height=1, name="goblinspawner", components={"spawn": Spawner("goblin", 2, 20)})
entities["goblinspawner"] = makeGoblinSpawner
def makeSeed(roomData):
return Entity(roomData, sprite="seed", height=0.3, components={"grow": Growing("plant", 100)})
entities["seed"] = makeSeed
def makeSownSeed():
return Entity(sprite="seed", height=0.05, components={"grow": Growing("youngplant", 100)})
entities["sownseed"] = makeSownSeed
def makePlant(roomData):
return Entity(roomData, sprite="plant", height=1.2)
def makeYoungPlant():
return Entity(sprite="youngplant", height=0.5, components={"grow": Growing("plant", 200)})
entities["youngplant"] = makeYoungPlant
def makePlant():
return Entity(sprite="plant", height=1.2)
entities["plant"] = makePlant
def makeEntity(entType, *args, **kwargs):
return entities[entType](*args, **kwargs)
def makeSeed():
return Entity(sprite="seed", height=0.3, components={"item": Build("sownseed")})
entities["seed"] = makeSeed
def makeEntity(entType, roomData, *args, **kwargs):
entity = entities[entType](*args, **kwargs)
entity.construct(roomData)
return entity

View File

@ -6,6 +6,7 @@ from components.fighter import Fighter
from components.healing import Healing
from components.alignment import Alignment
import faction
import entity
class Player:
@ -42,7 +43,7 @@ class Player:
self.leaveRoom()
pos = place or room.getEntrance()
self.entity = room.makeEntity(
self.entity = entity.Entity(
sprite = "player",
solid = False,
height = 2,
@ -55,7 +56,7 @@ class Player:
"alignment": Alignment(faction.GOOD),
"heal": Healing(interval=25)
})
self.entity.construct(room.getRoomData())
self.entity.addListener(self.onPlayerAction)
room.addObj(pos, self.entity)

View File

@ -48,7 +48,8 @@ class Room:
kwargs = obj.get("kwargs", {})
else:
continue
self.addObj((x, y), self.makeObject(objtype, *args, **kwargs))
ent = gameobjects.makeEntity(objtype, self.roomData, *args, **kwargs)
self.addObj((x, y), ent)
def getEntrance(self):
@ -87,11 +88,8 @@ class Room:
return self._getGround(pos)
return None
def makeObject(self, objtype, *args, **kwargs):
return gameobjects.makeEntity(objtype, self.roomData, *args, **kwargs)
def makeEntity(self, *args, **kwargs):
return entity.Entity(self.roomData, *args, **kwargs)
def getRoomData(self):
return self.roomData
def addObj(self, pos, obj):
obj.place(self.get(pos))

View File

@ -39,7 +39,9 @@ def generateBeginRoom():
g.set(50, 25, ["grass", "rabbit", "rabbit", "rabbit", "rabbit"])
g.set(11, 12, ["grass", "dummy"])
g.set(37, 18, ["spiketrap"])
g.set(19, 7, ["grass", "seed"])
g.set(21, 16, ["grass", "seed"])
g.set(21, 17, ["grass", "seed"])
g.set(22, 16, ["grass", "seed"])
g.set(30, 20, {"type": "roomexit", "args": ["basement", "stairup"], "kwargs": {"char": "stairdown"}})