Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from xors3d import *
- import time, math, random
- # some constants that should not be constants actually
- BLOCK_SIZE = 20
- FIELD_SIZE = 17
- TOP_OFFSET = 40
- # random shit
- def randomizePosition(pos):
- pos.x = random.randint(1, FIELD_SIZE - 2) * BLOCK_SIZE
- pos.y = random.randint(1, FIELD_SIZE - 2) * BLOCK_SIZE + TOP_OFFSET
- # how should I detect collisions with player?
- class AppClose(Exception):
- pass
- class PlayerDed(Exception):
- def __init__(self, score):
- self.score = score
- def addSegment(tail, world):
- '''Oh, dog, this whole function suck so much you have no idea'''
- em = world.entityManager
- entity = em.createEntity()
- if tail.numSegments > 0:
- pos = world.entityManager.getComponent(tail.segments[-1], Position)
- else:
- pos = world.entityManager.getComponent(tail.owner, Position)
- em.addComponent(entity, Position(pos.x, pos.y))
- em.addComponent(entity, Shape('square', BLOCK_SIZE, BLOCK_SIZE, 0xff00aa00))
- em.addComponent(entity, Name('tail'))
- em.addComponent(entity, BBox(BLOCK_SIZE, BLOCK_SIZE))
- tail.numSegments += 1
- tail.segments.append(entity)
- def loadTestImage():
- return xLoadImage('images/sand48.png')
- def GetDrawingColor():
- return xColorRed(), xColorGreen(), xColorBlue(), xColorAlpha()
- def alpha(clr):
- return (clr >> 24) & 0xff
- def red(clr):
- return (clr >> 16) & 0xff
- def green(clr):
- return (clr >> 8) & 0xff
- def blue(clr):
- return clr & 0xff
- def mkcolor(a, r, g, b):
- return ((a & 0xff) << 24 |
- (r & 0xff) << 16 |
- (g & 0xff) << 8 |
- b & 0xff)
- # components
- class MouseResponce(object):
- def __init__(self, btn, resp):
- self.button = btn
- self.action = resp
- class TextInfo(object):
- def __init__(self, text, getter):
- self.text = text
- self.getter = getter
- class Tail(object):
- def __init__(self, owner):
- self.numSegments = 0
- self.segments = []
- self.owner = owner
- class AlphaBlink(object):
- pass
- class BBox(object):
- def __init__(self, width, height):
- self.width = width
- self.height = height
- class Physics(object):
- '''Determines if object can be collided and type of collision
- or smth like that'''
- def __init__(self, colType):
- self.colType = colType
- class Collided(object):
- def __init__(self, other):
- self.other = other
- class Name(object):
- def __init__(self, name):
- self.name = name
- class Score(object):
- def __init__(self):
- self.score = 0
- class Harmful(object):
- pass
- class Harmless(object):
- pass
- class Destructible(object):
- pass
- class Shape(object):
- def __init__(self, shape, width, height, color):
- self.shape = shape
- self.width = width
- self.height = height
- self.color = color
- class Image(object):
- '''Unused'''
- def __init__(self, img):
- self.image = img
- class Position(object):
- def __init__(self, x, y):
- self.x = x
- self.y = y
- class KeyboardController(object):
- pass
- class Direction(object):
- def __init__(self, dx, dy):
- self.dx = dx
- self.dy = dy
- class Speed(object):
- def __init__(self, speed):
- self.speed = speed
- # systems
- # DON'T DO THIS AT HOME
- # THIS IS SOME QUICK WAY TO FIX SHIT
- can_act = False
- class TailController(object):
- def __init__(self):
- pass
- def update(self, world):
- if not can_act:
- return
- all = world.getAll((Tail,))
- # basically, do not append Tail for smth that isn't player, lol
- # I can ask for Position and Direction also
- for i in all:
- tail = world.entityManager.getComponent(i, Tail)
- pos = world.entityManager.getComponent(tail.owner, Position)
- direct = world.entityManager.getComponent(tail.owner, Direction)
- prevx, prevy = pos.x - BLOCK_SIZE * direct.dx, pos.y - BLOCK_SIZE * direct.dy
- for j in tail.segments:
- pos = world.entityManager.getComponent(j, Position)
- pos.x, pos.y, prevx, prevy = prevx, prevy, pos.x, pos.y # WHAT?
- if (tail.numSegments > 0 and
- world.entityManager.getComponent(tail.segments[-1], Physics) is None):
- world.entityManager.addComponent(j, Physics(Harmful))
- class TextRenderer(object):
- def __init__(self):
- pass
- def update(self, world):
- all = world.getAll((TextInfo,))
- for i in all:
- pos = world.entityManager.getComponent(i, Position)
- text = world.entityManager.getComponent(i, TextInfo)
- if text.getter:
- xText(pos.x, pos.y, text.text + text.getter())
- else:
- xText(pos.x, pos.y, text.text)
- class ArrowControls(object):
- def __init__(self):
- pass
- def update(self, world):
- all = world.getAll((Direction, KeyboardController))
- xd = (xKeyDown(KEY_RIGHT) - xKeyDown(KEY_LEFT))
- yd = (xKeyDown(KEY_DOWN) - xKeyDown(KEY_UP))
- if xd == yd == 0:
- return
- for i in all:
- dir = world.entityManager.getComponent(i, Direction)
- dir.dx = xd
- dir.dy = yd
- class EscapeHandler(object):
- def __init__(self):
- pass
- def update(self, world):
- if xKeyHit(KEY_ESCAPE):
- raise AppClose() # nice hack
- class XorsRenderer(object):
- def __init__(self):
- xGraphics3D(FIELD_SIZE * BLOCK_SIZE, TOP_OFFSET + FIELD_SIZE * BLOCK_SIZE)
- def update(self, world):
- all = world.getAll((Position, Shape))
- xCls()
- prevColor = GetDrawingColor()
- for i in all:
- pos = world.entityManager.getComponent(i, Position)
- shape = world.entityManager.getComponent(i, Shape)
- clr = shape.color
- if shape.shape == 'square':
- xColor(red(clr), green(clr), blue(clr), alpha(clr))
- xRect(pos.x, pos.y, shape.width, shape.height, True)
- # xColor(0x77, 0x77, 0x77)
- # xRect(pos.x, pos.y, shape.width, shape.height, False)
- else:
- raise ValueError('Invalid shape ' + shape)
- xColor(*prevColor)
- # xRenderWorld()
- # xText(5, 5, 'Fps: ' + str(xGetFPS()))
- def endFrame(self):
- xFlip()
- class ContinuousMotion(object):
- def __init__(self):
- self.lastUpdate = xMillisecs()
- def update(self, world):
- global can_act
- time = xMillisecs()
- if time - self.lastUpdate >= 150:
- self.lastUpdate = time
- can_act = True
- else:
- can_act = False
- return
- # this is boilerplate code
- # those next 4 (+/-) lines will be in every system
- # so update() should receive all the entities and components somehow
- all = world.getAll((Position, Direction, Speed))
- for i in all:
- pos = world.entityManager.getComponent(i, Position)
- dir = world.entityManager.getComponent(i, Direction)
- spd = world.entityManager.getComponent(i, Speed).speed
- pos.x += dir.dx * spd
- pos.y += dir.dy * spd
- class CollisionDetection(object):
- def __init__(self):
- pass
- def update(self, world):
- if not can_act: return # FIXME: HACK
- # if has Physics then can be collided
- # if no then we do not care if it has BBox or not
- all = world.getAll((Physics, Position, BBox))
- i = 0
- la = len(all)
- while i < la:
- p1 = world.entityManager.getComponent(all[i], Position)
- b1 = world.entityManager.getComponent(all[i], BBox)
- j = i + 1
- while j < la:
- p2 = world.entityManager.getComponent(all[j], Position)
- b2 = world.entityManager.getComponent(all[j], BBox)
- if xRectsOverlap(p1.x, p1.y, b1.width, b1.height,
- p2.x, p2.y, b2.width, b2.height):
- world.addComponent(all[i], Collided(all[j]))
- world.addComponent(all[j], Collided(all[i]))
- j += 1
- i+=1
- class CollisionResponce(object):
- def __init__(self):
- pass
- def update(self, world):
- if not can_act: return # FIXME: HACK
- all = world.getAll((Collided,))
- for i in all:
- other = world.entityManager.getComponent(i, Collided).other
- # if it's collided then it have Physics
- ct = world.entityManager.getComponent(other, Physics).colType
- if ct == Harmful:
- # print world.entityManager.getComponent(i, Name).name.capitalize(),\
- # 'collided hard with', world.entityManager.getComponent(other, Name).name
- if world.entityManager.getComponent(i, KeyboardController):
- world.removeComponent(i, Collided)
- raise PlayerDed(world.entityManager.getComponent(i, Score).score)
- elif ct == Destructible:
- # TODO: place food at random position and update user score
- pos = world.entityManager.getComponent(other, Position)
- randomizePosition(pos)
- # this should be in some score managing system (maybe?)
- # or will events be better?
- score = world.entityManager.getComponent(i, Score)
- if score: # if object has no score component then just ignore it
- score.score += 20
- # oh, boy...
- tail = world.entityManager.getComponent(i, Tail)
- if tail:
- addSegment(tail, world)
- world.removeComponent(i, Collided)
- class MouseHandler(object):
- def __init__(self):
- pass
- def update(self, world):
- # I NEED POST UPDATE
- ml, mr = xMouseDown(MOUSE_LEFT), xMouseDown(MOUSE_RIGHT)
- xFlushMouse()
- if ml == mr == 0: return
- mx, my = xMouseX(), xMouseY()
- all = world.getAll((MouseResponce, BBox, Position))
- for i in all:
- resp = world.entityManager.getComponent(i, MouseResponce)
- bbox = world.entityManager.getComponent(i, BBox)
- pos = world.entityManager.getComponent(i, Position)
- if (((resp.button == MOUSE_LEFT and ml) or (resp.button == MOUSE_RIGHT and mr)) # this was better, but I need to flush
- and
- (pos.x <= mx <= pos.x + bbox.width and pos.y <= my <= pos.y + bbox.height)):
- resp.action()
- return
- class AlphaWave(object):
- def __init__(self):
- pass
- def update(self, world):
- if not can_act: return # FIXME: HACK
- all = world.getAll((Shape, AlphaBlink))
- for i in all:
- shape = world.entityManager.getComponent(i, Shape)
- shape.color = ((int(math.cos(xMillisecs() / 100.0) * 200 + 55) << 24) |
- (shape.color & 0xffffff)) # why is this shit working weird?
- # managers
- class World(object):
- def __init__(self):
- self.systems = []
- self.entityManager = None
- # def getComponent(self, entity, component):
- # return self.entityManager.getComponent(entity, component)
- def addComponent(self, entity, component):
- self.entityManager.addComponent(entity, component)
- def removeComponent(self, entity, component):
- self.entityManager.removeComponent(entity, component)
- def getAll(self, components):
- comp = components[0]
- entities = self.entityManager.get(comp)
- for i in components[1:]: # this sucks
- entities = self.entityManager.filter(entities, i)
- return entities
- def registerSystem(self, info):
- # info is (priority, system) pair
- self.systems.append(info)
- self.systems.sort(key=lambda x: x[0])
- def setEntityManager(self, manager):
- self.entityManager = manager
- def update(self):
- for _, system in self.systems:
- system.update(self)
- time.sleep(1.0/120) # I'm losing 40 (80 with 30 objects) fps just because of python
- class EntityManager(object):
- def __init__(self):
- self.entities = set()
- # a lot of empty big arrays
- # probably should use dictionaries (not lists) with entity->component mapping
- self.comps = {
- Position.__name__: [],
- # Image.__name__: [],
- KeyboardController.__name__: [],
- Shape.__name__: [],
- Direction.__name__: [],
- Speed.__name__: [],
- BBox.__name__: [],
- Physics.__name__: [],
- Collided.__name__: [],
- Name.__name__: [],
- AlphaBlink.__name__: [],
- Score.__name__: [],
- Tail.__name__: [],
- TextInfo.__name__: [],
- MouseResponce.__name__: []
- }
- # def getArray(self, component):
- # return self.comps[component.__name__]
- def addComponent(self, entity, component):
- array = self.comps[component.__class__.__name__]
- while len(array) <= entity:
- array.append(None)
- array[entity] = component
- def removeComponent(self, entity, component):
- # array = self.getArray(component)
- array = self.comps[component.__name__]
- array[entity] = None
- def getComponent(self, entity, component):
- # array = self.getArray(component)
- array = self.comps[component.__name__]
- if len(array) <= entity:
- return None
- return array[entity]
- def get(self, component):
- # array = self.getArray(component)
- array = self.comps[component.__name__]
- return [i for i, x in enumerate(array) if not x is None] # this sucks
- def filter(self, entities, component):
- # array = self.getArray(component)
- array = self.comps[component.__name__]
- length = len(array)
- return [i for i in entities if length > i if not array[i] is None] # this sucks too
- def createEntity(self):
- i = 0 # yeah, itertools, but too lazy to search for docs
- while True:
- if i not in self.entities:
- self.entities.add(i)
- return i
- i += 1
- def destroyEntity(self, entity):
- self.entities.remove(entity)
- for name in self.comps:
- arr = self.comps[name]
- if len(arr) > entity:
- arr[entity] = None
- # main function
- def main():
- renderer = XorsRenderer() # should be created first
- world = World()
- em = EntityManager()
- world.setEntityManager(em)
- # because I don't know how to bump player to the front
- for i in xrange(FIELD_SIZE):
- entity = em.createEntity()
- em.addComponent(entity, Shape('square', BLOCK_SIZE, BLOCK_SIZE, 0xffff0000))
- em.addComponent(entity, Position(i * BLOCK_SIZE, TOP_OFFSET))
- em.addComponent(entity, BBox(BLOCK_SIZE, BLOCK_SIZE))
- em.addComponent(entity, Physics(Harmful))
- em.addComponent(entity, Name('block'))
- entity = em.createEntity()
- em.addComponent(entity, Shape('square', BLOCK_SIZE, BLOCK_SIZE, 0xffff0000))
- em.addComponent(entity, Position(i * BLOCK_SIZE, TOP_OFFSET + (FIELD_SIZE - 1) * BLOCK_SIZE))
- em.addComponent(entity, BBox(BLOCK_SIZE, BLOCK_SIZE))
- em.addComponent(entity, Physics(Harmful))
- em.addComponent(entity, Name('block'))
- for i in xrange(FIELD_SIZE - 2):
- entity = em.createEntity()
- em.addComponent(entity, Shape('square', BLOCK_SIZE, BLOCK_SIZE, 0xffff0000))
- em.addComponent(entity, Position(0, TOP_OFFSET + i * BLOCK_SIZE + BLOCK_SIZE))
- em.addComponent(entity, BBox(BLOCK_SIZE, BLOCK_SIZE))
- em.addComponent(entity, Physics(Harmful))
- em.addComponent(entity, Name('block'))
- entity = em.createEntity()
- em.addComponent(entity, Shape('square', BLOCK_SIZE, BLOCK_SIZE, 0xffff0000))
- em.addComponent(entity, Position((FIELD_SIZE - 1) * BLOCK_SIZE, TOP_OFFSET + i * BLOCK_SIZE + BLOCK_SIZE))
- em.addComponent(entity, BBox(BLOCK_SIZE, BLOCK_SIZE))
- em.addComponent(entity, Physics(Harmful))
- em.addComponent(entity, Name('block'))
- # food
- # TODO: create food after game start
- entity = em.createEntity()
- em.addComponent(entity, Shape('square', BLOCK_SIZE, BLOCK_SIZE, 0xff0000ff))
- foodPos = Position(200, 220) # will be updated later
- em.addComponent(entity, foodPos)
- em.addComponent(entity, BBox(BLOCK_SIZE, BLOCK_SIZE))
- em.addComponent(entity, Physics(Destructible))
- em.addComponent(entity, Name('food'))
- em.addComponent(entity, AlphaBlink())
- # PLAYER SHOULD BE LAST
- entity = em.createEntity()
- player = entity
- em.addComponent(entity, Shape('square', BLOCK_SIZE, BLOCK_SIZE, 0xff00ff00))
- em.addComponent(entity, Position(100, 100)) # SHOULD BE RANDOM
- em.addComponent(entity, KeyboardController())
- em.addComponent(entity, Direction(0, 0))
- em.addComponent(entity, Speed(BLOCK_SIZE))
- em.addComponent(entity, BBox(BLOCK_SIZE, BLOCK_SIZE))
- em.addComponent(entity, Physics(Harmless))
- em.addComponent(entity, Name('player'))
- em.addComponent(entity, Score())
- em.addComponent(entity, Tail(entity))
- def getScore(entity=entity):
- score = world.entityManager.getComponent(entity, Score)
- return str(score.score)
- entity = em.createEntity() # TODO: some UI drawing system
- em.addComponent(entity, Position(5, 5)) # ?
- em.addComponent(entity, TextInfo('Score: ', getScore))
- entity = em.createEntity()
- em.addComponent(entity, Position(5, 18))
- em.addComponent(entity, TextInfo('Fps: ', lambda: str(xGetFPS())))
- # TODO: some way to say shit like "run this system at 250 ms interval"
- # and not that can_act hack
- world.registerSystem((1, EscapeHandler()))
- world.registerSystem((2, ArrowControls()))
- world.registerSystem((16, renderer)) # oh, wow, looks like I can't postprocess, because renderer clear before rendering and flips immediately afterwards
- world.registerSystem((15, AlphaWave()))
- world.registerSystem((10, ContinuousMotion()))
- world.registerSystem((12, CollisionDetection()))
- world.registerSystem((14, CollisionResponce()))
- world.registerSystem((11, TailController()))
- world.registerSystem((20, TextRenderer()))
- world.registerSystem((3, MouseHandler()))
- gameEntityManager = em
- menuEntityManager = EntityManager()
- world.setEntityManager(menuEntityManager)
- # menu
- em = menuEntityManager
- # label
- label = [
- ' ##### # # # # # #### ',
- ' # # # ## # # # ',
- ' ##### ## # # # ### ## ',
- ' # # ## #### # # # ',
- ' ##### # # # # # # #### '
- ]
- sx, sy = 10, 10
- for y, line in enumerate(label):
- for x, char in enumerate(line):
- if char == '#':
- entity = em.createEntity()
- em.addComponent(entity, Shape('square', BLOCK_SIZE / 2, BLOCK_SIZE / 2, 0xff44aa44))
- em.addComponent(entity, Position(sx + x * BLOCK_SIZE / 2, sy + y * BLOCK_SIZE / 2))
- def restartPlayer():
- # oh dog, so much hacks
- gameEntityManager.addComponent(player, Score())
- pos = gameEntityManager.getComponent(player, Position)
- dir = gameEntityManager.getComponent(player, Direction)
- tail = gameEntityManager.getComponent(player, Tail)
- pos.x, pos.y = FIELD_SIZE // 2 * BLOCK_SIZE, FIELD_SIZE // 2 * BLOCK_SIZE + TOP_OFFSET
- dir.dx, dir.dy = 0, 0
- for i in tail.segments:
- gameEntityManager.destroyEntity(i)
- tail.segments = []
- tail.numSegments = 0
- # new game button
- entity = em.createEntity()
- em.addComponent(entity, Shape('square', 120, 20, 0xff4444aa))
- em.addComponent(entity, Position(20, 5 * BLOCK_SIZE / 2 + 20 + 20))
- em.addComponent(entity, TextInfo('New Game', None)) # TODO: some way to center text
- em.addComponent(entity, BBox(120, 20))
- em.addComponent(entity, MouseResponce(MOUSE_LEFT, lambda: world.setEntityManager(gameEntityManager)))
- # em.addComponent(entity, AutoCenter(HORZ, VERTICAL))
- # exit game button
- entity = em.createEntity()
- em.addComponent(entity, Shape('square', 120, 20, 0xff4444aa))
- em.addComponent(entity, Position(20, 5 * BLOCK_SIZE / 2 + 20 + 20 + 5 + 20)) # 20 + 20 + 5))
- em.addComponent(entity, TextInfo('Exit Game', None)) # TODO: some way to center text
- em.addComponent(entity, BBox(120, 20))
- def lololol():
- raise AppClose
- em.addComponent(entity, MouseResponce(MOUSE_LEFT, lololol))
- # em.addComponent(entity, AutoCenter(HORZ, VERTICAL))
- # maybe not AutoCenter, but some way to make object always be at some position (in percentage, not pixels)
- # game over screen
- gameOverScoreHack = 0
- em = EntityManager()
- gameOverManager = em
- entity = em.createEntity()
- em.addComponent(entity, Position(20, 20))
- em.addComponent(entity, Shape('square', 180, 20, 0xffaa44aa))
- em.addComponent(entity, TextInfo('Final Score: ', lambda: str(gameOverScoreHack)))
- entity = em.createEntity()
- em.addComponent(entity, Position(20, 80))
- em.addComponent(entity, Shape('square', 120, 20, 0xff4444aa))
- em.addComponent(entity, BBox(120, 20))
- em.addComponent(entity, MouseResponce(MOUSE_LEFT, lambda: world.setEntityManager(menuEntityManager)))
- em.addComponent(entity, TextInfo('OK', None))
- xAppTitle('SHNAKE') # can name be passed into constructor of renderer?
- restartPlayer()
- # ...
- randomizePosition(foodPos)
- _ = gameEntityManager.getComponent(player, Position)
- px, py = _.x, _.y
- while foodPos.x == px and foodPos.y == py:
- randomizePosition(foodPos)
- while not xWinMessage('WM_CLOSE'):
- try: # kinda ugly
- # renderer.startFrame?
- world.update()
- renderer.endFrame()
- except PlayerDed as pd:
- gameOverScoreHack = pd.score
- restartPlayer() # should check food collision here as well
- world.setEntityManager(gameOverManager)
- except AppClose:
- return
- if __name__ == '__main__':
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement