From b4daac6e0c34ecdcb0d12e32bcc739a5b71dadd2 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Sat, 11 Mar 2023 16:11:10 -0500 Subject: [PATCH] Camera following functionality, first pass --- src/EntityManager.cpp | 12 ++++++-- src/McRFPy_API.cpp | 64 ++++++++++++++++++++++++++++++++++------ src/McRFPy_API.h | 5 ++++ src/PythonScene.cpp | 15 +++++++++- src/scripts/TestScene.py | 39 +++++++++++++++++------- 5 files changed, 113 insertions(+), 22 deletions(-) diff --git a/src/EntityManager.cpp b/src/EntityManager.cpp index 0a2eb01..e364cb6 100644 --- a/src/EntityManager.cpp +++ b/src/EntityManager.cpp @@ -22,14 +22,22 @@ void EntityManager::update() //if (m_entitiesToAdd.size()) // m_entitiesToAdd.erase(m_entitiesToAdd.begin(), m_entitiesToAdd.end()); m_entitiesToAdd = EntityVec(); + } void EntityManager::removeDeadEntities(EntityVec & vec) { EntityVec survivors; // New vector - for (auto& e : m_entities) - { + for (auto& e : m_entities){ if (e->isActive()) survivors.push_back(e); // populate new vector + else if (e->cGrid) { // erase vector from grid + for( auto it = e->cGrid->grid->entities.begin(); it != e->cGrid->grid->entities.end(); it++){ + if( *it == e ){ + e->cGrid->grid->entities.erase( it ); + break; + } + } + } } //std::cout << "All entities: " << m_entities.size() << " Survivors: " << survivors.size() << std::endl; m_entities = survivors; // point to new vector diff --git a/src/McRFPy_API.cpp b/src/McRFPy_API.cpp index 271df9a..1ef7ad9 100644 --- a/src/McRFPy_API.cpp +++ b/src/McRFPy_API.cpp @@ -14,6 +14,7 @@ sf::Sound McRFPy_API::sfx; std::string McRFPy_API::input_mode; int McRFPy_API::turn_number; std::string McRFPy_API::active_grid; +bool McRFPy_API::do_camfollow; EntityManager McRFPy_API::entities; @@ -93,6 +94,7 @@ static PyMethodDef mcrfpyMethods[] = { {"createEntity", McRFPy_API::_createEntity, METH_VARARGS, ""}, //{"listEntities", McRFPy_API::_listEntities, METH_VARARGS, ""}, {"refreshFov", McRFPy_API::_refreshFov, METH_VARARGS, ""}, + {"camFollow", McRFPy_API::_camFollow, METH_VARARGS, ""}, {NULL, NULL, 0, NULL} }; @@ -638,7 +640,7 @@ PyObject* McRFPy_API::_modGrid(PyObject* self, PyObject* args) { PyObject* o; PyObject* bool_is_entityonly = Py_False; if (!PyArg_ParseTuple(args, "O|O", &o, &bool_is_entityonly)) return NULL; - std::cout << "EntOnly Flag: " << PyUnicode_AsUTF8(PyObject_Repr(bool_is_entityonly)) << std::endl; + //std::cout << "EntOnly Flag: " << PyUnicode_AsUTF8(PyObject_Repr(bool_is_entityonly)) << std::endl; std::string title = PyUnicode_AsUTF8(PyObject_GetAttrString(o, "title")); int grid_x = PyLong_AsLong(PyObject_GetAttrString(o, "grid_x")); int grid_y = PyLong_AsLong(PyObject_GetAttrString(o, "grid_y")); @@ -847,7 +849,7 @@ PyObject* McRFPy_API::_createAnimation(PyObject *self, PyObject *args) { [=](int s){obj->sprites[target_id].sprite_index = s;}, loop) ); - std::cout << "Frame animation constructed, there are now " <cGrid->grid; - std::cout << "Grid pointed to: " << (long)player_entity->cGrid->grid << std::endl; - if (!McRFPy_API::input_mode.compare("computerturn")) { + //std::cout << "Grid pointed to: " << (long)player_entity->cGrid->grid << std::endl; + if (McRFPy_API::input_mode.compare("playerturn") != 0) { // no input accepted while computer moving - std::cout << "Can't move while computer is moving." << std::endl; + std::cout << "Can't move while it's not player's turn." << std::endl; return; } // TODO: selection cursor via keyboard @@ -1026,9 +1028,9 @@ void McRFPy_API::player_input(int dx, int dy) { ") + (" << dx << ", " << dy << ") is OOB." << std::endl; return; } - std::cout << PyUnicode_AsUTF8(PyObject_Repr(player_entity->cBehavior->object)) << std::endl; + //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; + //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); @@ -1037,3 +1039,47 @@ void McRFPy_API::player_input(int dx, int dy) { std::cout << "player_input called on entity with no `move` method" << std::endl; } } + +void McRFPy_API::computerTurn() { + McRFPy_API::input_mode = "computerturnrunning"; + for (auto e : McRFPy_API::grids[McRFPy_API::active_grid]->entities) { + if (e->cBehavior) { + PyObject_Call(PyObject_GetAttrString(e->cBehavior->object, "ai_act"), PyTuple_New(0), NULL); + } + } +} + +void McRFPy_API::playerTurn() { + McRFPy_API::input_mode = "playerturn"; + for (auto e : McRFPy_API::entities.getEntities("player")) { + if (e->cBehavior) { + PyObject_Call(PyObject_GetAttrString(e->cBehavior->object, "player_act"), PyTuple_New(0), NULL); + } + } +} + +void McRFPy_API::camFollow() { + if (!McRFPy_API::do_camfollow) return; + auto& ag = McRFPy_API::grids[McRFPy_API::active_grid]; + for (auto e : McRFPy_API::entities.getEntities("player")) { + //std::cout << "grid center: " << ag->center_x << ", " << ag->center_y << std::endl << + // "player grid pos: " << e->cGrid->x << ", " << e->cGrid->y << std::endl << + // "player sprite pos: " << e->cGrid->indexsprite.x << ", " << e->cGrid->indexsprite.y << std::endl; + ag->center_x = e->cGrid->indexsprite.x * ag->grid_size + ag->grid_size * 0.5; + ag->center_y = e->cGrid->indexsprite.y * ag->grid_size + ag->grid_size * 0.5; + } +} + +PyObject* McRFPy_API::_camFollow(PyObject* self, PyObject* args) { + PyObject* set_camfollow; + if (!PyArg_ParseTuple(args, "|O", &set_camfollow)) return NULL; + if (set_camfollow == NULL) { + // return value + Py_INCREF(McRFPy_API::do_camfollow ? Py_True : Py_False); + return McRFPy_API::do_camfollow ? Py_True : Py_False; + } + + McRFPy_API::do_camfollow = PyObject_IsTrue(set_camfollow); + Py_INCREF(Py_None); + return Py_None; +} diff --git a/src/McRFPy_API.h b/src/McRFPy_API.h index f08c4d2..240a80f 100644 --- a/src/McRFPy_API.h +++ b/src/McRFPy_API.h @@ -108,10 +108,15 @@ public: static int turn_number; static PyObject* _turnNumber(PyObject*, PyObject*); static PyObject* _refreshFov(PyObject*, PyObject*); + static bool do_camfollow; + static void camFollow(); + static PyObject* _camFollow(PyObject*, PyObject*); // accept keyboard input from scene static sf::Vector2i cursor_position; static void player_input(int, int); + static void computerTurn(); + static void playerTurn(); // Jank Functionality static UIMenu* createMenu(int posx, int posy, int sizex, int sizey); diff --git a/src/PythonScene.cpp b/src/PythonScene.cpp index f90abb3..e0e40cf 100644 --- a/src/PythonScene.cpp +++ b/src/PythonScene.cpp @@ -36,6 +36,7 @@ PythonScene::PythonScene(GameEngine* g, std::string pymodule) registerAction(0, "event"); dragging = false; + McRFPy_API::do_camfollow = false; drag_grid = NULL; // import pymodule and call start() @@ -53,7 +54,7 @@ void PythonScene::animate() { (*it)->step(frametime); //std::cout << "Step complete" << std::endl; if ((*it)->isDone()) { - std::cout << "Cleaning up Animation" << std::endl; + //std::cout << "Cleaning up Animation" << std::endl; auto prev = it; it++; McRFPy_API::animations.erase(prev); @@ -101,6 +102,15 @@ void PythonScene::animate() { } void PythonScene::update() { + + // turn cycle: If player's input made the state "computerturnwait", finish + // all animations and then let the NPCs act + if (McRFPy_API::animations.size() == 0 && McRFPy_API::input_mode.compare("computerturnwait") == 0) { + McRFPy_API::input_mode = "computerturn"; + } + else if (McRFPy_API::animations.size() == 0 && McRFPy_API::input_mode.compare("computerturnrunning") == 0) { + McRFPy_API::input_mode = "playerturnstart"; + } McRFPy_API::entities.update(); // check if left click is still down & mouse has moved @@ -116,6 +126,9 @@ void PythonScene::update() { } animate(); + McRFPy_API::camFollow(); + if (McRFPy_API::input_mode.compare(std::string("computerturn")) == 0) McRFPy_API::computerTurn(); + if (McRFPy_API::input_mode.compare(std::string("playerturnstart")) == 0) McRFPy_API::playerTurn(); } void PythonScene::doLClick(sf::Vector2i mousepos) { diff --git a/src/scripts/TestScene.py b/src/scripts/TestScene.py index 8a5a237..35425c8 100644 --- a/src/scripts/TestScene.py +++ b/src/scripts/TestScene.py @@ -24,6 +24,7 @@ class TestEntity: self.y = y self.facing_direction = 0 self.do_fov = do_fov + self.label = label #print(f"Calling C++ with: {repr((self.grid, label, tex_index, self.basesprite, x, y, self))}") grids = mcrfpy.listGrids() for g in grids: @@ -31,6 +32,16 @@ class TestEntity: self.entity_index = len(g.entities) mcrfpy.createEntity(self.grid, label, tex_index, self.basesprite, x, y, self) + def ai_act(self): + if self.label == "player": return + self.move(randint(-1, 1), randint(-1, 1)) + scene.actors += 1 + + def player_act(self): + #print("I'M INTERVENING") + mcrfpy.unlockPlayerInput() + scene.updatehints() + def move(self, dx, dy): # select animation direction # prefer left or right for diagonals. @@ -40,6 +51,9 @@ class TestEntity: if g.at(self.x + dx, self.y + dy) is None or not g.at(self.x + dx, self.y + dy).walkable: print("Blocked at target location.") return + if self.label == "player": + mcrfpy.lockPlayerInput() + scene.updatehints() if (dx == 0 and dy == 0): direction = self.facing_direction # TODO, jump straight to computer turn elif (dx): @@ -64,8 +78,8 @@ class TestEntity: False, #loop: repeat indefinitely animation_frames # values: iterable of frames for 'sprite', lerp target for others ) - global animations_in_progress - animations_in_progress += 1 + #global animations_in_progress + #animations_in_progress += 1 if move: pos = [self.x, self.y] if (direction == 0): pos[1] += 1 @@ -96,18 +110,22 @@ class TestEntity: False, #loop: repeat indefinitely animove # values: iterable of frames for 'sprite', lerp target for others ) - animations_in_progress += 1 + #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() + #global animations_in_progress + #animations_in_progress -= 1 + scene.actors -= 1 + #print(f"{self} done animating - {scene.actors} remaining") + if scene.actors == 0: + mcrfpy.unlockPlayerInput() + scene.updatehints() class TestScene: def __init__(self, ui_name = "demobox1", grid_name = "demogrid"): # Texture & Sound Loading + self.actors = 0 print("Load textures") mcrfpy.createTexture("./assets/test_portraits.png", 32, 8, 8) #0 - portraits mcrfpy.createTexture("./assets/alives_other.png", 16, 64, 64) #1 - TinyWorld NPCs @@ -287,11 +305,12 @@ class TestScene: p.walkable = False p.transparent = False - room_centers = [(randint(0, self.grids[0].grid_x-1), randint(0, self.grids[0].grid_y-1)) for i in range(6)] - room_centers.append((3, 5)) + room_centers = [(randint(0, self.grids[0].grid_x-1), randint(0, self.grids[0].grid_y-1)) for i in range(20)] + \ + [ (3, 5), (10, 10), (20, 20), (30, 30), (40, 40) ] + #room_centers.append((3, 5)) for r in room_centers: print(r) - room_color = (randint(128, 192), randint(128, 192), randint(128, 192)) + room_color = (randint(16, 24)*8, randint(16, 24)*8, randint(16, 24)*8) #self.grids[0].at(r[0], r[1]).walkable = True #self.grids[0].at(r[0], r[1]).color = room_color halfx, halfy = randint(2, 11), randint(2,11)