Fix Grid to support None/null texture and fix error message bug
- Allow Grid to be created with None as texture parameter - Use default cell dimensions (16x16) when no texture provided - Skip sprite rendering when texture is null, but still render colors - Fix issue #77: Corrected copy/paste error in Grid.at() error messages - Grid now functional for color-only rendering and entity positioning Test created to verify Grid works without texture, showing colored cells. Closes #77
This commit is contained in:
parent
18cfe93a44
commit
1c71d8d4f7
|
@ -6,9 +6,15 @@ UIGrid::UIGrid() {}
|
||||||
|
|
||||||
UIGrid::UIGrid(int gx, int gy, std::shared_ptr<PyTexture> _ptex, sf::Vector2f _xy, sf::Vector2f _wh)
|
UIGrid::UIGrid(int gx, int gy, std::shared_ptr<PyTexture> _ptex, sf::Vector2f _xy, sf::Vector2f _wh)
|
||||||
: grid_x(gx), grid_y(gy),
|
: grid_x(gx), grid_y(gy),
|
||||||
zoom(1.0f), center_x((gx/2) * _ptex->sprite_width), center_y((gy/2) * _ptex->sprite_height),
|
zoom(1.0f),
|
||||||
ptex(_ptex), points(gx * gy)
|
ptex(_ptex), points(gx * gy)
|
||||||
{
|
{
|
||||||
|
// Use texture dimensions if available, otherwise use defaults
|
||||||
|
int cell_width = _ptex ? _ptex->sprite_width : DEFAULT_CELL_WIDTH;
|
||||||
|
int cell_height = _ptex ? _ptex->sprite_height : DEFAULT_CELL_HEIGHT;
|
||||||
|
|
||||||
|
center_x = (gx/2) * cell_width;
|
||||||
|
center_y = (gy/2) * cell_height;
|
||||||
entities = std::make_shared<std::list<std::shared_ptr<UIEntity>>>();
|
entities = std::make_shared<std::list<std::shared_ptr<UIEntity>>>();
|
||||||
|
|
||||||
box.setSize(_wh);
|
box.setSize(_wh);
|
||||||
|
@ -18,7 +24,10 @@ UIGrid::UIGrid(int gx, int gy, std::shared_ptr<PyTexture> _ptex, sf::Vector2f _x
|
||||||
// create renderTexture with maximum theoretical size; sprite can resize to show whatever amount needs to be rendered
|
// create renderTexture with maximum theoretical size; sprite can resize to show whatever amount needs to be rendered
|
||||||
renderTexture.create(1920, 1080); // TODO - renderTexture should be window size; above 1080p this will cause rendering errors
|
renderTexture.create(1920, 1080); // TODO - renderTexture should be window size; above 1080p this will cause rendering errors
|
||||||
|
|
||||||
|
// Only initialize sprite if texture is available
|
||||||
|
if (ptex) {
|
||||||
sprite = ptex->sprite(0);
|
sprite = ptex->sprite(0);
|
||||||
|
}
|
||||||
|
|
||||||
output.setTextureRect(
|
output.setTextureRect(
|
||||||
sf::IntRect(0, 0,
|
sf::IntRect(0, 0,
|
||||||
|
@ -40,12 +49,17 @@ void UIGrid::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||||
sf::IntRect(0, 0,
|
sf::IntRect(0, 0,
|
||||||
box.getSize().x, box.getSize().y));
|
box.getSize().x, box.getSize().y));
|
||||||
renderTexture.clear(sf::Color(8, 8, 8, 255)); // TODO - UIGrid needs a "background color" field
|
renderTexture.clear(sf::Color(8, 8, 8, 255)); // TODO - UIGrid needs a "background color" field
|
||||||
// sprites that are visible according to zoom, center_x, center_y, and box width
|
|
||||||
float center_x_sq = center_x / ptex->sprite_width;
|
|
||||||
float center_y_sq = center_y / ptex->sprite_height;
|
|
||||||
|
|
||||||
float width_sq = box.getSize().x / (ptex->sprite_width * zoom);
|
// Get cell dimensions - use texture if available, otherwise defaults
|
||||||
float height_sq = box.getSize().y / (ptex->sprite_height * zoom);
|
int cell_width = ptex ? ptex->sprite_width : DEFAULT_CELL_WIDTH;
|
||||||
|
int cell_height = ptex ? ptex->sprite_height : DEFAULT_CELL_HEIGHT;
|
||||||
|
|
||||||
|
// sprites that are visible according to zoom, center_x, center_y, and box width
|
||||||
|
float center_x_sq = center_x / cell_width;
|
||||||
|
float center_y_sq = center_y / cell_height;
|
||||||
|
|
||||||
|
float width_sq = box.getSize().x / (cell_width * zoom);
|
||||||
|
float height_sq = box.getSize().y / (cell_height * zoom);
|
||||||
float left_edge = center_x_sq - (width_sq / 2.0);
|
float left_edge = center_x_sq - (width_sq / 2.0);
|
||||||
float top_edge = center_y_sq - (height_sq / 2.0);
|
float top_edge = center_y_sq - (height_sq / 2.0);
|
||||||
|
|
||||||
|
@ -54,7 +68,7 @@ void UIGrid::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||||
|
|
||||||
//sprite.setScale(sf::Vector2f(zoom, zoom));
|
//sprite.setScale(sf::Vector2f(zoom, zoom));
|
||||||
sf::RectangleShape r; // for colors and overlays
|
sf::RectangleShape r; // for colors and overlays
|
||||||
r.setSize(sf::Vector2f(ptex->sprite_width * zoom, ptex->sprite_height * zoom));
|
r.setSize(sf::Vector2f(cell_width * zoom, cell_height * zoom));
|
||||||
r.setOutlineThickness(0);
|
r.setOutlineThickness(0);
|
||||||
|
|
||||||
int x_limit = left_edge + width_sq + 2;
|
int x_limit = left_edge + width_sq + 2;
|
||||||
|
@ -74,8 +88,8 @@ void UIGrid::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||||
y+=1)
|
y+=1)
|
||||||
{
|
{
|
||||||
auto pixel_pos = sf::Vector2f(
|
auto pixel_pos = sf::Vector2f(
|
||||||
(x*ptex->sprite_width - left_spritepixels) * zoom,
|
(x*cell_width - left_spritepixels) * zoom,
|
||||||
(y*ptex->sprite_height - top_spritepixels) * zoom );
|
(y*cell_height - top_spritepixels) * zoom );
|
||||||
|
|
||||||
auto gridpoint = at(std::floor(x), std::floor(y));
|
auto gridpoint = at(std::floor(x), std::floor(y));
|
||||||
|
|
||||||
|
@ -85,10 +99,10 @@ void UIGrid::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||||
r.setFillColor(gridpoint.color);
|
r.setFillColor(gridpoint.color);
|
||||||
renderTexture.draw(r);
|
renderTexture.draw(r);
|
||||||
|
|
||||||
// tilesprite
|
// tilesprite - only draw if texture is available
|
||||||
// if discovered but not visible, set opacity to 90%
|
// if discovered but not visible, set opacity to 90%
|
||||||
// if not discovered... just don't draw it?
|
// if not discovered... just don't draw it?
|
||||||
if (gridpoint.tilesprite != -1) {
|
if (ptex && gridpoint.tilesprite != -1) {
|
||||||
sprite = ptex->sprite(gridpoint.tilesprite, pixel_pos, sf::Vector2f(zoom, zoom)); //setSprite(gridpoint.tilesprite);;
|
sprite = ptex->sprite(gridpoint.tilesprite, pixel_pos, sf::Vector2f(zoom, zoom)); //setSprite(gridpoint.tilesprite);;
|
||||||
renderTexture.draw(sprite);
|
renderTexture.draw(sprite);
|
||||||
}
|
}
|
||||||
|
@ -104,8 +118,8 @@ void UIGrid::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||||
//drawent.setScale(zoom, zoom);
|
//drawent.setScale(zoom, zoom);
|
||||||
drawent.setScale(sf::Vector2f(zoom, zoom));
|
drawent.setScale(sf::Vector2f(zoom, zoom));
|
||||||
auto pixel_pos = sf::Vector2f(
|
auto pixel_pos = sf::Vector2f(
|
||||||
(e->position.x*ptex->sprite_width - left_spritepixels) * zoom,
|
(e->position.x*cell_width - left_spritepixels) * zoom,
|
||||||
(e->position.y*ptex->sprite_height - top_spritepixels) * zoom );
|
(e->position.y*cell_height - top_spritepixels) * zoom );
|
||||||
//drawent.setPosition(pixel_pos);
|
//drawent.setPosition(pixel_pos);
|
||||||
//renderTexture.draw(drawent);
|
//renderTexture.draw(drawent);
|
||||||
drawent.render(pixel_pos, renderTexture);
|
drawent.render(pixel_pos, renderTexture);
|
||||||
|
@ -230,20 +244,24 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
||||||
// Convert PyObject texture to IndexTexture*
|
// Convert PyObject texture to IndexTexture*
|
||||||
// This requires the texture object to have been initialized similar to UISprite's texture handling
|
// This requires the texture object to have been initialized similar to UISprite's texture handling
|
||||||
|
|
||||||
|
std::shared_ptr<PyTexture> texture_ptr = nullptr;
|
||||||
|
|
||||||
|
// Allow None for texture
|
||||||
|
if (textureObj != Py_None) {
|
||||||
//if (!PyObject_IsInstance(textureObj, (PyObject*)&PyTextureType)) {
|
//if (!PyObject_IsInstance(textureObj, (PyObject*)&PyTextureType)) {
|
||||||
if (!PyObject_IsInstance(textureObj, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))) {
|
if (!PyObject_IsInstance(textureObj, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))) {
|
||||||
PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance");
|
PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance or None");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
PyTextureObject* pyTexture = reinterpret_cast<PyTextureObject*>(textureObj);
|
PyTextureObject* pyTexture = reinterpret_cast<PyTextureObject*>(textureObj);
|
||||||
// TODO (7DRL day 2, item 4.) use shared_ptr / PyTextureObject on UIGrid
|
texture_ptr = pyTexture->data;
|
||||||
//IndexTexture* texture = pyTexture->data.get();
|
}
|
||||||
|
|
||||||
// Initialize UIGrid
|
// Initialize UIGrid - texture_ptr will be nullptr if texture was None
|
||||||
//self->data = new UIGrid(grid_x, grid_y, texture, sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h));
|
//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,
|
//self->data = std::make_shared<UIGrid>(grid_x, grid_y, pyTexture->data,
|
||||||
// sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h));
|
// 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);
|
self->data = std::make_shared<UIGrid>(grid_x, grid_y, texture_ptr, pos_result->data, size_result->data);
|
||||||
return 0; // Success
|
return 0; // Success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,9 +383,16 @@ PyObject* UIGrid::get_texture(PyUIGridObject* self, void* closure) {
|
||||||
//return self->data->getTexture()->pyObject();
|
//return self->data->getTexture()->pyObject();
|
||||||
// PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPointState")
|
// PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPointState")
|
||||||
//PyTextureObject* obj = (PyTextureObject*)((&PyTextureType)->tp_alloc(&PyTextureType, 0));
|
//PyTextureObject* obj = (PyTextureObject*)((&PyTextureType)->tp_alloc(&PyTextureType, 0));
|
||||||
|
|
||||||
|
// Return None if no texture
|
||||||
|
auto texture = self->data->getTexture();
|
||||||
|
if (!texture) {
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture");
|
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture");
|
||||||
auto obj = (PyTextureObject*)type->tp_alloc(type, 0);
|
auto obj = (PyTextureObject*)type->tp_alloc(type, 0);
|
||||||
obj->data = self->data->getTexture();
|
obj->data = texture;
|
||||||
return (PyObject*)obj;
|
return (PyObject*)obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,7 +404,7 @@ PyObject* UIGrid::py_at(PyUIGridObject* self, PyObject* o)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (x < 0 || x >= self->data->grid_x) {
|
if (x < 0 || x >= self->data->grid_x) {
|
||||||
PyErr_SetString(PyExc_ValueError, "x value out of range (0, Grid.grid_y)");
|
PyErr_SetString(PyExc_ValueError, "x value out of range (0, Grid.grid_x)");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (y < 0 || y >= self->data->grid_y) {
|
if (y < 0 || y >= self->data->grid_y) {
|
||||||
|
|
|
@ -21,6 +21,9 @@ class UIGrid: public UIDrawable
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<PyTexture> ptex;
|
std::shared_ptr<PyTexture> ptex;
|
||||||
|
// Default cell dimensions when no texture is provided
|
||||||
|
static constexpr int DEFAULT_CELL_WIDTH = 16;
|
||||||
|
static constexpr int DEFAULT_CELL_HEIGHT = 16;
|
||||||
public:
|
public:
|
||||||
UIGrid();
|
UIGrid();
|
||||||
//UIGrid(int, int, IndexTexture*, float, float, float, float);
|
//UIGrid(int, int, IndexTexture*, float, float, float, float);
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Test Grid creation with None texture - should work with color cells only"""
|
||||||
|
import mcrfpy
|
||||||
|
from mcrfpy import automation
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def test_grid_none_texture(runtime):
|
||||||
|
"""Test Grid functionality without texture"""
|
||||||
|
print("\n=== Testing Grid with None texture ===")
|
||||||
|
|
||||||
|
# Test 1: Create Grid with None texture
|
||||||
|
try:
|
||||||
|
grid = mcrfpy.Grid(10, 10, None, mcrfpy.Vector(50, 50), mcrfpy.Vector(400, 400))
|
||||||
|
print("✓ Grid created successfully with None texture")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ Failed to create Grid with None texture: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Add to UI
|
||||||
|
ui = mcrfpy.sceneUI("grid_none_test")
|
||||||
|
ui.append(grid)
|
||||||
|
|
||||||
|
# Test 2: Verify grid properties
|
||||||
|
try:
|
||||||
|
grid_size = grid.grid_size
|
||||||
|
print(f"✓ Grid size: {grid_size}")
|
||||||
|
|
||||||
|
# Check texture property
|
||||||
|
texture = grid.texture
|
||||||
|
if texture is None:
|
||||||
|
print("✓ Grid texture is None as expected")
|
||||||
|
else:
|
||||||
|
print(f"✗ Grid texture should be None, got: {texture}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ Property access failed: {e}")
|
||||||
|
|
||||||
|
# Test 3: Access grid points and set colors
|
||||||
|
try:
|
||||||
|
# Create a checkerboard pattern with colors
|
||||||
|
for x in range(10):
|
||||||
|
for y in range(10):
|
||||||
|
point = grid.at(x, y)
|
||||||
|
if (x + y) % 2 == 0:
|
||||||
|
point.color = mcrfpy.Color(255, 0, 0, 255) # Red
|
||||||
|
else:
|
||||||
|
point.color = mcrfpy.Color(0, 0, 255, 255) # Blue
|
||||||
|
print("✓ Successfully set grid point colors")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ Failed to set grid colors: {e}")
|
||||||
|
|
||||||
|
# Test 4: Add entities to the grid
|
||||||
|
try:
|
||||||
|
# Create an entity with its own texture
|
||||||
|
entity_texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||||
|
entity = mcrfpy.Entity(mcrfpy.Vector(5, 5), entity_texture, 1, grid)
|
||||||
|
grid.entities.append(entity)
|
||||||
|
print(f"✓ Added entity to grid, total entities: {len(grid.entities)}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ Failed to add entity: {e}")
|
||||||
|
|
||||||
|
# Test 5: Test grid interaction properties
|
||||||
|
try:
|
||||||
|
# Test zoom
|
||||||
|
grid.zoom = 2.0
|
||||||
|
print(f"✓ Set zoom to: {grid.zoom}")
|
||||||
|
|
||||||
|
# Test center
|
||||||
|
grid.center = mcrfpy.Vector(5, 5)
|
||||||
|
print(f"✓ Set center to: {grid.center}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ Grid properties failed: {e}")
|
||||||
|
|
||||||
|
# Take screenshot
|
||||||
|
filename = f"grid_none_texture_test_{int(runtime)}.png"
|
||||||
|
result = automation.screenshot(filename)
|
||||||
|
print(f"\nScreenshot saved: {filename} - Result: {result}")
|
||||||
|
print("The grid should show a red/blue checkerboard pattern")
|
||||||
|
|
||||||
|
print("\n✓ PASS - Grid works correctly without texture!")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Set up test scene
|
||||||
|
print("Creating test scene...")
|
||||||
|
mcrfpy.createScene("grid_none_test")
|
||||||
|
mcrfpy.setScene("grid_none_test")
|
||||||
|
|
||||||
|
# Add a background frame so we can see the grid
|
||||||
|
ui = mcrfpy.sceneUI("grid_none_test")
|
||||||
|
background = mcrfpy.Frame(0, 0, 800, 600,
|
||||||
|
fill_color=mcrfpy.Color(200, 200, 200),
|
||||||
|
outline_color=mcrfpy.Color(0, 0, 0),
|
||||||
|
outline=2.0)
|
||||||
|
ui.append(background)
|
||||||
|
|
||||||
|
# Schedule test
|
||||||
|
mcrfpy.setTimer("test", test_grid_none_texture, 100)
|
||||||
|
print("Test scheduled...")
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Test Grid creation with null/None texture to reproduce segfault"""
|
||||||
|
import mcrfpy
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def test_grid_null_texture():
|
||||||
|
"""Test if Grid can be created without a texture"""
|
||||||
|
print("=== Testing Grid with null texture ===")
|
||||||
|
|
||||||
|
# Create test scene
|
||||||
|
mcrfpy.createScene("grid_null_test")
|
||||||
|
mcrfpy.setScene("grid_null_test")
|
||||||
|
ui = mcrfpy.sceneUI("grid_null_test")
|
||||||
|
|
||||||
|
# Test 1: Try with None
|
||||||
|
try:
|
||||||
|
print("Test 1: Creating Grid with None texture...")
|
||||||
|
grid = mcrfpy.Grid(10, 10, None, mcrfpy.Vector(0, 0), mcrfpy.Vector(400, 400))
|
||||||
|
print("✗ Should have raised exception for None texture")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✓ Correctly rejected None texture: {e}")
|
||||||
|
|
||||||
|
# Test 2: Try without texture parameter (if possible)
|
||||||
|
try:
|
||||||
|
print("\nTest 2: Creating Grid with missing parameters...")
|
||||||
|
grid = mcrfpy.Grid(10, 10)
|
||||||
|
print("✗ Should have raised exception for missing parameters")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✓ Correctly rejected missing parameters: {e}")
|
||||||
|
|
||||||
|
print("\nTest complete - Grid requires texture parameter")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Run immediately
|
||||||
|
test_grid_null_texture()
|
Loading…
Reference in New Issue