Squashed: grid-entity-integration partial features for 7DRL 2025 deployment
This squash commit includes changes from April 21st through 28th, 2024, and the past 3 days of work at 7DRL. Rather than resume my feature branch work, I made minor changes to safe the C++ functionality and wrote workarounds in Python. I'm very likely to delete this commit from history by rolling master back to the previous commit, and squash merging a finished feature branch.
This commit is contained in:
parent
232105a893
commit
e928dda4b3
Binary file not shown.
After Width: | Height: | Size: 662 KiB |
|
@ -9,10 +9,10 @@ GameEngine::GameEngine()
|
|||
{
|
||||
Resources::font.loadFromFile("./assets/JetbrainsMono.ttf");
|
||||
Resources::game = this;
|
||||
window_title = "McRogueFace - 7DRL 2024 Engine Demo";
|
||||
window_title = "Crypt of Sokoban - 7DRL 2025, McRogueface Engine";
|
||||
window.create(sf::VideoMode(1024, 768), window_title, sf::Style::Titlebar | sf::Style::Close);
|
||||
visible = window.getDefaultView();
|
||||
window.setFramerateLimit(30);
|
||||
window.setFramerateLimit(60);
|
||||
scene = "uitest";
|
||||
scenes["uitest"] = new UITestScene(this);
|
||||
|
||||
|
@ -63,7 +63,10 @@ void GameEngine::run()
|
|||
currentFrame++;
|
||||
frameTime = clock.restart().asSeconds();
|
||||
fps = 1 / frameTime;
|
||||
window.setTitle(window_title + " " + std::to_string(fps) + " FPS");
|
||||
int whole_fps = (int)fps;
|
||||
int tenth_fps = int(fps * 100) % 10;
|
||||
//window.setTitle(window_title + " " + std::to_string(fps) + " FPS");
|
||||
window.setTitle(window_title + " " + std::to_string(whole_fps) + "." + std::to_string(tenth_fps) + " FPS");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -109,3 +109,16 @@ int PyVector::set_member(PyObject* obj, PyObject* value, void* closure)
|
|||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyVectorObject* PyVector::from_arg(PyObject* args)
|
||||
{
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
|
||||
if (PyObject_IsInstance(args, (PyObject*)type)) return (PyVectorObject*)args;
|
||||
auto obj = (PyVectorObject*)type->tp_alloc(type, 0);
|
||||
int err = init(obj, args, NULL);
|
||||
if (err) {
|
||||
Py_DECREF(obj);
|
||||
return NULL;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Python.h"
|
||||
#include "McRFPy_API.h"
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
@ -22,6 +23,7 @@ public:
|
|||
static PyObject* pynew(PyTypeObject* type, PyObject* args=NULL, PyObject* kwds=NULL);
|
||||
static PyObject* get_member(PyObject*, void*);
|
||||
static int set_member(PyObject*, PyObject*, void*);
|
||||
static PyVectorObject* from_arg(PyObject*);
|
||||
|
||||
static PyGetSetDef getsetters[];
|
||||
};
|
||||
|
|
|
@ -13,10 +13,11 @@ UIDrawable* UICaption::click_at(sf::Vector2f point)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void UICaption::render(sf::Vector2f offset)
|
||||
void UICaption::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||
{
|
||||
text.move(offset);
|
||||
Resources::game->getWindow().draw(text);
|
||||
//Resources::game->getWindow().draw(text);
|
||||
target.draw(text);
|
||||
text.move(-offset);
|
||||
}
|
||||
|
||||
|
@ -222,17 +223,30 @@ PyObject* UICaption::repr(PyUICaptionObject* self)
|
|||
int UICaption::init(PyUICaptionObject* self, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
using namespace mcrfpydef;
|
||||
static const char* keywords[] = { "x", "y", "text", "font", "fill_color", "outline_color", "outline", nullptr };
|
||||
float x = 0.0f, y = 0.0f, outline = 0.0f;
|
||||
// Constructor switch to Vector position
|
||||
//static const char* keywords[] = { "x", "y", "text", "font", "fill_color", "outline_color", "outline", nullptr };
|
||||
//float x = 0.0f, y = 0.0f, outline = 0.0f;
|
||||
static const char* keywords[] = { "pos", "text", "font", "fill_color", "outline_color", "outline", nullptr };
|
||||
PyObject* pos;
|
||||
float outline = 0.0f;
|
||||
char* text;
|
||||
PyObject* font=NULL, *fill_color=NULL, *outline_color=NULL;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffzOOOf",
|
||||
const_cast<char**>(keywords), &x, &y, &text, &font, &fill_color, &outline_color, &outline))
|
||||
//if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffzOOOf",
|
||||
// const_cast<char**>(keywords), &x, &y, &text, &font, &fill_color, &outline_color, &outline))
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|zOOOf",
|
||||
const_cast<char**>(keywords), &pos, &text, &font, &fill_color, &outline_color, &outline))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyVectorObject* pos_result = PyVector::from_arg(pos);
|
||||
if (!pos_result)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "pos must be a mcrfpy.Vector instance or arguments to mcrfpy.Vector.__init__");
|
||||
return -1;
|
||||
}
|
||||
self->data->text.setPosition(pos_result->data);
|
||||
// check types for font, fill_color, outline_color
|
||||
|
||||
std::cout << PyUnicode_AsUTF8(PyObject_Repr(font)) << std::endl;
|
||||
|
@ -251,7 +265,6 @@ int UICaption::init(PyUICaptionObject* self, PyObject* args, PyObject* kwds)
|
|||
//self->data->text.setFont(Resources::game->getFont());
|
||||
}
|
||||
|
||||
self->data->text.setPosition(sf::Vector2f(x, y));
|
||||
self->data->text.setString((std::string)text);
|
||||
self->data->text.setOutlineThickness(outline);
|
||||
if (fill_color) {
|
||||
|
|
|
@ -7,7 +7,7 @@ class UICaption: public UIDrawable
|
|||
{
|
||||
public:
|
||||
sf::Text text;
|
||||
void render(sf::Vector2f) override final;
|
||||
void render(sf::Vector2f, sf::RenderTarget&) override final;
|
||||
PyObjectsEnum derived_type() override final;
|
||||
virtual UIDrawable* click_at(sf::Vector2f point) override final;
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "UICaption.h"
|
||||
#include "UISprite.h"
|
||||
#include "UIGrid.h"
|
||||
#include "GameEngine.h"
|
||||
|
||||
UIDrawable::UIDrawable() { click_callable = NULL; }
|
||||
|
||||
|
@ -13,7 +14,7 @@ void UIDrawable::click_unregister()
|
|||
|
||||
void UIDrawable::render()
|
||||
{
|
||||
render(sf::Vector2f());
|
||||
render(sf::Vector2f(), Resources::game->getWindow());
|
||||
}
|
||||
|
||||
PyObject* UIDrawable::get_click(PyObject* self, void* closure) {
|
||||
|
|
|
@ -28,7 +28,8 @@ class UIDrawable
|
|||
{
|
||||
public:
|
||||
void render();
|
||||
virtual void render(sf::Vector2f) = 0;
|
||||
//virtual void render(sf::Vector2f) = 0;
|
||||
virtual void render(sf::Vector2f, sf::RenderTarget&) = 0;
|
||||
virtual PyObjectsEnum derived_type() = 0;
|
||||
|
||||
// Mouse input handling - callable object, methods to find event's destination
|
||||
|
|
|
@ -34,18 +34,30 @@ PyObject* UIEntity::at(PyUIEntityObject* self, PyObject* o) {
|
|||
}
|
||||
|
||||
int UIEntity::init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) {
|
||||
static const char* keywords[] = { "x", "y", "texture", "sprite_index", "grid", nullptr };
|
||||
float x = 0.0f, y = 0.0f, scale = 1.0f;
|
||||
//static const char* keywords[] = { "x", "y", "texture", "sprite_index", "grid", nullptr };
|
||||
//float x = 0.0f, y = 0.0f, scale = 1.0f;
|
||||
static const char* keywords[] = { "pos", "texture", "sprite_index", "grid", nullptr };
|
||||
PyObject* pos;
|
||||
float scale = 1.0f;
|
||||
int sprite_index = -1;
|
||||
PyObject* texture = NULL;
|
||||
PyObject* grid = NULL;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffOi|O",
|
||||
const_cast<char**>(keywords), &x, &y, &texture, &sprite_index, &grid))
|
||||
//if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffOi|O",
|
||||
// const_cast<char**>(keywords), &x, &y, &texture, &sprite_index, &grid))
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOi|O",
|
||||
const_cast<char**>(keywords), &pos, &texture, &sprite_index, &grid))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyVectorObject* pos_result = PyVector::from_arg(pos);
|
||||
if (!pos_result)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "pos must be a mcrfpy.Vector instance or arguments to mcrfpy.Vector.__init__");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// check types for texture
|
||||
//
|
||||
// Set Texture
|
||||
|
@ -75,7 +87,7 @@ int UIEntity::init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) {
|
|||
|
||||
// TODO - PyTextureObjects and IndexTextures are a little bit of a mess with shared/unshared pointers
|
||||
self->data->sprite = UISprite(pytexture->data, sprite_index, sf::Vector2f(0,0), 1.0);
|
||||
self->data->position = sf::Vector2f(x, y);
|
||||
self->data->position = pos_result->data;
|
||||
if (grid != NULL) {
|
||||
PyUIGridObject* pygrid = (PyUIGridObject*)grid;
|
||||
self->data->grid = pygrid->data;
|
||||
|
@ -95,6 +107,10 @@ PyObject* sfVector2f_to_PyObject(sf::Vector2f vector) {
|
|||
return Py_BuildValue("(ff)", vector.x, vector.y);
|
||||
}
|
||||
|
||||
PyObject* sfVector2i_to_PyObject(sf::Vector2i vector) {
|
||||
return Py_BuildValue("(ii)", vector.x, vector.y);
|
||||
}
|
||||
|
||||
sf::Vector2f PyObject_to_sfVector2f(PyObject* obj) {
|
||||
float x, y;
|
||||
if (!PyArg_ParseTuple(obj, "ff", &x, &y)) {
|
||||
|
@ -103,6 +119,14 @@ sf::Vector2f PyObject_to_sfVector2f(PyObject* obj) {
|
|||
return sf::Vector2f(x, y);
|
||||
}
|
||||
|
||||
sf::Vector2i PyObject_to_sfVector2i(PyObject* obj) {
|
||||
int x, y;
|
||||
if (!PyArg_ParseTuple(obj, "ii", &x, &y)) {
|
||||
return sf::Vector2i(); // TODO / reconsider this default: Return default vector on parse error
|
||||
}
|
||||
return sf::Vector2i(x, y);
|
||||
}
|
||||
|
||||
// TODO - deprecate / remove this helper
|
||||
PyObject* UIGridPointState_to_PyObject(const UIGridPointState& state) {
|
||||
return PyObject_New(PyObject, (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPointState"));
|
||||
|
@ -125,11 +149,19 @@ PyObject* UIGridPointStateVector_to_PyList(const std::vector<UIGridPointState>&
|
|||
}
|
||||
|
||||
PyObject* UIEntity::get_position(PyUIEntityObject* self, void* closure) {
|
||||
return sfVector2f_to_PyObject(self->data->position);
|
||||
if (reinterpret_cast<long>(closure) == 0) {
|
||||
return sfVector2f_to_PyObject(self->data->position);
|
||||
} else {
|
||||
return sfVector2i_to_PyObject(self->data->collision_pos);
|
||||
}
|
||||
}
|
||||
|
||||
int UIEntity::set_position(PyUIEntityObject* self, PyObject* value, void* closure) {
|
||||
self->data->position = PyObject_to_sfVector2f(value);
|
||||
if (reinterpret_cast<long>(closure) == 0) {
|
||||
self->data->position = PyObject_to_sfVector2f(value);
|
||||
} else {
|
||||
self->data->collision_pos = PyObject_to_sfVector2i(value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -158,7 +190,8 @@ PyMethodDef UIEntity::methods[] = {
|
|||
};
|
||||
|
||||
PyGetSetDef UIEntity::getsetters[] = {
|
||||
{"position", (getter)UIEntity::get_position, (setter)UIEntity::set_position, "Entity position", NULL},
|
||||
{"draw_pos", (getter)UIEntity::get_position, (setter)UIEntity::set_position, "Entity position (graphically)", (void*)0},
|
||||
{"pos", (getter)UIEntity::get_position, (setter)UIEntity::set_position, "Entity position (integer grid coordinates)", (void*)1},
|
||||
{"gridstate", (getter)UIEntity::get_gridstate, NULL, "Grid point states for the entity", NULL},
|
||||
{"sprite_number", (getter)UIEntity::get_spritenumber, (setter)UIEntity::set_spritenumber, "Sprite number (index) on the texture on the display", NULL},
|
||||
{NULL} /* Sentinel */
|
||||
|
|
|
@ -40,7 +40,8 @@ public:
|
|||
std::vector<UIGridPointState> gridstate;
|
||||
UISprite sprite;
|
||||
sf::Vector2f position; //(x,y) in grid coordinates; float for animation
|
||||
void render(sf::Vector2f); //override final;
|
||||
sf::Vector2i collision_pos; //(x, y) in grid coordinates: int for collision
|
||||
//void render(sf::Vector2f); //override final;
|
||||
|
||||
UIEntity();
|
||||
UIEntity(UIGrid&);
|
||||
|
@ -65,7 +66,7 @@ namespace mcrfpydef {
|
|||
.tp_basicsize = sizeof(PyUIEntityObject),
|
||||
.tp_itemsize = 0,
|
||||
.tp_repr = (reprfunc)UIEntity::repr,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
||||
.tp_doc = "UIEntity objects",
|
||||
.tp_methods = UIEntity::methods,
|
||||
.tp_getset = UIEntity::getsetters,
|
||||
|
|
|
@ -44,14 +44,15 @@ PyObjectsEnum UIFrame::derived_type()
|
|||
return PyObjectsEnum::UIFRAME;
|
||||
}
|
||||
|
||||
void UIFrame::render(sf::Vector2f offset)
|
||||
void UIFrame::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||
{
|
||||
box.move(offset);
|
||||
Resources::game->getWindow().draw(box);
|
||||
//Resources::game->getWindow().draw(box);
|
||||
target.draw(box);
|
||||
box.move(-offset);
|
||||
|
||||
for (auto drawable : *children) {
|
||||
drawable->render(offset + box.getPosition());
|
||||
drawable->render(offset + box.getPosition(), target);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
sf::RectangleShape box;
|
||||
float outline;
|
||||
std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>> children;
|
||||
void render(sf::Vector2f) override final;
|
||||
void render(sf::Vector2f, sf::RenderTarget&) override final;
|
||||
void move(sf::Vector2f);
|
||||
PyObjectsEnum derived_type() override final;
|
||||
virtual UIDrawable* click_at(sf::Vector2f point) override final;
|
||||
|
|
|
@ -32,9 +32,9 @@ UIGrid::UIGrid(int gx, int gy, std::shared_ptr<PyTexture> _ptex, sf::Vector2f _x
|
|||
void UIGrid::update() {}
|
||||
|
||||
|
||||
void UIGrid::render(sf::Vector2f)
|
||||
void UIGrid::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||
{
|
||||
output.setPosition(box.getPosition()); // output sprite can move; update position when drawing
|
||||
output.setPosition(box.getPosition() + offset); // output sprite can move; update position when drawing
|
||||
// output size can change; update size when drawing
|
||||
output.setTextureRect(
|
||||
sf::IntRect(0, 0,
|
||||
|
@ -172,7 +172,8 @@ void UIGrid::render(sf::Vector2f)
|
|||
|
||||
// render to window
|
||||
renderTexture.display();
|
||||
Resources::game->getWindow().draw(output);
|
||||
//Resources::game->getWindow().draw(output);
|
||||
target.draw(output);
|
||||
|
||||
}
|
||||
|
||||
|
@ -204,12 +205,28 @@ UIDrawable* UIGrid::click_at(sf::Vector2f point)
|
|||
int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
||||
int grid_x, grid_y;
|
||||
PyObject* textureObj;
|
||||
float box_x, box_y, box_w, box_h;
|
||||
//float box_x, box_y, box_w, box_h;
|
||||
PyObject* pos, *size;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "iiOffff", &grid_x, &grid_y, &textureObj, &box_x, &box_y, &box_w, &box_h)) {
|
||||
//if (!PyArg_ParseTuple(args, "iiOffff", &grid_x, &grid_y, &textureObj, &box_x, &box_y, &box_w, &box_h)) {
|
||||
if (!PyArg_ParseTuple(args, "iiOOO", &grid_x, &grid_y, &textureObj, &pos, &size)) {
|
||||
return -1; // If parsing fails, return an error
|
||||
}
|
||||
|
||||
PyVectorObject* pos_result = PyVector::from_arg(pos);
|
||||
if (!pos_result)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "pos must be a mcrfpy.Vector instance or arguments to mcrfpy.Vector.__init__");
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyVectorObject* size_result = PyVector::from_arg(size);
|
||||
if (!size_result)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "pos must be a mcrfpy.Vector instance or arguments to mcrfpy.Vector.__init__");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Convert PyObject texture to IndexTexture*
|
||||
// This requires the texture object to have been initialized similar to UISprite's texture handling
|
||||
|
||||
|
@ -224,8 +241,9 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
|||
|
||||
// Initialize UIGrid
|
||||
//self->data = new UIGrid(grid_x, grid_y, texture, sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h));
|
||||
self->data = std::make_shared<UIGrid>(grid_x, grid_y, pyTexture->data,
|
||||
sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h));
|
||||
//self->data = std::make_shared<UIGrid>(grid_x, grid_y, pyTexture->data,
|
||||
// sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h));
|
||||
self->data = std::make_shared<UIGrid>(grid_x, grid_y, pyTexture->data, pos_result->data, size_result->data);
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
|
@ -423,22 +441,6 @@ PyObject* UIGrid::get_children(PyUIGridObject* self, void* closure)
|
|||
|
||||
PyObject* UIGrid::repr(PyUIGridObject* self)
|
||||
{
|
||||
|
||||
// if (member_ptr == 0) // x
|
||||
// self->data->box.setPosition(val, self->data->box.getPosition().y);
|
||||
// else if (member_ptr == 1) // y
|
||||
// self->data->box.setPosition(self->data->box.getPosition().x, val);
|
||||
// else if (member_ptr == 2) // w
|
||||
// self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y));
|
||||
// else if (member_ptr == 3) // h
|
||||
// self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val));
|
||||
// else if (member_ptr == 4) // center_x
|
||||
// self->data->center_x = val;
|
||||
// else if (member_ptr == 5) // center_y
|
||||
// self->data->center_y = val;
|
||||
// else if (member_ptr == 6) // zoom
|
||||
// self->data->zoom = val;
|
||||
|
||||
std::ostringstream ss;
|
||||
if (!self->data) ss << "<Grid (invalid internal object)>";
|
||||
else {
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
//UIGrid(int, int, IndexTexture*, float, float, float, float);
|
||||
UIGrid(int, int, std::shared_ptr<PyTexture>, sf::Vector2f, sf::Vector2f);
|
||||
void update();
|
||||
void render(sf::Vector2f) override final;
|
||||
void render(sf::Vector2f, sf::RenderTarget&) override final;
|
||||
UIGridPoint& at(int, int);
|
||||
PyObjectsEnum derived_type() override final;
|
||||
//void setSprite(int);
|
||||
|
|
|
@ -18,14 +18,16 @@ UISprite::UISprite(std::shared_ptr<PyTexture> _ptex, int _sprite_index, sf::Vect
|
|||
sprite = ptex->sprite(sprite_index, _pos, sf::Vector2f(_scale, _scale));
|
||||
}
|
||||
|
||||
/*
|
||||
void UISprite::render(sf::Vector2f offset)
|
||||
{
|
||||
sprite.move(offset);
|
||||
Resources::game->getWindow().draw(sprite);
|
||||
sprite.move(-offset);
|
||||
}
|
||||
*/
|
||||
|
||||
void UISprite::render(sf::Vector2f offset, sf::RenderTexture& target)
|
||||
void UISprite::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||
{
|
||||
sprite.move(offset);
|
||||
target.draw(sprite);
|
||||
|
|
|
@ -25,10 +25,10 @@ public:
|
|||
UISprite();
|
||||
UISprite(std::shared_ptr<PyTexture>, int, sf::Vector2f, float);
|
||||
void update();
|
||||
void render(sf::Vector2f) override final;
|
||||
void render(sf::Vector2f, sf::RenderTarget&) override final;
|
||||
virtual UIDrawable* click_at(sf::Vector2f point) override final;
|
||||
|
||||
void render(sf::Vector2f, sf::RenderTexture&);
|
||||
//void render(sf::Vector2f, sf::RenderTexture&);
|
||||
|
||||
void setPosition(sf::Vector2f);
|
||||
sf::Vector2f getPosition();
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
import mcrfpy
|
||||
#t = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
t = mcrfpy.Texture("assets/kenney_TD_MR_IP.png", 16, 16)
|
||||
#def iterable_entities(grid):
|
||||
# """Workaround for UIEntityCollection bug; see issue #72"""
|
||||
# entities = []
|
||||
# for i in range(len(grid.entities)):
|
||||
# entities.append(grid.entities[i])
|
||||
# return entities
|
||||
|
||||
class COSEntity(): #mcrfpy.Entity): # Fake mcrfpy.Entity integration; engine bugs workarounds
|
||||
def __init__(self, g:mcrfpy.Grid, x=0, y=0, sprite_num=86, *, game):
|
||||
#self.e = mcrfpy.Entity((x, y), t, sprite_num)
|
||||
#super().__init__((x, y), t, sprite_num)
|
||||
self._entity = mcrfpy.Entity((x, y), t, sprite_num)
|
||||
#grid.entities.append(self.e)
|
||||
self.grid = g
|
||||
#g.entities.append(self._entity)
|
||||
self.game = game
|
||||
self.game.add_entity(self)
|
||||
|
||||
## Wrapping mcfrpy.Entity properties to emulate derived class... see issue #76
|
||||
@property
|
||||
def draw_pos(self):
|
||||
return self._entity.draw_pos
|
||||
|
||||
@draw_pos.setter
|
||||
def draw_pos(self, value):
|
||||
self._entity.draw_pos = value
|
||||
|
||||
@property
|
||||
def sprite_number(self):
|
||||
return self._entity.sprite_number
|
||||
|
||||
@sprite_number.setter
|
||||
def sprite_number(self, value):
|
||||
self._entity.sprite_number = value
|
||||
|
||||
def __repr__(self):
|
||||
return f"<COSEntity ({self.draw_pos}) on {self.grid}>"
|
||||
|
||||
def die(self):
|
||||
# ugly workaround! grid.entities isn't really iterable (segfaults)
|
||||
for i in range(len(self.grid.entities)):
|
||||
e = self.grid.entities[i]
|
||||
if e == self._entity:
|
||||
#if e == self:
|
||||
self.grid.entities.remove(i)
|
||||
break
|
||||
else:
|
||||
print(f"!!! {self!r} wasn't removed from grid on call to die()")
|
||||
|
||||
def bump(self, other, dx, dy, test=False):
|
||||
raise NotImplementedError
|
||||
|
||||
def do_move(self, tx, ty):
|
||||
"""Base class method to move this entity
|
||||
Assumes try_move succeeded, for everyone!
|
||||
from: self._entity.draw_pos
|
||||
to: (tx, ty)
|
||||
calls ev_exit for every entity at (draw_pos)
|
||||
calls ev_enter for every entity at (tx, ty)
|
||||
"""
|
||||
old_pos = self.draw_pos
|
||||
self.draw_pos = (tx, ty)
|
||||
for e in self.game.entities:
|
||||
if e is self: continue
|
||||
if e.draw_pos == old_pos: e.ev_exit(self)
|
||||
for e in self.game.entities:
|
||||
if e is self: continue
|
||||
if e.draw_pos == (tx, ty): e.ev_enter(self)
|
||||
|
||||
|
||||
def ev_enter(self, other):
|
||||
pass
|
||||
|
||||
def ev_exit(self, other):
|
||||
pass
|
||||
|
||||
def try_move(self, dx, dy, test=False):
|
||||
x_max, y_max = self.grid.grid_size
|
||||
tx, ty = int(self.draw_pos[0] + dx), int(self.draw_pos[1] + dy)
|
||||
#for e in iterable_entities(self.grid):
|
||||
|
||||
# sorting entities to test against the boulder instead of the button when they overlap.
|
||||
for e in sorted(self.game.entities, key = lambda i: i.draw_order, reverse = True):
|
||||
if e.draw_pos == (tx, ty):
|
||||
#print(f"bumping {e}")
|
||||
return e.bump(self, dx, dy)
|
||||
|
||||
if tx < 0 or tx >= x_max:
|
||||
return False
|
||||
if ty < 0 or ty >= y_max:
|
||||
return False
|
||||
if self.grid.at((tx, ty)).walkable == True:
|
||||
if not test:
|
||||
#self.draw_pos = (tx, ty)
|
||||
self.do_move(tx, ty)
|
||||
return True
|
||||
else:
|
||||
#print("Bonk")
|
||||
return False
|
||||
|
||||
def _relative_move(self, dx, dy):
|
||||
tx, ty = int(self.draw_pos[0] + dx), int(self.draw_pos[1] + dy)
|
||||
#self.draw_pos = (tx, ty)
|
||||
self.do_move(tx, ty)
|
||||
|
||||
class PlayerEntity(COSEntity):
|
||||
def __init__(self, *, game):
|
||||
#print(f"spawn at origin")
|
||||
self.draw_order = 10
|
||||
super().__init__(game.grid, 0, 0, sprite_num=84, game=game)
|
||||
|
||||
def respawn(self, avoid=None):
|
||||
# find spawn point
|
||||
x_max, y_max = g.size
|
||||
spawn_points = []
|
||||
for x in range(x_max):
|
||||
for y in range(y_max):
|
||||
if g.at((x, y)).walkable:
|
||||
spawn_points.append((x, y))
|
||||
random.shuffle(spawn_points)
|
||||
## TODO - find other entities to avoid spawning on top of
|
||||
for spawn in spawn_points:
|
||||
for e in avoid or []:
|
||||
if e.draw_pos == spawn: break
|
||||
else:
|
||||
break
|
||||
self.draw_pos = spawn
|
||||
|
||||
def __repr__(self):
|
||||
return f"<PlayerEntity {self.draw_pos}, {self.grid}>"
|
||||
|
||||
|
||||
class BoulderEntity(COSEntity):
|
||||
def __init__(self, x, y, *, game):
|
||||
self.draw_order = 8
|
||||
super().__init__(game.grid, x, y, 66, game=game)
|
||||
|
||||
def bump(self, other, dx, dy, test=False):
|
||||
if type(other) == BoulderEntity:
|
||||
#print("Boulders can't push boulders")
|
||||
return False
|
||||
#tx, ty = int(self.e.position[0] + dx), int(self.e.position[1] + dy)
|
||||
tx, ty = int(self.draw_pos[0] + dx), int(self.draw_pos[1] + dy)
|
||||
# Is the boulder blocked the same direction as the bumper? If not, let's both move
|
||||
old_pos = int(self.draw_pos[0]), int(self.draw_pos[1])
|
||||
if self.try_move(dx, dy, test=test):
|
||||
if not test:
|
||||
other.do_move(*old_pos)
|
||||
#other.draw_pos = old_pos
|
||||
return True
|
||||
|
||||
class ButtonEntity(COSEntity):
|
||||
def __init__(self, x, y, exit_entity, *, game):
|
||||
self.draw_order = 1
|
||||
super().__init__(game.grid, x, y, 42, game=game)
|
||||
self.exit = exit_entity
|
||||
|
||||
def ev_enter(self, other):
|
||||
print("Button makes a satisfying click!")
|
||||
self.exit.unlock()
|
||||
|
||||
def ev_exit(self, other):
|
||||
print("Button makes a disappointing click.")
|
||||
self.exit.lock()
|
||||
|
||||
def bump(self, other, dx, dy, test=False):
|
||||
#if type(other) == BoulderEntity:
|
||||
# self.exit.unlock()
|
||||
# TODO: unlock, and then lock again, when player steps on/off
|
||||
if not test:
|
||||
other._relative_move(dx, dy)
|
||||
return True
|
||||
|
||||
class ExitEntity(COSEntity):
|
||||
def __init__(self, x, y, bx, by, *, game):
|
||||
self.draw_order = 2
|
||||
super().__init__(game.grid, x, y, 45, game=game)
|
||||
self.my_button = ButtonEntity(bx, by, self, game=game)
|
||||
self.unlocked = False
|
||||
#global cos_entities
|
||||
#cos_entities.append(self.my_button)
|
||||
|
||||
def unlock(self):
|
||||
self.sprite_number = 21
|
||||
self.unlocked = True
|
||||
|
||||
def lock(self):
|
||||
self.sprite_number = 45
|
||||
self.unlocked = False
|
||||
|
||||
def bump(self, other, dx, dy, test=False):
|
||||
if type(other) == BoulderEntity:
|
||||
return False
|
||||
if self.unlocked:
|
||||
if not test:
|
||||
other._relative_move(dx, dy)
|
||||
#TODO - player go down a level logic
|
||||
if type(other) == PlayerEntity:
|
||||
self.game.create_level(self.game.depth + 1)
|
||||
self.game.swap_level(self.game.level, self.game.spawn_point)
|
|
@ -0,0 +1,195 @@
|
|||
import random
|
||||
import mcrfpy
|
||||
import cos_tiles as ct
|
||||
|
||||
t = mcrfpy.Texture("assets/kenney_TD_MR_IP.png", 16, 16)
|
||||
|
||||
def binary_space_partition(x, y, w, h):
|
||||
d = random.choices(["vert", "horiz"], weights=[w/(w+h), h/(w+h)])[0]
|
||||
split = random.randint(30, 70) / 100.0
|
||||
if d == "vert":
|
||||
coord = int(w * split)
|
||||
return (x, y, coord, h), (x+coord, y, w-coord, h)
|
||||
else: # horizontal
|
||||
coord = int(h * split)
|
||||
return (x, y, w, coord), (x, y+coord, w, h-coord)
|
||||
|
||||
room_area = lambda x, y, w, h: w * h
|
||||
|
||||
class BinaryRoomNode:
|
||||
def __init__(self, xywh):
|
||||
self.data = xywh
|
||||
self.left = None
|
||||
self.right = None
|
||||
|
||||
def __repr__(self):
|
||||
return f"<RoomNode {self.data}>"
|
||||
|
||||
def split(self):
|
||||
new_data = binary_space_partition(*self.data)
|
||||
self.left = BinaryRoomNode(new_data[0])
|
||||
self.right = BinaryRoomNode(new_data[1])
|
||||
|
||||
def walk(self):
|
||||
if self.left and self.right:
|
||||
return self.left.walk() + self.right.walk()
|
||||
return [self]
|
||||
|
||||
class RoomGraph:
|
||||
def __init__(self, xywh):
|
||||
self.root = BinaryRoomNode(xywh)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<RoomGraph, root={self.root}, {len(self.walk())} rooms>"
|
||||
|
||||
def walk(self):
|
||||
w = self.root.walk() if self.root else []
|
||||
#print(w)
|
||||
return w
|
||||
|
||||
def room_coord(room, margin=0):
|
||||
x, y, w, h = room.data
|
||||
w -= 1
|
||||
h -= 1
|
||||
margin += 1
|
||||
x += margin
|
||||
y += margin
|
||||
w -= margin
|
||||
h -= margin
|
||||
if w < 0: w = 0
|
||||
if h < 0: h = 0
|
||||
tx = x if w==0 else random.randint(x, x+w)
|
||||
ty = y if h==0 else random.randint(y, y+h)
|
||||
return (tx, ty)
|
||||
|
||||
class Level:
|
||||
def __init__(self, width, height):
|
||||
self.width = width
|
||||
self.height = height
|
||||
#self.graph = [(0, 0, width, height)]
|
||||
self.graph = RoomGraph( (0, 0, width, height) )
|
||||
self.grid = mcrfpy.Grid(width, height, t, (10, 10), (1014, 758))
|
||||
self.highlighted = -1 #debug view feature
|
||||
|
||||
def fill(self, xywh, highlight = False):
|
||||
if highlight:
|
||||
ts = 0
|
||||
else:
|
||||
ts = room_area(*xywh) % 131
|
||||
X, Y, W, H = xywh
|
||||
for x in range(X, X+W):
|
||||
for y in range(Y, Y+H):
|
||||
self.grid.at((x, y)).tilesprite = ts
|
||||
|
||||
def highlight(self, delta):
|
||||
rooms = self.graph.walk()
|
||||
if self.highlighted < len(rooms):
|
||||
print(f"reset {self.highlighted}")
|
||||
self.fill(rooms[self.highlighted].data) # reset
|
||||
self.highlighted += delta
|
||||
print(f"highlight {self.highlighted}")
|
||||
self.highlighted = self.highlighted % len(rooms)
|
||||
self.fill(rooms[self.highlighted].data, highlight = True)
|
||||
|
||||
def reset(self):
|
||||
self.graph = RoomGraph( (0, 0, self.width, self.height) )
|
||||
for x in range(self.width):
|
||||
for y in range(self.height):
|
||||
self.grid.at((x, y)).walkable = True
|
||||
self.grid.at((x, y)).transparent = True
|
||||
self.grid.at((x, y)).tilesprite = 0 #random.choice([40, 28])
|
||||
|
||||
def split(self, single=False):
|
||||
if single:
|
||||
areas = {g.data: room_area(*g.data) for g in self.graph.walk()}
|
||||
largest = sorted(self.graph.walk(), key=lambda g: areas[g.data])[-1]
|
||||
largest.split()
|
||||
else:
|
||||
for room in self.graph.walk(): room.split()
|
||||
self.fill_rooms()
|
||||
|
||||
def fill_rooms(self, features=None):
|
||||
rooms = self.graph.walk()
|
||||
print(f"rooms: {len(rooms)}")
|
||||
for i, g in enumerate(rooms):
|
||||
X, Y, W, H = g.data
|
||||
#c = [random.randint(0, 255) for _ in range(3)]
|
||||
ts = room_area(*g.data) % 131 + i # modulo - consistent tile pick
|
||||
for x in range(X, X+W):
|
||||
for y in range(Y, Y+H):
|
||||
self.grid.at((x, y)).tilesprite = ts
|
||||
|
||||
def wall_rooms(self):
|
||||
rooms = self.graph.walk()
|
||||
for g in rooms:
|
||||
#if random.random() > 0.66: continue
|
||||
X, Y, W, H = g.data
|
||||
for x in range(X, X+W):
|
||||
self.grid.at((x, Y)).walkable = False
|
||||
#self.grid.at((x, Y+H-1)).walkable = False
|
||||
for y in range(Y, Y+H):
|
||||
self.grid.at((X, y)).walkable = False
|
||||
#self.grid.at((X+W-1, y)).walkable = False
|
||||
# boundary of entire level
|
||||
for x in range(0, self.width):
|
||||
# self.grid.at((x, 0)).walkable = False
|
||||
self.grid.at((x, self.height-1)).walkable = False
|
||||
for y in range(0, self.height):
|
||||
# self.grid.at((0, y)).walkable = False
|
||||
self.grid.at((self.width-1, y)).walkable = False
|
||||
|
||||
def generate(self, target_rooms = 5, features=None):
|
||||
if features is None:
|
||||
shuffled = ["boulder", "button"]
|
||||
random.shuffle(shuffled)
|
||||
features = ["spawn"] + shuffled + ["exit", "treasure"]
|
||||
# Binary space partition to reach given # of rooms
|
||||
self.reset()
|
||||
while len(self.graph.walk()) < target_rooms:
|
||||
self.split(single=len(self.graph.walk()) > target_rooms * .5)
|
||||
|
||||
# Player path planning
|
||||
#self.fill_rooms()
|
||||
self.wall_rooms()
|
||||
rooms = self.graph.walk()
|
||||
feature_coords = {}
|
||||
prev_room = None
|
||||
for room in rooms:
|
||||
if not features: break
|
||||
f = features.pop(0)
|
||||
feature_coords[f] = room_coord(room, margin=4 if f in ("boulder",) else 1)
|
||||
|
||||
## Hallway generation
|
||||
# plow an inelegant path
|
||||
if prev_room:
|
||||
start = room_coord(prev_room, margin=2)
|
||||
end = room_coord(room, margin=2)
|
||||
# get x1,y1 and x2,y2 coordinates: top left and bottom right points on the rect formed by two random points, one from each of the 2 rooms
|
||||
x1 = min([start[0], end[0]])
|
||||
x2 = max([start[0], end[0]])
|
||||
dw = x2 - x1
|
||||
y1 = min([start[1], end[1]])
|
||||
y2 = max([start[1], end[1]])
|
||||
dh = y2 - y1
|
||||
#print(x1, y1, x2, y2, dw, dh)
|
||||
|
||||
# random: top left or bottom right as the corner between the paths
|
||||
tx, ty = (x1, y1) if random.random() >= 0.5 else (x2, y2)
|
||||
|
||||
for x in range(x1, x1+dw):
|
||||
self.grid.at((x, ty)).walkable = True
|
||||
#self.grid.at((x, ty)).color = (255, 0, 0)
|
||||
#self.grid.at((x, ty)).tilesprite = -1
|
||||
for y in range(y1, y1+dh):
|
||||
self.grid.at((tx, y)).walkable = True
|
||||
#self.grid.at((tx, y)).color = (0, 255, 0)
|
||||
#self.grid.at((tx, y)).tilesprite = -1
|
||||
prev_room = room
|
||||
|
||||
|
||||
# Tile painting
|
||||
possibilities = None
|
||||
while possibilities or possibilities is None:
|
||||
possibilities = ct.wfc_pass(self.grid, possibilities)
|
||||
|
||||
return feature_coords
|
|
@ -1,300 +0,0 @@
|
|||
import mcrfpy
|
||||
mcrfpy.createScene("play")
|
||||
ui = mcrfpy.sceneUI("play")
|
||||
t = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16) # 12, 11)
|
||||
font = mcrfpy.Font("assets/JetbrainsMono.ttf")
|
||||
|
||||
frame_color = (64, 64, 128)
|
||||
|
||||
grid = mcrfpy.Grid(20, 15, t, 10, 10, 800, 595)
|
||||
grid.zoom = 2.0
|
||||
entity_frame = mcrfpy.Frame(815, 10, 194, 595, fill_color = frame_color)
|
||||
inventory_frame = mcrfpy.Frame(10, 610, 800, 143, fill_color = frame_color)
|
||||
stats_frame = mcrfpy.Frame(815, 610, 194, 143, fill_color = frame_color)
|
||||
|
||||
begin_btn = mcrfpy.Frame(350,250,100,100, fill_color = (255,0,0))
|
||||
begin_btn.children.append(mcrfpy.Caption(5, 5, "Begin", font))
|
||||
def cos_keys(key, state):
|
||||
if key == 'M' and state == 'start':
|
||||
mapgen()
|
||||
elif state == "end": return
|
||||
elif key == "W":
|
||||
player.move("N")
|
||||
elif key == "A":
|
||||
player.move("W")
|
||||
elif key == "S":
|
||||
player.move("S")
|
||||
elif key == "D":
|
||||
player.move("E")
|
||||
|
||||
|
||||
def cos_init(*args):
|
||||
if args[3] != "start": return
|
||||
mcrfpy.keypressScene(cos_keys)
|
||||
ui.remove(4)
|
||||
|
||||
begin_btn.click = cos_init
|
||||
|
||||
[ui.append(e) for e in (grid, entity_frame, inventory_frame, stats_frame, begin_btn)]
|
||||
|
||||
import random
|
||||
def rcolor():
|
||||
return tuple([random.randint(0, 255) for i in range(3)]) # TODO list won't work with GridPoint.color, so had to cast to tuple
|
||||
|
||||
x_max, y_max = grid.grid_size
|
||||
for x in range(x_max):
|
||||
for y in range(y_max):
|
||||
grid.at((x,y)).color = rcolor()
|
||||
|
||||
from math import pi, cos, sin
|
||||
def mapgen(room_size_max = 7, room_size_min = 3, room_count = 4):
|
||||
# reset map
|
||||
for x in range(x_max):
|
||||
for y in range(y_max):
|
||||
grid.at((x, y)).walkable = False
|
||||
grid.at((x, y)).transparent= False
|
||||
grid.at((x,y)).tilesprite = random.choices([40, 28], weights=[.8, .2])[0]
|
||||
global cos_entities
|
||||
for e in cos_entities:
|
||||
e.e.position = (999,999) # TODO
|
||||
e.die()
|
||||
cos_entities = []
|
||||
|
||||
#Dungeon generation
|
||||
centers = []
|
||||
attempts = 0
|
||||
while len(centers) < room_count:
|
||||
# Leaving this attempt here for later comparison. These rooms sucked.
|
||||
# overlapping, uninteresting hallways, crowded into the corners sometimes, etc.
|
||||
attempts += 1
|
||||
if attempts > room_count * 15: break
|
||||
# room_left = random.randint(1, x_max)
|
||||
# room_top = random.randint(1, y_max)
|
||||
|
||||
# Take 2 - circular distribution of rooms
|
||||
angle_mid = (len(centers) / room_count) * 2 * pi + 0.785
|
||||
angle = random.uniform(angle_mid - 0.25, angle_mid + 0.25)
|
||||
radius = random.uniform(3, 14)
|
||||
room_left = int(radius * cos(angle)) + int(x_max/2)
|
||||
if room_left <= 1: room_left = 1
|
||||
if room_left > x_max - 1: room_left = x_max - 2
|
||||
room_top = int(radius * sin(angle)) + int(y_max/2)
|
||||
if room_top <= 1: room_top = 1
|
||||
if room_top > y_max - 1: room_top = y_max - 2
|
||||
room_w = random.randint(room_size_min, room_size_max)
|
||||
if room_w + room_left >= x_max: room_w = x_max - room_left - 2
|
||||
room_h = random.randint(room_size_min, room_size_max)
|
||||
if room_h + room_top >= y_max: room_h = y_max - room_top - 2
|
||||
#print(room_left, room_top, room_left + room_w, room_top + room_h)
|
||||
if any( # centers contained in this randomly generated room
|
||||
[c[0] >= room_left and c[0] <= room_left + room_w and c[1] >= room_top and c[1] <= room_top + room_h for c in centers]
|
||||
):
|
||||
continue # re-randomize the room position
|
||||
centers.append(
|
||||
(int(room_left + (room_w/2)), int(room_top + (room_h/2)))
|
||||
)
|
||||
|
||||
for x in range(room_w):
|
||||
for y in range(room_h):
|
||||
grid.at((room_left+x, room_top+y)).walkable=True
|
||||
grid.at((room_left+x, room_top+y)).transparent=True
|
||||
grid.at((room_left+x, room_top+y)).tilesprite = random.choice([48, 49, 50, 51, 52, 53])
|
||||
|
||||
# generate a boulder
|
||||
if (room_w > 2 and room_h > 2):
|
||||
room_boulder_x, room_boulder_y = random.randint(room_left+1, room_left+room_w-1), random.randint(room_top+1, room_top+room_h-1)
|
||||
cos_entities.append(BoulderEntity(room_boulder_x, room_boulder_y))
|
||||
|
||||
print(f"{room_count} rooms generated after {attempts} attempts.")
|
||||
#print(centers)
|
||||
# hallways
|
||||
pairs = []
|
||||
for c1 in centers:
|
||||
for c2 in centers:
|
||||
if c1 == c2: continue
|
||||
if (c2, c1) in pairs or (c1, c2) in pairs: continue
|
||||
left = min(c1[0], c2[0])
|
||||
right = max(c1[0], c2[0])
|
||||
top = min(c1[1], c2[1])
|
||||
bottom = max(c1[1], c2[1])
|
||||
|
||||
corners = [(left, top), (left, bottom), (right, top), (right, bottom)]
|
||||
corners.remove(c1)
|
||||
corners.remove(c2)
|
||||
random.shuffle(corners)
|
||||
target, other = corners
|
||||
for x in range(target[0], other[0], -1 if target[0] > other[0] else 1):
|
||||
was_walkable = grid.at((x, target[1])).walkable
|
||||
grid.at((x, target[1])).walkable=True
|
||||
grid.at((x, target[1])).transparent=True
|
||||
if not was_walkable:
|
||||
grid.at((x, target[1])).tilesprite = random.choices([0, 12, 24], weights=[.6, .3, .1])[0]
|
||||
for y in range(target[1], other[1], -1 if target[1] > other[1] else 1):
|
||||
was_walkable = grid.at((target[0], y)).walkable
|
||||
grid.at((target[0], y)).walkable=True
|
||||
grid.at((target[0], y)).transparent=True
|
||||
if not was_walkable:
|
||||
grid.at((target[0], y)).tilesprite = random.choices([0, 12, 24], weights=[0.4, 0.3, 0.3])[0]
|
||||
pairs.append((c1, c2))
|
||||
|
||||
|
||||
# spawn exit and button
|
||||
spawn_points = []
|
||||
for x in range(x_max):
|
||||
for y in range(y_max):
|
||||
if grid.at((x, y)).walkable:
|
||||
spawn_points.append((x, y))
|
||||
random.shuffle(spawn_points)
|
||||
door_spawn, button_spawn = spawn_points[:2]
|
||||
cos_entities.append(ExitEntity(*door_spawn, *button_spawn))
|
||||
|
||||
# respawn player
|
||||
global player
|
||||
if player:
|
||||
player.position = (999,999) # TODO - die() is broken and I don't know why
|
||||
player = PlayerEntity()
|
||||
|
||||
|
||||
#for x in range(x_max):
|
||||
# for y in range(y_max):
|
||||
# if grid.at((x, y)).walkable:
|
||||
# #grid.at((x,y)).tilesprite = random.choice([48, 49, 50, 51, 52, 53])
|
||||
# pass
|
||||
# else:
|
||||
# #grid.at((x,y)).tilesprite = random.choices([40, 28], weights=[.8, .2])[0]
|
||||
|
||||
#131 - last sprite
|
||||
#123, 124 - brown, grey rats
|
||||
#121 - ghost
|
||||
#114, 115, 116 - green, red, blue potion
|
||||
#102 - shield
|
||||
#98 - low armor guy, #97 - high armor guy
|
||||
#89 - chest, #91 - empty chest
|
||||
#84 - wizard
|
||||
#82 - barrel
|
||||
#66 - boulder
|
||||
#64, 65 - graves
|
||||
#48 - 53: ground (not going to figure out how they fit together tonight)
|
||||
#42 - button-looking ground
|
||||
#40 - basic solid wall
|
||||
#36, 37, 38 - wall (left, middle, right)
|
||||
#28 solid wall but with a grate
|
||||
#21 - wide open door, 33 medium open, 45 closed door
|
||||
#0 - basic dirt
|
||||
class MyEntity:
|
||||
def __init__(self, x=0, y=0, sprite_num=86):
|
||||
self.e = mcrfpy.Entity(x, y, t, sprite_num)
|
||||
grid.entities.append(self.e)
|
||||
def die(self):
|
||||
for i in range(len(grid.entities)):
|
||||
e = grid.entities[i]
|
||||
if e == self.e:
|
||||
grid.entities.remove(i)
|
||||
break
|
||||
def bump(self, other, dx, dy):
|
||||
raise NotImplementedError
|
||||
|
||||
def try_move(self, dx, dy):
|
||||
tx, ty = int(self.e.position[0] + dx), int(self.e.position[1] + dy)
|
||||
for e in cos_entities:
|
||||
if e.e.position == (tx, ty):
|
||||
#print(f"bumping {e}")
|
||||
return e.bump(self, dx, dy)
|
||||
if tx < 0 or tx >= x_max:
|
||||
#print("out of bounds horizontally")
|
||||
return False
|
||||
if ty < 0 or ty >= y_max:
|
||||
#print("out of bounds vertically")
|
||||
return False
|
||||
if grid.at((tx, ty)).walkable == True:
|
||||
#print("Motion!")
|
||||
self.e.position = (tx, ty)
|
||||
return True
|
||||
else:
|
||||
#print("Bonk")
|
||||
return False
|
||||
|
||||
def _relative_move(self, dx, dy):
|
||||
tx, ty = int(self.e.position[0] + dx), int(self.e.position[1] + dy)
|
||||
self.e.position = (tx, ty)
|
||||
|
||||
|
||||
def move(self, direction):
|
||||
if direction == "N":
|
||||
self.try_move(0, -1)
|
||||
elif direction == "E":
|
||||
self.try_move(1, 0)
|
||||
elif direction == "S":
|
||||
self.try_move(0, 1)
|
||||
elif direction == "W":
|
||||
self.try_move(-1, 0)
|
||||
|
||||
cos_entities = []
|
||||
|
||||
class PlayerEntity(MyEntity):
|
||||
def __init__(self):
|
||||
# find spawn point
|
||||
spawn_points = []
|
||||
for x in range(x_max):
|
||||
for y in range(y_max):
|
||||
if grid.at((x, y)).walkable:
|
||||
spawn_points.append((x, y))
|
||||
random.shuffle(spawn_points)
|
||||
for spawn in spawn_points:
|
||||
for e in cos_entities:
|
||||
if e.e.position == spawn: break
|
||||
else:
|
||||
break
|
||||
|
||||
#print(f"spawn at {spawn}")
|
||||
super().__init__(spawn[0], spawn[1], sprite_num=84)
|
||||
|
||||
class BoulderEntity(MyEntity):
|
||||
def __init__(self, x, y):
|
||||
super().__init__(x, y, 66)
|
||||
|
||||
def bump(self, other, dx, dy):
|
||||
if type(other) == BoulderEntity:
|
||||
#print("Boulders can't push boulders")
|
||||
return False
|
||||
tx, ty = int(self.e.position[0] + dx), int(self.e.position[1] + dy)
|
||||
# Is the boulder blocked the same direction as the bumper? If not, let's both move
|
||||
old_pos = int(self.e.position[0]), int(self.e.position[1])
|
||||
if self.try_move(dx, dy):
|
||||
other.e.position = old_pos
|
||||
return True
|
||||
|
||||
class ButtonEntity(MyEntity):
|
||||
def __init__(self, x, y, exit):
|
||||
super().__init__(x, y, 42)
|
||||
self.exit = exit
|
||||
|
||||
def bump(self, other, dx, dy):
|
||||
if type(other) == BoulderEntity:
|
||||
self.exit.unlock()
|
||||
other._relative_move(dx, dy)
|
||||
return True
|
||||
|
||||
class ExitEntity(MyEntity):
|
||||
def __init__(self, x, y, bx, by):
|
||||
super().__init__(x, y, 45)
|
||||
self.my_button = ButtonEntity(bx, by, self)
|
||||
self.unlocked = False
|
||||
global cos_entities
|
||||
cos_entities.append(self.my_button)
|
||||
|
||||
def unlock(self):
|
||||
self.e.sprite_number = 21
|
||||
self.unlocked = True
|
||||
|
||||
def lock(self):
|
||||
self.e.sprite_number = 45
|
||||
self.unlocked = True
|
||||
|
||||
def bump(self, other, dx, dy):
|
||||
if type(other) == BoulderEntity:
|
||||
return False
|
||||
if self.unlocked:
|
||||
other._relative_move(dx, dy)
|
||||
|
||||
player = None
|
|
@ -0,0 +1,223 @@
|
|||
tiles = {}
|
||||
deltas = [
|
||||
(-1, -1), ( 0, -1), (+1, -1),
|
||||
(-1, 0), ( 0, 0), (+1, 0),
|
||||
(-1, +1), ( 0, +1), (+1, +1)
|
||||
]
|
||||
|
||||
class TileInfo:
|
||||
GROUND, WALL, DONTCARE = True, False, None
|
||||
chars = {
|
||||
"X": WALL,
|
||||
"_": GROUND,
|
||||
"?": DONTCARE
|
||||
}
|
||||
symbols = {v: k for k, v in chars.items()}
|
||||
|
||||
def __init__(self, values:dict):
|
||||
self._values = values
|
||||
self.rules = []
|
||||
self.chance = 1.0
|
||||
|
||||
@staticmethod
|
||||
def from_grid(grid, xy:tuple):
|
||||
values = {}
|
||||
for d in deltas:
|
||||
tx, ty = d[0] + xy[0], d[1] + xy[1]
|
||||
try:
|
||||
values[d] = grid.at((tx, ty)).walkable
|
||||
except ValueError:
|
||||
values[d] = True
|
||||
return TileInfo(values)
|
||||
|
||||
@staticmethod
|
||||
def from_string(s):
|
||||
values = {}
|
||||
for d, c in zip(deltas, s):
|
||||
values[d] = TileInfo.chars[c]
|
||||
return TileInfo(values)
|
||||
|
||||
def __hash__(self):
|
||||
"""for use as a dictionary key"""
|
||||
return hash(tuple(self._values.items()))
|
||||
|
||||
def match(self, other:"TileInfo"):
|
||||
for d, rule in self._values.items():
|
||||
if rule is TileInfo.DONTCARE: continue
|
||||
if other._values[d] is TileInfo.DONTCARE: continue
|
||||
if rule != other._values[d]: return False
|
||||
return True
|
||||
|
||||
def show(self):
|
||||
nine = ['', '', '\n'] * 3
|
||||
for k, end in zip(deltas, nine):
|
||||
c = TileInfo.symbols[self._values[k]]
|
||||
print(c, end=end)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<TileInfo {self._values}>"
|
||||
|
||||
cardinal_directions = {
|
||||
"N": ( 0, -1),
|
||||
"S": ( 0, +1),
|
||||
"E": (-1, 0),
|
||||
"W": (+1, 0)
|
||||
}
|
||||
|
||||
def special_rule_verify(rule, grid, xy, unverified_tiles, pass_unverified=False):
|
||||
cardinal, allowed_tile = rule
|
||||
dxy = cardinal_directions[cardinal.upper()]
|
||||
tx, ty = xy[0] + dxy[0], xy[1] + dxy[1]
|
||||
#print(f"Special rule: {cardinal} {allowed_tile} {type(allowed_tile)} -> ({tx}, {ty}) [{grid.at((tx, ty)).tilesprite}]{'*' if (tx, ty) in unverified_tiles else ''}")
|
||||
if (tx, ty) in unverified_tiles and cardinal in "nsew": return pass_unverified
|
||||
try:
|
||||
return grid.at((tx, ty)).tilesprite == allowed_tile
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
import random
|
||||
tile_of_last_resort = 431
|
||||
|
||||
def find_possible_tiles(grid, x, y, unverified_tiles=None, pass_unverified=False):
|
||||
ti = TileInfo.from_grid(grid, (x, y))
|
||||
if unverified_tiles is None: unverified_tiles = []
|
||||
matches = [(k, v) for k, v in tiles.items() if k.match(ti)]
|
||||
if not matches:
|
||||
return []
|
||||
possible = []
|
||||
if not any([tileinfo.rules for tileinfo, _ in matches]):
|
||||
# make weighted choice, as the tile does not depend on verification
|
||||
wts = [k.chance for k, v in matches]
|
||||
tileinfo, tile = random.choices(matches, weights=wts)[0]
|
||||
return [tile]
|
||||
|
||||
for tileinfo, tile in matches:
|
||||
if not tileinfo.rules:
|
||||
possible.append(tile)
|
||||
continue
|
||||
for r in tileinfo.rules: #for r in ...: if ... continue == more readable than an "any" 1-liner
|
||||
p = special_rule_verify(r, grid, (x,y),
|
||||
unverified_tiles=unverified_tiles,
|
||||
pass_unverified = pass_unverified
|
||||
)
|
||||
if p:
|
||||
possible.append(tile)
|
||||
continue
|
||||
return list(set(list(possible)))
|
||||
|
||||
def wfc_first_pass(grid):
|
||||
w, h = grid.grid_size
|
||||
possibilities = {}
|
||||
for x in range(0, w):
|
||||
for y in range(0, h):
|
||||
matches = find_possible_tiles(grid, x, y, pass_unverified=True)
|
||||
if len(matches) == 0:
|
||||
grid.at((x, y)).tilesprite = tile_of_last_resort
|
||||
possibilities[(x,y)] = matches
|
||||
elif len(matches) == 1:
|
||||
grid.at((x, y)).tilesprite = matches[0]
|
||||
else:
|
||||
possibilities[(x,y)] = matches
|
||||
return possibilities
|
||||
|
||||
def wfc_pass(grid, possibilities=None):
|
||||
w, h = grid.grid_size
|
||||
if possibilities is None:
|
||||
#print("first pass results:")
|
||||
possibilities = wfc_first_pass(grid)
|
||||
counts = {}
|
||||
for v in possibilities.values():
|
||||
if len(v) in counts: counts[len(v)] += 1
|
||||
else: counts[len(v)] = 1
|
||||
print(counts)
|
||||
return possibilities
|
||||
elif len(possibilities) == 0:
|
||||
print("We're done!")
|
||||
return
|
||||
old_possibilities = possibilities
|
||||
possibilities = {}
|
||||
for (x, y) in old_possibilities.keys():
|
||||
matches = find_possible_tiles(grid, x, y, unverified_tiles=old_possibilities.keys(), pass_unverified = True)
|
||||
if len(matches) == 0:
|
||||
print((x,y), matches)
|
||||
grid.at((x, y)).tilesprite = tile_of_last_resort
|
||||
possibilities[(x,y)] = matches
|
||||
elif len(matches) == 1:
|
||||
grid.at((x, y)).tilesprite = matches[0]
|
||||
else:
|
||||
grid.at((x, y)).tilesprite = -1
|
||||
grid.at((x, y)).color = (32 * len(matches), 32 * len(matches), 32 * len(matches))
|
||||
possibilities[(x,y)] = matches
|
||||
|
||||
if len(possibilities) == len(old_possibilities):
|
||||
#print("No more tiles could be solved without collapse")
|
||||
counts = {}
|
||||
for v in possibilities.values():
|
||||
if len(v) in counts: counts[len(v)] += 1
|
||||
else: counts[len(v)] = 1
|
||||
#print(counts)
|
||||
if 0 in counts: del counts[0]
|
||||
if len(counts) == 0:
|
||||
print("Contrats! You broke it! (insufficient tile defs to solve remaining tiles)")
|
||||
return []
|
||||
target = min(list(counts.keys()))
|
||||
while possibilities:
|
||||
for (x, y) in possibilities.keys():
|
||||
if len(possibilities[(x, y)]) != target:
|
||||
continue
|
||||
ti = TileInfo.from_grid(grid, (x, y))
|
||||
matches = [(k, v) for k, v in tiles.items() if k.match(ti)]
|
||||
verifiable_matches = find_possible_tiles(grid, x, y, unverified_tiles=possibilities.keys())
|
||||
if not verifiable_matches: continue
|
||||
#print(f"collapsing {(x, y)} ({target} choices)")
|
||||
matches = [(k, v) for k, v in matches if v in verifiable_matches]
|
||||
wts = [k.chance for k, v in matches]
|
||||
tileinfo, tile = random.choices(matches, weights=wts)[0]
|
||||
grid.at((x, y)).tilesprite = tile
|
||||
del possibilities[(x, y)]
|
||||
break
|
||||
else:
|
||||
selected = random.choice(list(possibilities.keys()))
|
||||
#print(f"No tiles have verifable solutions: QUANTUM -> {selected}")
|
||||
# sprinkle some quantumness on it
|
||||
ti = TileInfo.from_grid(grid, (x, y))
|
||||
matches = [(k, v) for k, v in tiles.items() if k.match(ti)]
|
||||
wts = [k.chance for k, v in matches]
|
||||
if not wts:
|
||||
print(f"This one: {(x,y)} {matches}\n{wts}")
|
||||
del possibilities[(x, y)]
|
||||
return possibilities
|
||||
tileinfo, tile = random.choices(matches, weights=wts)[0]
|
||||
grid.at((x, y)).tilesprite = tile
|
||||
del possibilities[(x, y)]
|
||||
|
||||
return possibilities
|
||||
|
||||
#with open("scripts/tile_def.txt", "r") as f:
|
||||
with open("scripts/simple_tiles.txt", "r") as f:
|
||||
for block in f.read().split('\n\n'):
|
||||
info, constraints = block.split('\n', 1)
|
||||
if '#' in info:
|
||||
info, comment = info.split('#', 1)
|
||||
rules = []
|
||||
if '@' in info:
|
||||
info, *block_rules = info.split('@')
|
||||
#print(block_rules)
|
||||
for r in block_rules:
|
||||
rules.append((r[0], int(r[1:])))
|
||||
#cardinal_dir = block_rules[0]
|
||||
#partner
|
||||
if ':' not in info:
|
||||
tile_id = int(info)
|
||||
chance = 1.0
|
||||
else:
|
||||
tile_id, chance = info.split(':')
|
||||
tile_id = int(tile_id)
|
||||
chance = float(chance.strip())
|
||||
constraints = constraints.replace('\n', '')
|
||||
k = TileInfo.from_string(constraints)
|
||||
k.rules = rules
|
||||
k.chance = chance
|
||||
tiles[k] = tile_id
|
||||
|
||||
|
|
@ -1,60 +1,109 @@
|
|||
import mcrfpy
|
||||
#t = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16) # 12, 11)
|
||||
#t = mcrfpy.Texture("assets/kenney_TD_MR_IP.png", 16, 16) # 12, 11)
|
||||
font = mcrfpy.Font("assets/JetbrainsMono.ttf")
|
||||
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
|
||||
print("[game.py] Default texture:")
|
||||
print(mcrfpy.default_texture)
|
||||
print(type(mcrfpy.default_texture))
|
||||
frame_color = (64, 64, 128)
|
||||
|
||||
# build test widgets
|
||||
import random
|
||||
import cos_entities as ce
|
||||
import cos_level as cl
|
||||
#import cos_tiles as ct
|
||||
|
||||
mcrfpy.createScene("pytest")
|
||||
mcrfpy.setScene("pytest")
|
||||
ui = mcrfpy.sceneUI("pytest")
|
||||
class Crypt:
|
||||
def __init__(self):
|
||||
mcrfpy.createScene("play")
|
||||
self.ui = mcrfpy.sceneUI("play")
|
||||
mcrfpy.setScene("play")
|
||||
mcrfpy.keypressScene(self.cos_keys)
|
||||
|
||||
# Frame
|
||||
f = mcrfpy.Frame(25, 19, 462, 346, fill_color=(255, 92, 92))
|
||||
print("Frame alive")
|
||||
# fill (LinkedColor / Color): f.fill_color
|
||||
# outline (LinkedColor / Color): f.outline_color
|
||||
# pos (LinkedVector / Vector): f.pos
|
||||
# size (LinkedVector / Vector): f.size
|
||||
entity_frame = mcrfpy.Frame(815, 10, 194, 595, fill_color = frame_color)
|
||||
inventory_frame = mcrfpy.Frame(10, 610, 800, 143, fill_color = frame_color)
|
||||
stats_frame = mcrfpy.Frame(815, 610, 194, 143, fill_color = frame_color)
|
||||
|
||||
# Caption
|
||||
print("Caption attempt w/ fill_color:")
|
||||
#c = mcrfpy.Caption(512+25, 19, "Hi.", font)
|
||||
#c = mcrfpy.Caption(512+25, 19, "Hi.", font, fill_color=(255, 128, 128))
|
||||
c = mcrfpy.Caption(512+25, 19, "Hi.", font, fill_color=mcrfpy.Color(255, 128, 128), outline_color=(128, 255, 128))
|
||||
print("Caption alive")
|
||||
# fill (LinkedColor / Color): c.fill_color
|
||||
#color_val = c.fill_color
|
||||
print(c.fill_color)
|
||||
#print("Set a fill color")
|
||||
#c.fill_color = (255, 255, 255)
|
||||
print("Lol, did it segfault?")
|
||||
# outline (LinkedColor / Color): c.outline_color
|
||||
# font (Font): c.font
|
||||
# pos (LinkedVector / Vector): c.pos
|
||||
#self.level = cl.Level(30, 23)
|
||||
self.entities = []
|
||||
self.depth=1
|
||||
self.create_level(self.depth)
|
||||
#self.grid = mcrfpy.Grid(20, 15, t, (10, 10), (1014, 758))
|
||||
self.player = ce.PlayerEntity(game=self)
|
||||
self.swap_level(self.level, self.spawn_point)
|
||||
|
||||
# Sprite
|
||||
s = mcrfpy.Sprite(25, 384+19, texture, 86, 9.0)
|
||||
# pos (LinkedVector / Vector): s.pos
|
||||
# texture (Texture): s.texture
|
||||
# Test Entities
|
||||
#ce.BoulderEntity(9, 7, game=self)
|
||||
#ce.BoulderEntity(9, 8, game=self)
|
||||
#ce.ExitEntity(12, 6, 14, 4, game=self)
|
||||
# scene setup
|
||||
|
||||
# Grid
|
||||
g = mcrfpy.Grid(10, 10, texture, 512+25, 384+19, 462, 346)
|
||||
# texture (Texture): g.texture
|
||||
# pos (LinkedVector / Vector): g.pos
|
||||
# size (LinkedVector / Vector): g.size
|
||||
|
||||
for _x in range(10):
|
||||
for _y in range(10):
|
||||
g.at((_x, _y)).color = (255 - _x*25, 255 - _y*25, 255)
|
||||
g.zoom = 2.0
|
||||
[self.ui.append(e) for e in (self.grid,)] # entity_frame, inventory_frame, stats_frame)]
|
||||
|
||||
[ui.append(d) for d in (f, c, s, g)]
|
||||
self.possibilities = None # track WFC possibilities between rounds
|
||||
|
||||
print("built!")
|
||||
def add_entity(self, e:ce.COSEntity):
|
||||
self.entities.append(e)
|
||||
self.entities.sort(key = lambda e: e.draw_order, reverse=False)
|
||||
# hack / workaround for grid.entities not interable
|
||||
while len(self.grid.entities): # while there are entities on the grid,
|
||||
self.grid.entities.remove(0) # remove the 1st ("0th")
|
||||
for e in self.entities:
|
||||
self.grid.entities.append(e._entity)
|
||||
|
||||
# tests
|
||||
def create_level(self, depth):
|
||||
#if depth < 3:
|
||||
# features = None
|
||||
self.level = cl.Level(30, 23)
|
||||
self.grid = self.level.grid
|
||||
coords = self.level.generate()
|
||||
self.entities = []
|
||||
for k, v in coords.items():
|
||||
if k == "spawn":
|
||||
self.spawn_point = v
|
||||
elif k == "boulder":
|
||||
ce.BoulderEntity(v[0], v[1], game=self)
|
||||
elif k == "button":
|
||||
pass
|
||||
elif k == "exit":
|
||||
ce.ExitEntity(v[0], v[1], coords["button"][0], coords["button"][1], game=self)
|
||||
|
||||
def cos_keys(self, key, state):
|
||||
d = None
|
||||
if state == "end": return
|
||||
elif key == "W": d = (0, -1)
|
||||
elif key == "A": d = (-1, 0)
|
||||
elif key == "S": d = (0, 1)
|
||||
elif key == "D": d = (1, 0)
|
||||
elif key == "M": self.level.generate()
|
||||
elif key == "R":
|
||||
self.level.reset()
|
||||
self.possibilities = None
|
||||
elif key == "T":
|
||||
self.level.split()
|
||||
self.possibilities = None
|
||||
elif key == "Y": self.level.split(single=True)
|
||||
elif key == "U": self.level.highlight(+1)
|
||||
elif key == "I": self.level.highlight(-1)
|
||||
elif key == "O":
|
||||
self.level.wall_rooms()
|
||||
self.possibilities = None
|
||||
#elif key == "P": ct.format_tiles(self.grid)
|
||||
elif key == "P":
|
||||
self.possibilities = ct.wfc_pass(self.grid, self.possibilities)
|
||||
if d: self.player.try_move(*d)
|
||||
|
||||
def swap_level(self, new_level, spawn_point):
|
||||
self.level = new_level
|
||||
self.grid = self.level.grid
|
||||
self.grid.zoom = 2.0
|
||||
# TODO, make an entity mover function
|
||||
self.add_entity(self.player)
|
||||
self.player.grid = self.grid
|
||||
self.player.draw_pos = spawn_point
|
||||
self.grid.entities.append(self.player._entity)
|
||||
try:
|
||||
self.ui.remove(0)
|
||||
except:
|
||||
pass
|
||||
self.ui.append(self.grid)
|
||||
|
||||
crypt = Crypt()
|
||||
|
|
|
@ -1,221 +0,0 @@
|
|||
#print("Hello mcrogueface")
|
||||
import mcrfpy
|
||||
import cos_play
|
||||
# Universal stuff
|
||||
font = mcrfpy.Font("assets/JetbrainsMono.ttf")
|
||||
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16) #12, 11)
|
||||
texture_cold = mcrfpy.Texture("assets/kenney_ice.png", 16, 16) #12, 11)
|
||||
texture_hot = mcrfpy.Texture("assets/kenney_lava.png", 16, 16) #12, 11)
|
||||
|
||||
# Test stuff
|
||||
mcrfpy.createScene("boom")
|
||||
mcrfpy.setScene("boom")
|
||||
ui = mcrfpy.sceneUI("boom")
|
||||
box = mcrfpy.Frame(40, 60, 200, 300, fill_color=(255,128,0), outline=4.0, outline_color=(64,64,255,96))
|
||||
ui.append(box)
|
||||
|
||||
#caption = mcrfpy.Caption(10, 10, "Clicky", font, (255, 255, 255, 255), (0, 0, 0, 255))
|
||||
#box.click = lambda x, y, btn, type: print("Hello callback: ", x, y, btn, type)
|
||||
#box.children.append(caption)
|
||||
|
||||
test_sprite_number = 86
|
||||
sprite = mcrfpy.Sprite(20, 60, texture, test_sprite_number, 4.0)
|
||||
spritecap = mcrfpy.Caption(5, 5, "60", font)
|
||||
def click_sprite(x, y, btn, action):
|
||||
global test_sprite_number
|
||||
if action != "start": return
|
||||
if btn in ("left", "wheel_up"):
|
||||
test_sprite_number -= 1
|
||||
elif btn in ("right", "wheel_down"):
|
||||
test_sprite_number += 1
|
||||
sprite.sprite_number = test_sprite_number # TODO - inconsistent naming for __init__, __repr__ and getsetter: sprite_number vs sprite_index
|
||||
spritecap.text = test_sprite_number
|
||||
|
||||
sprite.click = click_sprite # TODO - sprites don't seem to correct for screen position or scale when clicking
|
||||
box.children.append(sprite)
|
||||
box.children.append(spritecap)
|
||||
box.click = click_sprite
|
||||
|
||||
f_a = mcrfpy.Frame(250, 60, 80, 80, fill_color=(255, 92, 92))
|
||||
f_a_txt = mcrfpy.Caption(5, 5, "0", font)
|
||||
|
||||
f_b = mcrfpy.Frame(340, 60, 80, 80, fill_color=(92, 255, 92))
|
||||
f_b_txt = mcrfpy.Caption(5, 5, "0", font)
|
||||
|
||||
f_c = mcrfpy.Frame(430, 60, 80, 80, fill_color=(92, 92, 255))
|
||||
f_c_txt = mcrfpy.Caption(5, 5, "0", font)
|
||||
|
||||
|
||||
ui.append(f_a)
|
||||
f_a.children.append(f_a_txt)
|
||||
ui.append(f_b)
|
||||
f_b.children.append(f_b_txt)
|
||||
ui.append(f_c)
|
||||
f_c.children.append(f_c_txt)
|
||||
|
||||
import sys
|
||||
def ding(*args):
|
||||
f_a_txt.text = str(sys.getrefcount(ding)) + " refs"
|
||||
f_b_txt.text = sys.getrefcount(dong)
|
||||
f_c_txt.text = sys.getrefcount(stress_test)
|
||||
|
||||
def dong(*args):
|
||||
f_a_txt.text = str(sys.getrefcount(ding)) + " refs"
|
||||
f_b_txt.text = sys.getrefcount(dong)
|
||||
f_c_txt.text = sys.getrefcount(stress_test)
|
||||
|
||||
running = False
|
||||
timers = []
|
||||
|
||||
def add_ding():
|
||||
global timers
|
||||
n = len(timers)
|
||||
mcrfpy.setTimer(f"timer{n}", ding, 100)
|
||||
print("+1 ding:", timers)
|
||||
|
||||
def add_dong():
|
||||
global timers
|
||||
n = len(timers)
|
||||
mcrfpy.setTimer(f"timer{n}", dong, 100)
|
||||
print("+1 dong:", timers)
|
||||
|
||||
def remove_random():
|
||||
global timers
|
||||
target = random.choice(timers)
|
||||
print("-1 timer:", target)
|
||||
print("remove from list")
|
||||
timers.remove(target)
|
||||
print("delTimer")
|
||||
mcrfpy.delTimer(target)
|
||||
print("done")
|
||||
|
||||
import random
|
||||
import time
|
||||
def stress_test(*args):
|
||||
global running
|
||||
global timers
|
||||
if not running:
|
||||
print("stress test initial")
|
||||
running = True
|
||||
timers.append("recurse")
|
||||
add_ding()
|
||||
add_dong()
|
||||
mcrfpy.setTimer("recurse", stress_test, 1000)
|
||||
mcrfpy.setTimer("terminate", lambda *args: mcrfpy.delTimer("recurse"), 30000)
|
||||
ding(); dong()
|
||||
else:
|
||||
#print("stress test random activity")
|
||||
#random.choice([
|
||||
# add_ding,
|
||||
# add_dong,
|
||||
# remove_random
|
||||
# ])()
|
||||
#print(timers)
|
||||
print("Segfaultin' time")
|
||||
mcrfpy.delTimer("recurse")
|
||||
print("Does this still work?")
|
||||
time.sleep(0.5)
|
||||
print("How about now?")
|
||||
|
||||
|
||||
stress_test()
|
||||
|
||||
|
||||
# Loading Screen
|
||||
mcrfpy.createScene("loading")
|
||||
ui = mcrfpy.sceneUI("loading")
|
||||
#mcrfpy.setScene("loading")
|
||||
logo_texture = mcrfpy.Texture("assets/temp_logo.png", 1024, 1024)#1, 1)
|
||||
logo_sprite = mcrfpy.Sprite(50, 50, logo_texture, 0, 0.5)
|
||||
ui.append(logo_sprite)
|
||||
logo_sprite.click = lambda *args: mcrfpy.setScene("menu")
|
||||
logo_caption = mcrfpy.Caption(70, 600, "Click to Proceed", font, (255, 0, 0, 255), (0, 0, 0, 255))
|
||||
#logo_caption.fill_color =(255, 0, 0, 255)
|
||||
ui.append(logo_caption)
|
||||
|
||||
|
||||
# menu screen
|
||||
mcrfpy.createScene("menu")
|
||||
|
||||
for e in [
|
||||
mcrfpy.Caption(10, 10, "Crypt of Sokoban", font, (255, 255, 255), (0, 0, 0)),
|
||||
mcrfpy.Caption(20, 55, "a McRogueFace demo project", font, (192, 192, 192), (0, 0, 0)),
|
||||
mcrfpy.Frame(15, 70, 150, 60, fill_color=(64, 64, 128)),
|
||||
mcrfpy.Frame(15, 145, 150, 60, fill_color=(64, 64, 128)),
|
||||
mcrfpy.Frame(15, 220, 150, 60, fill_color=(64, 64, 128)),
|
||||
mcrfpy.Frame(15, 295, 150, 60, fill_color=(64, 64, 128)),
|
||||
#mcrfpy.Frame(900, 10, 100, 100, fill_color=(255, 0, 0)),
|
||||
]:
|
||||
mcrfpy.sceneUI("menu").append(e)
|
||||
|
||||
def click_once(fn):
|
||||
def wraps(*args, **kwargs):
|
||||
#print(args)
|
||||
action = args[3]
|
||||
if action != "start": return
|
||||
return fn(*args, **kwargs)
|
||||
return wraps
|
||||
|
||||
@click_once
|
||||
def asdf(x, y, btn, action):
|
||||
print(f"clicky @({x},{y}) {action}->{btn}")
|
||||
|
||||
@click_once
|
||||
def clicked_exit(*args):
|
||||
mcrfpy.exit()
|
||||
|
||||
menu_btns = [
|
||||
("Boom", lambda *args: 1 / 0),
|
||||
("Exit", clicked_exit),
|
||||
("About", lambda *args: mcrfpy.setScene("about")),
|
||||
("Settings", lambda *args: mcrfpy.setScene("settings")),
|
||||
("Start", lambda *args: mcrfpy.setScene("play"))
|
||||
]
|
||||
for i in range(len(mcrfpy.sceneUI("menu"))):
|
||||
e = mcrfpy.sceneUI("menu")[i] # TODO - fix iterator
|
||||
#print(e, type(e))
|
||||
if type(e) is not mcrfpy.Frame: continue
|
||||
label, fn = menu_btns.pop()
|
||||
#print(label)
|
||||
e.children.append(mcrfpy.Caption(5, 5, label, font, (192, 192, 255), (0,0,0)))
|
||||
e.click = fn
|
||||
|
||||
|
||||
# settings screen
|
||||
mcrfpy.createScene("settings")
|
||||
window_scaling = 1.0
|
||||
|
||||
scale_caption = mcrfpy.Caption(180, 70, "1.0x", font, (255, 255, 255), (0, 0, 0))
|
||||
#scale_caption.fill_color = (255, 255, 255) # TODO - mcrfpy.Caption.__init__ is not setting colors
|
||||
for e in [
|
||||
mcrfpy.Caption(10, 10, "Settings", font, (255, 255, 255), (0, 0, 0)),
|
||||
mcrfpy.Frame(15, 70, 150, 60, fill_color=(64, 64, 128)), # +
|
||||
mcrfpy.Frame(300, 70, 150, 60, fill_color=(64, 64, 128)), # -
|
||||
mcrfpy.Frame(15, 295, 150, 60, fill_color=(64, 64, 128)),
|
||||
scale_caption,
|
||||
]:
|
||||
mcrfpy.sceneUI("settings").append(e)
|
||||
|
||||
@click_once
|
||||
def game_scale(x, y, btn, action, delta):
|
||||
global window_scaling
|
||||
print(f"WIP - scale the window from {window_scaling:.1f} to {window_scaling+delta:.1f}")
|
||||
window_scaling += delta
|
||||
scale_caption.text = f"{window_scaling:.1f}x"
|
||||
mcrfpy.setScale(window_scaling)
|
||||
#mcrfpy.setScale(2)
|
||||
|
||||
settings_btns = [
|
||||
("back", lambda *args: mcrfpy.setScene("menu")),
|
||||
("-", lambda x, y, btn, action: game_scale(x, y, btn, action, -0.1)),
|
||||
("+", lambda x, y, btn, action: game_scale(x, y, btn, action, +0.1))
|
||||
]
|
||||
|
||||
for i in range(len(mcrfpy.sceneUI("settings"))):
|
||||
e = mcrfpy.sceneUI("settings")[i] # TODO - fix iterator
|
||||
#print(e, type(e))
|
||||
if type(e) is not mcrfpy.Frame: continue
|
||||
label, fn = settings_btns.pop()
|
||||
#print(label, fn)
|
||||
e.children.append(mcrfpy.Caption(5, 5, label, font, (192, 192, 255), (0,0,0)))
|
||||
e.click = fn
|
Loading…
Reference in New Issue