separated Entity creation and connection to room; improved loot system; improved inventory item stuff; added plantable seeds as loot
This commit is contained in:
parent
0a16221a99
commit
051e1bfae5
24
README.md
24
README.md
|
@ -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
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"goblin": "g",
|
||||
"seed": ":",
|
||||
"plant": "Y",
|
||||
"youngplant": "v",
|
||||
" ": " "
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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"}})
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue