diff --git a/assets/custom_player.png b/assets/custom_player.png new file mode 100644 index 0000000..0a32f4b Binary files /dev/null and b/assets/custom_player.png differ diff --git a/src/GameEngine.cpp b/src/GameEngine.cpp index a35700b..74d4b55 100644 --- a/src/GameEngine.cpp +++ b/src/GameEngine.cpp @@ -24,7 +24,7 @@ GameEngine::GameEngine() McRFPy_API::executePyString("from UIMenu import *"); McRFPy_API::executePyString("from Grid import *"); - scenes["py"] = new PythonScene(this, "MusicScene"); + scenes["py"] = new PythonScene(this, "TestScene"); IndexSprite::game = this; diff --git a/src/McRFPy_API.cpp b/src/McRFPy_API.cpp index 560891f..95e3d82 100644 --- a/src/McRFPy_API.cpp +++ b/src/McRFPy_API.cpp @@ -575,12 +575,14 @@ PyObject* McRFPy_API::_listGrids(PyObject*, PyObject*) { auto grid = it->second; auto p = grid->box.getPosition(); auto s = grid->box.getSize(); - PyObject* grid_args = Py_BuildValue("(siiiiiii)", + PyObject* grid_args = Py_BuildValue("(siiiiiiiO)", title.c_str(), (int)grid->grid_x, (int)grid->grid_y, (int)grid->grid_size, - (int)p.x, (int)p.y, (int)s.x, (int)s.y); + (int)p.x, (int)p.y, (int)s.x, (int)s.y, + grid->visible? Py_True: Py_False); - //std::cout << PyUnicode_AsUTF8(PyObject_Repr(grid_args)) << std::endl; + grid->visible ? Py_INCREF(Py_True) : Py_INCREF(Py_False); + std::cout << PyUnicode_AsUTF8(PyObject_Repr(grid_args)) << std::endl; PyObject* gridobj = PyObject_CallObject((PyObject*) grid_type, grid_args); //std::cout << (long)gridobj << std::flush <points[i].tile_overlay = PyLong_AsLong(PyObject_GetAttrString(gpointobj, "tile_overlay")); grid->points[i].uisprite = PyLong_AsLong(PyObject_GetAttrString(gpointobj, "uisprite")); } + + PyObject* entlist = PyObject_GetAttrString(o, "entities"); + //std::cout << PyUnicode_AsUTF8(PyObject_Repr(entlist)) << std::endl; + for (int i = 0; i < grid->entities.size(); i++) { + PyObject* entobj = PyList_GetItem(entlist, i); + //std::cout << PyUnicode_AsUTF8(PyObject_Repr(entobj)) << std::endl; + grid->entities[i]->cGrid->x = PyLong_AsLong(PyObject_GetAttrString(entobj, "x")); + grid->entities[i]->cGrid->y = PyLong_AsLong(PyObject_GetAttrString(entobj, "y")); + grid->entities[i]->cGrid->indexsprite.texture_index = PyLong_AsLong(PyObject_GetAttrString(entobj, "tex_index")); + grid->entities[i]->cGrid->indexsprite.sprite_index = PyLong_AsLong(PyObject_GetAttrString(entobj, "sprite_index")); + } Py_INCREF(Py_None); return Py_None; @@ -964,3 +977,38 @@ PyObject* McRFPy_API::_listEntities(PyObject* self, PyObject* args) { return Py_None; } */ + +void McRFPy_API::player_input(int dx, int dy) { + std::cout << "# entities tagged 'player': " << McRFPy_API::entities.getEntities("player").size() << std::endl; + auto player_entity = McRFPy_API::entities.getEntities("player")[0]; + auto grid = player_entity->cGrid->grid; + std::cout << "Grid pointed to: " << (long)player_entity->cGrid->grid << std::endl; + if (!McRFPy_API::input_mode.compare("computerturn")) { + // no input accepted while computer moving + std::cout << "Can't move while computer is moving." << std::endl; + return; + } + // TODO: selection cursor via keyboard + // else if (!input_mode.compare("selectpoint") {} + // else if (!input_mode.compare("selectentity") {} + + // grid bounds check + if (player_entity->cGrid->x + dx < 0 || + player_entity->cGrid->y + dy < 0 || + player_entity->cGrid->x + dx > grid->grid_x - 1 || + player_entity->cGrid->y + dy > grid->grid_y - 1) { + std::cout << "(" << player_entity->cGrid->x << ", " << player_entity->cGrid->y << + ") + (" << dx << ", " << dy << ") is OOB." << std::endl; + return; + } + std::cout << PyUnicode_AsUTF8(PyObject_Repr(player_entity->cBehavior->object)) << std::endl; + PyObject* move_fn = PyObject_GetAttrString(player_entity->cBehavior->object, "move"); + std::cout << PyUnicode_AsUTF8(PyObject_Repr(move_fn)) << std::endl; + if (move_fn) { + std::cout << "Calling `move`" << std::endl; + PyObject* move_args = Py_BuildValue("(ii)", dx, dy); + PyObject_CallObject((PyObject*) move_fn, move_args); + } else { + std::cout << "player_input called on entity with no `move` method" << std::endl; + } +} diff --git a/src/McRFPy_API.h b/src/McRFPy_API.h index 6effe2a..22befbd 100644 --- a/src/McRFPy_API.h +++ b/src/McRFPy_API.h @@ -106,6 +106,10 @@ public: // turn cycle static int turn_number; static PyObject* _turnNumber(PyObject*, PyObject*); + + // accept keyboard input from scene + static sf::Vector2i cursor_position; + static void player_input(int, int); // Jank Functionality static UIMenu* createMenu(int posx, int posy, int sizex, int sizey); diff --git a/src/PythonScene.cpp b/src/PythonScene.cpp index 315aac5..f90abb3 100644 --- a/src/PythonScene.cpp +++ b/src/PythonScene.cpp @@ -212,6 +212,15 @@ void PythonScene::doAction(std::string name, std::string type) { // try zoom out doZoom(mousepos, -1); } + else if (ACTIONONCE("up")) { McRFPy_API::player_input(+0, -1); } + else if (ACTIONONCE("upright")) { McRFPy_API::player_input(+1, -1); } + else if (ACTIONONCE("right")) { McRFPy_API::player_input(+1, +0); } + else if (ACTIONONCE("downright")) { McRFPy_API::player_input(+1, +1); } + else if (ACTIONONCE("down")) { McRFPy_API::player_input(+0, +1); } + else if (ACTIONONCE("downleft")) { McRFPy_API::player_input(-1, +1); } + else if (ACTIONONCE("left")) { McRFPy_API::player_input(-1, +0); } + else if (ACTIONONCE("upleft")) { McRFPy_API::player_input(-1, -1); } + else if (ACTIONONCE("wait")) { McRFPy_API::player_input(+0, +0); } } void PythonScene::sRender() { diff --git a/src/scripts/TestScene.py b/src/scripts/TestScene.py index b103457..1b20b50 100644 --- a/src/scripts/TestScene.py +++ b/src/scripts/TestScene.py @@ -8,8 +8,90 @@ WHITE = (255, 255, 255) RED, GREEN, BLUE = (255, 0, 0), (0, 255, 0), (0, 0, 255) DARKRED, DARKGREEN, DARKBLUE = (192, 0, 0), (0, 192, 0), (0, 0, 192) +animations_in_progress = 0 + class TestEntity: - pass + def __init__(self, grid, label, tex_index, basesprite, x, y, texture_width=64, walk_frames=5, attack_frames=6): + self.grid = grid + self.basesprite = basesprite + self.texture_width = texture_width + self.walk_frames = walk_frames + self.attack_frames = attack_frames + self.x = x + self.y = y + self.facing_direction = 0 + #print(f"Calling C++ with: {repr((self.grid, label, tex_index, self.basesprite, x, y, self))}") + grids = mcrfpy.listGrids() + for g in grids: + if g.title == self.grid: + self.entity_index = len(g.entities) + mcrfpy.createEntity(self.grid, label, tex_index, self.basesprite, x, y, self) + + def move(self, dx, dy): + # select animation direction + # prefer left or right for diagonals. + if (dx == 0 and dy == 0): + direction = self.facing_direction # TODO, jump straight to computer turn + elif (dx): + direction = 2 if dx == +1 else 3 + else: + direction = 0 if dy == +1 else 1 + self.animate(direction, move=True, animove=(self.x + dx, self.y+dy)) + self.facing_direction = direction + + + def animate(self, direction, attacking=False, move=False, animove=None): + start_sprite = self.basesprite + (self.texture_width * (direction + (4 if attacking else 0))) + animation_frames = [start_sprite + i for i in range((self.attack_frames if attacking else self.walk_frames))] + mcrfpy.createAnimation( + 0.25, # duration, seconds + self.grid, # parent: a UIMenu or Grid key + "entity", # target type: 'menu', 'grid', 'caption', 'button', 'sprite', or 'entity' + self.entity_index, # target id: integer index for menu or grid objs; None for grid/menu + "sprite", # field: 'position', 'size', 'bgcolor', 'textcolor', or 'sprite' + self.animation_done, #callback: callable once animation is complete + False, #loop: repeat indefinitely + animation_frames # values: iterable of frames for 'sprite', lerp target for others + ) + global animations_in_progress + animations_in_progress += 1 + if move: + pos = [self.x, self.y] + if (direction == 0): pos[1] += 1 + elif (direction == 1): pos[1] -= 1 + elif (direction == 2): pos[0] += 1 + elif (direction == 3): pos[0] -= 1 + if not animove: + self.x, self.y = pos + animove = pos + else: + pos = animove + self.x, self.y = animove + #scene.move_entity(self.grid, self.entity_index, pos) + for g in mcrfpy.listGrids(): + if g.title == self.grid: + g.entities[self.entity_index].x = pos[0] + g.entities[self.entity_index].y = pos[1] + mcrfpy.modGrid(g) + if animove: + mcrfpy.createAnimation( + 0.25, # duration, seconds + self.grid, # parent: a UIMenu or Grid key + "entity", # target type: 'menu', 'grid', 'caption', 'button', 'sprite', or 'entity' + self.entity_index, # target id: integer index for menu or grid objs; None for grid/menu + "position", # field: 'position', 'size', 'bgcolor', 'textcolor', or 'sprite' + self.animation_done, #callback: callable once animation is complete + False, #loop: repeat indefinitely + animove # values: iterable of frames for 'sprite', lerp target for others + ) + animations_in_progress += 1 + + + def animation_done(self): + global animations_in_progress + animations_in_progress -= 1 + #print(f"{self} done animating") + # if animations_in_progress == 0: mcrfpy.unlockPlayerInput() class TestScene: def __init__(self, ui_name = "demobox1", grid_name = "demogrid"): @@ -18,24 +100,25 @@ class TestScene: mcrfpy.createTexture("./assets/test_portraits.png", 32, 8, 8) #0 - portraits mcrfpy.createTexture("./assets/alives_other.png", 16, 64, 64) #1 - TinyWorld NPCs mcrfpy.createTexture("./assets/alives_other.png", 32, 32, 32) #2 - TinyWorld NPCs - 2x2 sprite + mcrfpy.createTexture("./assets/custom_player.png", 16, 5, 13) #3 - player self.ui_name = ui_name self.grid_name = grid_name print("Create UI") # Create dialog UI - mcrfpy.createMenu(ui_name, 20, 520, 500, 200) + mcrfpy.createMenu(ui_name, 20, 540, 500, 200) #mcrfpy.createCaption(ui_name, "Hello There", 18, BLACK) mcrfpy.createCaption(ui_name, "", 24, RED) #mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKBLUE, (0, 0, 0), "clicky", "testaction") - mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKRED, (0, 0, 0), "REPL", "startrepl") - mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKGREEN, (0, 0, 0), "map gen", "gridgen") + mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKRED, (0, 0, 0), "REPL", "startrepl") + mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKGREEN, (0, 0, 0), "map gen", "gridgen") #mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKGREEN, (0, 0, 0), "mapL", "gridgen2") #mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKBLUE, (192, 0, 0), "a_menu", "animtest") #mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKRED, GREEN, "a_spr", "animtest2") - mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKBLUE, GREEN, "Next sp", "nextsp") - mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKBLUE, RED, "Prev sp", "prevsp") - mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKBLUE, RED, "+16 sp", "skipsp") - mcrfpy.createSprite(ui_name, 2, randint(0, 3), 20, 40, 5.0) + mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKBLUE, GREEN, "Next sp", "nextsp") + mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKBLUE, RED, "Prev sp", "prevsp") + mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKBLUE, DARKGREEN, "+16 sp", "skipsp") + mcrfpy.createSprite(ui_name, 1, 0, 20, 40, 3.0) print("Create UI 2") entitymenu = "entitytestmenu" @@ -46,7 +129,7 @@ class TestScene: mcrfpy.createButton(entitymenu, 0, 110, 150, 40, DARKBLUE, BLACK, "Left", "test_left") mcrfpy.createButton(entitymenu, 0, 160, 150, 40, DARKBLUE, BLACK, "Right", "test_right") mcrfpy.createButton(entitymenu, 0, 210, 150, 40, DARKBLUE, BLACK, "Attack", "test_attack") - + mcrfpy.createButton(entitymenu, 0, 210, 150, 40, DARKBLUE, RED, "TE", "testent") print("Make UIs visible") self.menus = mcrfpy.listMenus() self.menus[0].visible = True @@ -54,22 +137,42 @@ class TestScene: self.menus[1].visible = True mcrfpy.modMenu(self.menus[0]) mcrfpy.modMenu(self.menus[1]) + print(mcrfpy.listMenus()) print("Create grid") # create grid (title, gx, gy, gs, x, y, w, h) mcrfpy.createGrid(grid_name, 20, 20, 16, 20, 20, 800, 500) self.grids = mcrfpy.listGrids() + print(self.grids) print("Create entities") - mcrfpy.createEntity("demogrid", "dragon", 2, 545, 10, 10, lambda: None) - mcrfpy.createEntity("demogrid", "tinyenemy", 1, 1538, 3, 6, lambda: None) + #mcrfpy.createEntity("demogrid", "dragon", 2, 545, 10, 10, lambda: None) + #mcrfpy.createEntity("demogrid", "tinyenemy", 1, 1538, 3, 6, lambda: None) + + print("Create fancy entity") + self.tes = [ + TestEntity("demogrid", "classtest", 1, 1538, 5, 7, 64, walk_frames=5, attack_frames=6), + TestEntity("demogrid", "classtest", 1, 1545, 7, 9, 64, walk_frames=5, attack_frames=6), + TestEntity("demogrid", "classtest", 1, 1552, 9, 11, 64, walk_frames=5, attack_frames=6), + TestEntity("demogrid", "classtest", 1, 1566, 11, 13, 64, walk_frames=4, attack_frames=6), + TestEntity("demogrid", "classtest", 1, 1573, 13, 15, 64, walk_frames=4, attack_frames=6), + TestEntity("demogrid", "classtest", 1, 1583, 15, 17, 64, walk_frames=4, attack_frames=6), + TestEntity("demogrid", "classtest", 1, 130, 9, 7, 64, walk_frames=5, attack_frames=6), + TestEntity("demogrid", "classtest", 1, 136, 11, 9, 64, walk_frames=5, attack_frames=6), + TestEntity("demogrid", "classtest", 1, 143, 13, 11, 64, walk_frames=5, attack_frames=6), + TestEntity("demogrid", "classtest", 1, 158, 15, 13, 64, walk_frames=5, attack_frames=6), + TestEntity("demogrid", "classtest", 1, 165, 17, 15, 64, walk_frames=5, attack_frames=6), + TestEntity("demogrid", "player", 3, 20, 17, 3, 5, walk_frames=4, attack_frames=5) + ] + self.grids = mcrfpy.listGrids() self.entity_direction = 0 - mcrfpy.registerPyAction("test_down", lambda: self.animate_entity(0, False)) - mcrfpy.registerPyAction("test_up", lambda: self.animate_entity(1, False)) - mcrfpy.registerPyAction("test_right", lambda: self.animate_entity(2, False)) - mcrfpy.registerPyAction("test_left", lambda: self.animate_entity(3, False)) - mcrfpy.registerPyAction("test_attack", lambda: self.animate_entity(None, True)) + mcrfpy.registerPyAction("test_down", lambda: [te.animate(0, False, True) for te in self.tes]) + mcrfpy.registerPyAction("test_up", lambda: [te.animate(1, False, True) for te in self.tes]) + mcrfpy.registerPyAction("test_right", lambda: [te.animate(2, False, True) for te in self.tes]) + mcrfpy.registerPyAction("test_left", lambda: [te.animate(3, False, True) for te in self.tes]) + mcrfpy.registerPyAction("test_attack", lambda: [te.animate(0, True) for te in self.tes]) + mcrfpy.registerPyAction("testent", lambda: [te.animate(2, True) for te in self.tes]) # Button behavior self.clicks = 0 @@ -115,6 +218,18 @@ class TestScene: orc_animation # values: iterable of frames for 'sprite', lerp target for others ) + #def move_entity(self, targetgrid, entity_index, position): + # for i, g in enumerate(self.grids): + # if g.title == targetgrid: + # g.entities[entity_index].x = position[0] + # g.entities[entity_index].y = position[1] + # #pts = g.points + # g.visible = True + # mcrfpy.modGrid(g) + # self.grids = mcrfpy.listGrids() + # #self.grids[i].points = pts + # return + def changesprite(self, n): self.sprite_index += n @@ -129,12 +244,14 @@ class TestScene: mcrfpy.modMenu(self.menus[0]) def gridgen(self): - print(f"[Python] modifying {len(self.grids[0].points)} grid points") + #print(f"[Python] modifying {len(self.grids[0].points)} grid points") for p in self.grids[0].points: p.color = (randint(0, 255), randint(64, 192), 0) - print("[Python] Modifying:") + #print("[Python] Modifying:") self.grids[0].visible = True mcrfpy.modGrid(self.grids[0]) + print(f"Sent grid: {repr(self.grids[0])}") + print(f"Received grid: {repr(mcrfpy.listGrids()[0])}") def animation_done(self, s): print(f"The `{s}` animation completed.")