Compare commits

...

10 Commits

Author SHA1 Message Date
John McCardle 232105a893 Squashed commit of the following: [reprs_and_member_names]
Closes #22
Closes #23
Closes #24
Closes #25
Closes #31
Closes #56

commit 43fac8f4f3
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Apr 20 18:32:52 2024 -0400

    Typo in UIFrame repr

commit 3fd5ad93e2
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Apr 20 18:32:30 2024 -0400

    Add UIGridPoint and UIGridPointState repr

commit 03376897b8
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Apr 20 18:32:17 2024 -0400

    Add UIGrid repr

commit 48af072a33
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Apr 20 18:32:05 2024 -0400

    Add UIEntity repr
2024-04-20 18:33:18 -04:00
John McCardle c2de9b08d6 Refactor: remove "s" prefix from "sRender" method ( -> "render") Closes #44 2024-04-20 14:16:14 -04:00
John McCardle a465a9861d Color parsing for UICaption __init__ - closes #58 2024-04-20 13:37:19 -04:00
John McCardle ac7f7052cd Squashed commit of the following: [break_up_ui_h]
Closes #43

No segfault found in cos_play after completing the checklist. Maybe I accidentally fixed it...?

commit 6aa151aba3
Author: John McCardle <mccardle.john@gmail.com>
Date:   Fri Apr 19 21:43:58 2024 -0400

    UISprite.h/.cpp cleanup

commit ec0374ef50
Author: John McCardle <mccardle.john@gmail.com>
Date:   Fri Apr 19 21:37:39 2024 -0400

    UIGridPoint.h/.cpp reorganization

commit 2cb7339535
Author: John McCardle <mccardle.john@gmail.com>
Date:   Fri Apr 19 21:19:25 2024 -0400

    UIGrid.h/.cpp cleanup. I have reservations about the UIEntityCollection[Iter] classes + methods living there, but not enough to fix it right now.

commit 5d6af324bf
Author: John McCardle <mccardle.john@gmail.com>
Date:   Thu Apr 18 22:14:57 2024 -0400

    UIFrame - moving static method into class namespace; no type object access

commit 567218cd7b
Author: John McCardle <mccardle.john@gmail.com>
Date:   Thu Apr 18 21:23:49 2024 -0400

    UIEntity fixes for the UI.h split: There are segfaults in cos_play, I may have missed a type usage or something

commit 76693acd28
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Apr 13 00:18:37 2024 -0400

    delete leftover comments

commit 9efe998a33
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Apr 13 00:17:43 2024 -0400

    some work on UICaption and UICollection; fixing segfaults resulting from mcrfpydef namepace TypeObject usage

commit 714965da45
Author: John McCardle <mccardle.john@gmail.com>
Date:   Fri Apr 12 14:15:00 2024 -0400

    eliminate extra includes on UICaption

commit 8efa25878f
Author: John McCardle <mccardle.john@gmail.com>
Date:   Wed Apr 10 23:41:14 2024 -0400

    remove a lot of stuff

commit c186d8c7f3
Author: John McCardle <mccardle.john@gmail.com>
Date:   Wed Apr 10 23:10:15 2024 -0400

    We are compiling again! Started refactoring UICaption to be more idiomatic

commit 1b6e2a709b
Author: John McCardle <mccardle.john@gmail.com>
Date:   Tue Apr 9 22:42:02 2024 -0400

    Still not quite compiling; as predicted, a lot of interdependency and definition order bugs to untangle

commit aa7553a818
Author: John McCardle <mccardle.john@gmail.com>
Date:   Tue Apr 9 22:41:20 2024 -0400

    PyTexture clean up scribbles and experiments

commit c0201d989a
Author: John McCardle <mccardle.john@gmail.com>
Date:   Mon Apr 8 22:55:00 2024 -0400

    additional unsaved changes

commit 83a63a3093
Author: John McCardle <mccardle.john@gmail.com>
Date:   Mon Apr 8 22:45:00 2024 -0400

    doesn't compile, but UI.h/.cpp code has been divvy'd up.

    refs #43 @2h
2024-04-20 10:32:04 -04:00
John McCardle 1a7186f745 Squashed commit of the following: [standardize_font_handling]
closes #60, closes #5, closes #68

The major functionality added here was proper use of types in the module, by importing after finalization.

commit 5009fa0fb9
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sun Apr 7 22:44:15 2024 -0400

    PyFont - use the new standard method for instancing

commit a19781b56a
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sun Apr 7 15:21:17 2024 -0400

    Many hours of pain & research behind this small commit. Safe object building by not messing with types before interpreter is fully initialized

commit 159658521c
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sun Mar 31 21:41:45 2024 -0400

    Font mostly working, just a few weird bugs with the types of the default items added to the module
2024-04-07 22:51:31 -04:00
John McCardle fbf263a038 Squashed commit of the following: [standardize_vector_handling]
closes #13

Deferring class standardization for the UI.h overhaul.

commit 5edebdd643
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sun Mar 31 14:21:07 2024 -0400

    PyVector init should be pretty reliable now

commit c13e185289
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sun Mar 31 13:51:29 2024 -0400

    PyColor fix - Init corrections

commit 8871f6be6e
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Mar 30 22:51:55 2024 -0400

    Parse arguments: no args & Vector object args work, tuples and bare numerics still do not

commit 1c12e8719c
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Mar 30 22:32:28 2024 -0400

    Not bad for a quick first salvo, but I cannot figure out why init isn't cooperating.
2024-03-31 18:00:19 -04:00
John McCardle f82508b753 Squashed commit of the following: [standardize_color_handling]
closes #11

Check the abandoned feature branch for PyLinkedColor, a time-expensive but now abandoned feature to link a color value to a UIDrawable.

There are some TODOs left in the PyColor class, but that can go under cleanup. I'm way over time on this, so I'm taking a small victory :)

commit 572aa52605
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Mar 30 21:18:26 2024 -0400

    More color table updates

commit 01706bd59d
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Mar 30 21:13:31 2024 -0400

    Color wrapup... Cutting PyLinkedColor to simplify my cursedly mortal, finite existence

commit 3991ac13d6
Author: John McCardle <mccardle.john@gmail.com>
Date:   Thu Mar 28 23:50:50 2024 -0400

    Still having segfaults with LinkedColor and captions (specifically outline color, but that might not be the actual cause). PyColor shaping back up in simplified form.

commit 06e24a1b27
Author: John McCardle <mccardle.john@gmail.com>
Date:   Thu Mar 28 20:53:49 2024 -0400

    LinkedColor now reflecting changes to the linked color value. Needs set method + RGBA / color properties

commit 41509dfe96
Author: John McCardle <mccardle.john@gmail.com>
Date:   Wed Mar 27 21:10:03 2024 -0400

    Addressing issues with PyColor by splitting behavior off into PyLinkedColor

commit 13a4ddf41b
Author: John McCardle <mccardle.john@gmail.com>
Date:   Tue Mar 26 23:02:00 2024 -0400

    Build runs again. PyColor objects are being instantiated, with bugs and no test of color changing

commit 1601fc7fab
Author: John McCardle <mccardle.john@gmail.com>
Date:   Mon Mar 25 20:48:08 2024 -0400

    Still doesn't compile, but now the issue is in UI.h overcoupling. Progress!

commit 13672c8fdb
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sun Mar 24 21:19:37 2024 -0400

    Dabbling around this morning; still not building

commit 79090b553f
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sun Mar 24 08:36:06 2024 -0400

    Unsaved changes from last night

commit 2cac6f03c6
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Mar 23 23:07:10 2024 -0400

    untested PyColor base implementation

commit 3728e5fcc8
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Mar 23 23:06:36 2024 -0400

    Color naming prototype
2024-03-30 21:20:40 -04:00
John McCardle 4ffe438d1b Squashed commit of the following: [standardize_texture_handling]
closes #18

commit b114ec3085
Author: John McCardle <mccardle.john@gmail.com>
Date:   Thu Mar 21 22:22:35 2024 -0400

    cleaning up for merge

commit d7228172c4
Author: John McCardle <mccardle.john@gmail.com>
Date:   Thu Mar 21 21:39:15 2024 -0400

    Messy, but monumental: PyTexture::pyObject works

    this also coincidentally fixes a weird bug I encountered while
    (mis?)using tp_alloc: by using PyType_GenericAlloc, I avoid the segfault
    that tp_alloc sometimes causes. See the horrible UIDrawable retrieval
    macro that I use in UI.h for a workaround that can probably be replaced
    with this technique

commit 2cf8f94310
Author: John McCardle <mccardle.john@gmail.com>
Date:   Wed Mar 20 21:16:52 2024 -0400

    Radical new example pattern for exposing a C++ class to Python

commit 84a8886da2
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sun Mar 17 16:29:33 2024 -0400

    Fixed render issue with UIGrid / PyTexture: wasn't positioning or scaling properly after fetching sprite

commit 20f80c4114
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sun Mar 17 16:23:52 2024 -0400

    Fixed sprite indexing error in PyTexture; needs non-square sprite tests, but feeling confident!

commit afd4ff1925
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Mar 16 21:53:24 2024 -0400

    good progress, we're building again. Issue with Grid (tile sprite) textures and I think the sprite indexes are being calculated wrong (x and y transposed?)

commit bfd33102d1
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Mar 16 14:52:35 2024 -0400

    Squashed basically all the compile bugs in UISprite, but UIEntity and UIGrid use textures as well, so they need to be fixed too before the project will build again

commit 47d0e34a17
Author: John McCardle <mccardle.john@gmail.com>
Date:   Sat Mar 16 11:31:39 2024 -0400

    Initial PyTexture class

    no testing done.
    should enable rectangular (non-square) textures

    "sprite" method; let's just overwrite sprites with texture coords
    Hoping to replace awful code like:
    `self->data->sprite.sprite.setTextureRect(self->data->sprite.itex->spriteCoordinates(val));`

    with something like:
    `self->data->sprite = self->data->texture->sprite(val);`
2024-03-21 22:24:42 -04:00
John McCardle cdaf309272 Squashed commit of the following: [raii_pyobjects]
closes #4

commit 8f060dc87b
Author: John McCardle <mccardle.john@gmail.com>
Date:   Fri Mar 15 22:20:03 2024 -0400

    Removing std::cout debugging statements

commit c9d5251c71
Author: John McCardle <mccardle.john@gmail.com>
Date:   Fri Mar 15 20:00:57 2024 -0400

    In-place map modification worked

commit 0a8f67e391
Author: John McCardle <mccardle.john@gmail.com>
Date:   Thu Mar 14 23:13:13 2024 -0400

    Stress test is failing: By removing a timer to a function (from inside that function?) I can immediately cause a segfault.

commit 05d9f6a882
Author: John McCardle <mccardle.john@gmail.com>
Date:   Tue Mar 12 22:27:12 2024 -0400

    wow, good test of Key and Click Callable classes. Cleanup, squash, and merge after I give it a lookover in daylight, though.

commit 972768eb26
Author: John McCardle <mccardle.john@gmail.com>
Date:   Tue Mar 12 21:02:48 2024 -0400

    inital PyCallable work; isolate very well behaved usage of PyObject references behind RAII
2024-03-15 22:20:37 -04:00
John McCardle 2c3c1449ee chore: changing comments to reflect TODOs conversion to issues 2024-03-12 21:03:39 -04:00
46 changed files with 5433 additions and 3138 deletions

157
css_colors.txt Normal file
View File

@ -0,0 +1,157 @@
aqua #00FFFF
black #000000
blue #0000FF
fuchsia #FF00FF
gray #808080
green #008000
lime #00FF00
maroon #800000
navy #000080
olive #808000
purple #800080
red #FF0000
silver #C0C0C0
teal #008080
white #FFFFFF
yellow #FFFF00
aliceblue #F0F8FF
antiquewhite #FAEBD7
aqua #00FFFF
aquamarine #7FFFD4
azure #F0FFFF
beige #F5F5DC
bisque #FFE4C4
black #000000
blanchedalmond #FFEBCD
blue #0000FF
blueviolet #8A2BE2
brown #A52A2A
burlywood #DEB887
cadetblue #5F9EA0
chartreuse #7FFF00
chocolate #D2691E
coral #FF7F50
cornflowerblue #6495ED
cornsilk #FFF8DC
crimson #DC143C
cyan #00FFFF
darkblue #00008B
darkcyan #008B8B
darkgoldenrod #B8860B
darkgray #A9A9A9
darkgreen #006400
darkkhaki #BDB76B
darkmagenta #8B008B
darkolivegreen #556B2F
darkorange #FF8C00
darkorchid #9932CC
darkred #8B0000
darksalmon #E9967A
darkseagreen #8FBC8F
darkslateblue #483D8B
darkslategray #2F4F4F
darkturquoise #00CED1
darkviolet #9400D3
deeppink #FF1493
deepskyblue #00BFFF
dimgray #696969
dodgerblue #1E90FF
firebrick #B22222
floralwhite #FFFAF0
forestgreen #228B22
fuchsia #FF00FF
gainsboro #DCDCDC
ghostwhite #F8F8FF
gold #FFD700
goldenrod #DAA520
gray #7F7F7F
green #008000
greenyellow #ADFF2F
honeydew #F0FFF0
hotpink #FF69B4
indianred #CD5C5C
indigo #4B0082
ivory #FFFFF0
khaki #F0E68C
lavender #E6E6FA
lavenderblush #FFF0F5
lawngreen #7CFC00
lemonchiffon #FFFACD
lightblue #ADD8E6
lightcoral #F08080
lightcyan #E0FFFF
lightgoldenrodyellow #FAFAD2
lightgreen #90EE90
lightgrey #D3D3D3
lightpink #FFB6C1
lightsalmon #FFA07A
lightseagreen #20B2AA
lightskyblue #87CEFA
lightslategray #778899
lightsteelblue #B0C4DE
lightyellow #FFFFE0
lime #00FF00
limegreen #32CD32
linen #FAF0E6
magenta #FF00FF
maroon #800000
mediumaquamarine #66CDAA
mediumblue #0000CD
mediumorchid #BA55D3
mediumpurple #9370DB
mediumseagreen #3CB371
mediumslateblue #7B68EE
mediumspringgreen #00FA9A
mediumturquoise #48D1CC
mediumvioletred #C71585
midnightblue #191970
mintcream #F5FFFA
mistyrose #FFE4E1
moccasin #FFE4B5
navajowhite #FFDEAD
navy #000080
navyblue #9FAFDF
oldlace #FDF5E6
olive #808000
olivedrab #6B8E23
orange #FFA500
orangered #FF4500
orchid #DA70D6
palegoldenrod #EEE8AA
palegreen #98FB98
paleturquoise #AFEEEE
palevioletred #DB7093
papayawhip #FFEFD5
peachpuff #FFDAB9
peru #CD853F
pink #FFC0CB
plum #DDA0DD
powderblue #B0E0E6
purple #800080
red #FF0000
rosybrown #BC8F8F
royalblue #4169E1
saddlebrown #8B4513
salmon #FA8072
sandybrown #FA8072
seagreen #2E8B57
seashell #FFF5EE
sienna #A0522D
silver #C0C0C0
skyblue #87CEEB
slateblue #6A5ACD
slategray #708090
snow #FFFAFA
springgreen #00FF7F
steelblue #4682B4
tan #D2B48C
teal #008080
thistle #D8BFD8
tomato #FF6347
turquoise #40E0D0
violet #EE82EE
wheat #F5DEB3
white #FFFFFF
whitesmoke #F5F5F5
yellow #FFFF00
yellowgreen #9ACD32

111
generate_color_table.py Normal file
View File

@ -0,0 +1,111 @@
# data sources: CSS docs, jennyscrayoncollection 2017 article on Crayola colors, XKCD color survey
# target: Single C++ header file to provide a struct of color RGB codes and names.
# This file pre-computes the nearest neighbor of every color.
# if an RGB code being searched for is closer than the nearest neighbor, it's the closest color name.
def hex_to_rgb(txt):
if '#' in txt: txt = txt.replace('#', '')
r = txt[0:2]
g = txt[2:4]
b = txt[4:6]
return tuple([int(s, 16) for s in (r,g,b)])
class palette:
def __init__(self, name, filename, priority):
self.name = name
self.priority = priority
with open(filename, "r") as f:
print(f"scanning {filename}")
self.colors = {}
for line in f.read().split('\n'):
if len(line.split('\t')) < 2: continue
name, code = line.split('\t')
#print(name, code)
self.colors[name] = hex_to_rgb(code)
def __repr__(self):
return f"<Palette '{self.name}' - {len(self.colors)} colors, priority = {self.priority}>"
palettes = [
#palette("jenny", "jenny_colors.txt", 3), # I should probably use wikipedia as a source for copyright reasons
palette("crayon", "wikicrayons_colors.txt", 2),
palette("xkcd", "xkcd_colors.txt", 1),
palette("css", "css_colors.txt", 0),
#palette("matplotlib", "matplotlib_colors.txt", 2) # there's like 10 colors total, I think we'll survive without them
]
all_colors = []
from math import sqrt
def rgbdist(c1, c2):
return sqrt((c1.r - c2.r)**2 + (c1.g - c2.g)**2 + (c1.b - c2.b)**2)
class Color:
def __init__(self, r, g, b, name, prefix, priority):
self.r = r
self.g = g
self.b = b
self.name = name
self.prefix = prefix
self.priority = priority
self.nearest_neighbor = None
def __repr__(self):
return f"<Color ({self.r}, {self.g}, {self.b}) - '{self.prefix}:{self.name}', priority = {self.priority}, nearest_neighbor={self.nearest_neighbor.name if self.nearest_neighbor is not None else None}>"
def nn(self, colors):
nearest = None
nearest_dist = 999999
for c in colors:
dist = rgbdist(self, c)
if dist == 0: continue
if dist < nearest_dist:
nearest = c
nearest_dist = dist
self.nearest_neighbor = nearest
for p in palettes:
prefix = p.name
priority = p.priority
for name, rgb in p.colors.items():
all_colors.append(Color(*rgb, name, prefix, priority))
print(f"{prefix}->{len(all_colors)}")
for c in all_colors:
c.nn(all_colors)
smallest_dist = 9999999999999
largest_dist = 0
for c in all_colors:
dist = rgbdist(c, c.nearest_neighbor)
if dist > largest_dist: largest_dist = dist
if dist < smallest_dist: smallest_dist = dist
#print(f"{c.prefix}:{c.name} -> {c.nearest_neighbor.prefix}:{c.nearest_neighbor.name}\t{rgbdist(c, c.nearest_neighbor):.2f}")
# questions -
# are there any colors where their nearest neighbor's nearest neighbor isn't them? (There should be)
nonnear_pairs = 0
for c in all_colors:
neighbor = c.nearest_neighbor
their_neighbor = neighbor.nearest_neighbor
if c is not their_neighbor:
#print(f"{c.prefix}:{c.name} -> {neighbor.prefix}:{neighbor.name} -> {their_neighbor.prefix}:{their_neighbor.name}")
nonnear_pairs += 1
print("Non-near pairs:", nonnear_pairs)
#print(f"{c.prefix}:{c.name} -> {c.nearest_neighbor.prefix}:{c.nearest_neighbor.name}\t{rgbdist(c, c.nearest_neighbor):.2f}")
# Are there duplicates? They should be removed from the palette that won't be used
dupes = 0
for c in all_colors:
for c2 in all_colors:
if c is c2: continue
if c.r == c2.r and c.g == c2.g and c.b == c2.b:
dupes += 1
print("dupes:", dupes, "this many will need to be removed:", dupes / 2)
# What order to put them in? Do we want large radiuses first, or some sort of "common color" table?
# does manhattan distance change any answers over the 16.7M RGB values?
# What's the worst case lookup? (Checking all 1200 colors to find the name?)

View File

@ -59,7 +59,7 @@ void GameEngine::run()
if (!paused) if (!paused)
{ {
} }
currentScene()->sRender(); currentScene()->render();
currentFrame++; currentFrame++;
frameTime = clock.restart().asSeconds(); frameTime = clock.restart().asSeconds();
fps = 1 / frameTime; fps = 1 / frameTime;
@ -69,14 +69,14 @@ void GameEngine::run()
void GameEngine::manageTimer(std::string name, PyObject* target, int interval) void GameEngine::manageTimer(std::string name, PyObject* target, int interval)
{ {
//std::cout << "Manage timer called. " << name << " " << interval << std::endl;
auto it = timers.find(name); auto it = timers.find(name);
if (it != timers.end()) // overwrite existing if (it != timers.end()) // overwrite existing
{ {
if (target == NULL || target == Py_None) // delete if (target == NULL || target == Py_None)
{ {
Py_DECREF(timers[name].target); // Delete: Overwrite existing timer with one that calls None. This will be deleted in the next timer check
timers.erase(it); // see gitea issue #4: this allows for a timer to be deleted during its own call to itself
timers[name] = std::make_shared<PyTimerCallable>(Py_None, 1000, runtime.getElapsedTime().asMilliseconds());
return; return;
} }
} }
@ -85,16 +85,23 @@ void GameEngine::manageTimer(std::string name, PyObject* target, int interval)
std::cout << "Refusing to initialize timer to None. It's not an error, it's just pointless." << std::endl; std::cout << "Refusing to initialize timer to None. It's not an error, it's just pointless." << std::endl;
return; return;
} }
timers[name] = Timer(target, interval, runtime.getElapsedTime().asMilliseconds()); timers[name] = std::make_shared<PyTimerCallable>(target, interval, runtime.getElapsedTime().asMilliseconds());
Py_INCREF(target);
} }
void GameEngine::testTimers() void GameEngine::testTimers()
{ {
int now = runtime.getElapsedTime().asMilliseconds(); int now = runtime.getElapsedTime().asMilliseconds();
for (auto& [name, timer]: timers) auto it = timers.begin();
while (it != timers.end())
{ {
timer.test(now); it->second->test(now);
if (it->second->isNone())
{
it = timers.erase(it);
}
else
it++;
} }
} }
@ -157,8 +164,10 @@ void GameEngine::sUserInput()
std::string name = currentScene()->action(actionCode); std::string name = currentScene()->action(actionCode);
currentScene()->doAction(name, actionType); currentScene()->doAction(name, actionType);
} }
else if (currentScene()->key_callable != NULL && currentScene()->key_callable != Py_None) else if (currentScene()->key_callable)
{ {
currentScene()->key_callable->call(ActionCode::key_str(event.key.code), actionType);
/*
PyObject* args = Py_BuildValue("(ss)", ActionCode::key_str(event.key.code).c_str(), actionType.c_str()); PyObject* args = Py_BuildValue("(ss)", ActionCode::key_str(event.key.code).c_str(), actionType.c_str());
PyObject* retval = PyObject_Call(currentScene()->key_callable, args, NULL); PyObject* retval = PyObject_Call(currentScene()->key_callable, args, NULL);
if (!retval) if (!retval)
@ -170,6 +179,7 @@ void GameEngine::sUserInput()
{ {
std::cout << "key_callable returned a non-None value. It's not an error, it's just not being saved or used." << std::endl; std::cout << "key_callable returned a non-None value. It's not an error, it's just not being saved or used." << std::endl;
} }
*/
} }
else else
{ {

View File

@ -5,6 +5,7 @@
#include "McRFPy_API.h" #include "McRFPy_API.h"
#include "IndexTexture.h" #include "IndexTexture.h"
#include "Timer.h" #include "Timer.h"
#include "PyCallable.h"
class GameEngine class GameEngine
{ {
@ -20,7 +21,8 @@ class GameEngine
std::string window_title; std::string window_title;
sf::Clock runtime; sf::Clock runtime;
std::map<std::string, Timer> timers; //std::map<std::string, Timer> timers;
std::map<std::string, std::shared_ptr<PyTimerCallable>> timers;
void testTimers(); void testTimers();
public: public:

View File

@ -9,6 +9,11 @@ std::vector<sf::SoundBuffer> McRFPy_API::soundbuffers;
sf::Music McRFPy_API::music; sf::Music McRFPy_API::music;
sf::Sound McRFPy_API::sfx; sf::Sound McRFPy_API::sfx;
std::shared_ptr<PyFont> McRFPy_API::default_font;
std::shared_ptr<PyTexture> McRFPy_API::default_texture;
PyObject* McRFPy_API::mcrf_module;
static PyMethodDef mcrfpyMethods[] = { static PyMethodDef mcrfpyMethods[] = {
{"registerPyAction", McRFPy_API::_registerPyAction, METH_VARARGS, {"registerPyAction", McRFPy_API::_registerPyAction, METH_VARARGS,
"Register a callable Python object to correspond to an action string. (actionstr, callable)"}, "Register a callable Python object to correspond to an action string. (actionstr, callable)"},
@ -39,8 +44,15 @@ static PyMethodDef mcrfpyMethods[] = {
}; };
static PyModuleDef mcrfpyModule = { static PyModuleDef mcrfpyModule = {
PyModuleDef_HEAD_INIT, "mcrfpy", NULL, -1, mcrfpyMethods, PyModuleDef_HEAD_INIT, /* m_base - Always initialize this member to PyModuleDef_HEAD_INIT. */
NULL, NULL, NULL, NULL "mcrfpy", /* m_name */
NULL, /* m_doc - Docstring for the module; usually a docstring variable created with PyDoc_STRVAR is used. */
-1, /* m_size - Setting m_size to -1 means that the module does not support sub-interpreters, because it has global state. */
mcrfpyMethods, /* m_methods */
NULL, /* m_slots - An array of slot definitions ... When using single-phase initialization, m_slots must be NULL. */
NULL, /* traverseproc m_traverse - A traversal function to call during GC traversal of the module object */
NULL, /* inquiry m_clear - A clear function to call during GC clearing of the module object */
NULL /* freefunc m_free - A function to call during deallocation of the module object */
}; };
// Module initializer fn, passed to PyImport_AppendInittab // Module initializer fn, passed to PyImport_AppendInittab
@ -53,34 +65,38 @@ PyObject* PyInit_mcrfpy()
return NULL; return NULL;
} }
// This code runs, but Python segfaults when accessing the UIFrame type. using namespace mcrfpydef;
//std::cout << "Adding UIFrame object to module\n"; PyTypeObject* pytypes[] = {
PyModule_AddType(m, &mcrfpydef::PyColorType); /*SFML exposed types*/
PyModule_AddType(m, &mcrfpydef::PyFontType); &PyColorType, /*&PyLinkedColorType,*/ &PyFontType, &PyTextureType, &PyVectorType,
PyModule_AddType(m, &mcrfpydef::PyUICaptionType);
PyModule_AddType(m, &mcrfpydef::PyTextureType);
PyModule_AddType(m, &mcrfpydef::PyUISpriteType);
if (PyModule_AddType(m, &mcrfpydef::PyUIFrameType) < 0) /*UI widgets*/
&PyUICaptionType, &PyUISpriteType, &PyUIFrameType, &PyUIEntityType, &PyUIGridType,
/*game map & perspective data*/
&PyUIGridPointType, &PyUIGridPointStateType,
/*collections & iterators*/
&PyUICollectionType, &PyUICollectionIterType,
&PyUIEntityCollectionType, &PyUIEntityCollectionIterType,
nullptr};
int i = 0;
auto t = pytypes[i];
while (t != nullptr)
{ {
std::cout << "Error adding UIFrame type to module; aborting" << std::endl; /*std::cout << */ PyType_Ready(t); /*<< std::endl; */
Py_DECREF(&mcrfpydef::PyUIFrameType); PyModule_AddType(m, t);
return NULL; t = pytypes[i++];
} }
PyModule_AddType(m, &mcrfpydef::PyUICollectionType);
PyModule_AddType(m, &mcrfpydef::PyUICollectionIterType);
PyModule_AddType(m, &mcrfpydef::PyUIGridPointType);
PyModule_AddType(m, &mcrfpydef::PyUIGridPointStateType);
PyModule_AddType(m, &mcrfpydef::PyUIEntityType);
PyModule_AddType(m, &mcrfpydef::PyUIEntityCollectionIterType);
PyModule_AddType(m, &mcrfpydef::PyUIEntityCollectionType);
PyModule_AddType(m, &mcrfpydef::PyUIGridType);
// Add default_font and default_texture to module
McRFPy_API::default_font = std::make_shared<PyFont>("assets/JetbrainsMono.ttf");
McRFPy_API::default_texture = std::make_shared<PyTexture>("assets/kenney_tinydungeon.png", 16, 16);
//PyModule_AddObject(m, "default_font", McRFPy_API::default_font->pyObject());
//PyModule_AddObject(m, "default_texture", McRFPy_API::default_texture->pyObject());
PyModule_AddObject(m, "default_font", Py_None);
PyModule_AddObject(m, "default_texture", Py_None);
//McRFPy_API::mcrf_module = m;
return m; return m;
} }
@ -132,9 +148,13 @@ PyStatus init_python(const char *program_name)
#endif #endif
status = Py_InitializeFromConfig(&config); status = Py_InitializeFromConfig(&config);
PyConfig_Clear(&config);
return status; return status;
} }
/*
void McRFPy_API::setSpriteTexture(int ti) void McRFPy_API::setSpriteTexture(int ti)
{ {
int tx = ti % texture_width, ty = ti / texture_width; int tx = ti % texture_width, ty = ti / texture_width;
@ -143,6 +163,7 @@ void McRFPy_API::setSpriteTexture(int ti)
ty * texture_size, ty * texture_size,
texture_size, texture_size)); texture_size, texture_size));
} }
*/
// functionality // functionality
//void McRFPy_API:: //void McRFPy_API::
@ -159,6 +180,15 @@ void McRFPy_API::api_init() {
//texture_sprite_count = texture_width * texture_height; //texture_sprite_count = texture_width * texture_height;
//texture.setSmooth(false); //texture.setSmooth(false);
// Add default_font and default_texture to module
McRFPy_API::mcrf_module = PyImport_ImportModule("mcrfpy");
std::cout << PyUnicode_AsUTF8(PyObject_Repr(McRFPy_API::mcrf_module)) << std::endl;
//PyModule_AddObject(McRFPy_API::mcrf_module, "default_font", McRFPy_API::default_font->pyObject());
PyObject_SetAttrString(McRFPy_API::mcrf_module, "default_font", McRFPy_API::default_font->pyObject());
//PyModule_AddObject(McRFPy_API::mcrf_module, "default_texture", McRFPy_API::default_texture->pyObject());
PyObject_SetAttrString(McRFPy_API::mcrf_module, "default_texture", McRFPy_API::default_texture->pyObject());
//sprite.setTexture(texture); //sprite.setTexture(texture);
//sprite.setScale(sf::Vector2f(4.0f, 4.0f)); //sprite.setScale(sf::Vector2f(4.0f, 4.0f));
//setSpriteTexture(0); //setSpriteTexture(0);
@ -453,6 +483,7 @@ PyObject* McRFPy_API::_createScene(PyObject* self, PyObject* args) {
PyObject* McRFPy_API::_keypressScene(PyObject* self, PyObject* args) { PyObject* McRFPy_API::_keypressScene(PyObject* self, PyObject* args) {
PyObject* callable; PyObject* callable;
if (!PyArg_ParseTuple(args, "O", &callable)) return NULL; if (!PyArg_ParseTuple(args, "O", &callable)) return NULL;
/*
if (game->currentScene()->key_callable != NULL and game->currentScene()->key_callable != Py_None) if (game->currentScene()->key_callable != NULL and game->currentScene()->key_callable != Py_None)
{ {
Py_DECREF(game->currentScene()->key_callable); Py_DECREF(game->currentScene()->key_callable);
@ -460,6 +491,8 @@ PyObject* McRFPy_API::_keypressScene(PyObject* self, PyObject* args) {
Py_INCREF(callable); Py_INCREF(callable);
game->currentScene()->key_callable = callable; game->currentScene()->key_callable = callable;
Py_INCREF(Py_None); Py_INCREF(Py_None);
*/
game->currentScene()->key_callable = std::make_unique<PyKeyCallable>(callable);
return Py_None; return Py_None;
} }

View File

@ -3,6 +3,9 @@
#include "Python.h" #include "Python.h"
#include <list> #include <list>
#include "PyFont.h"
#include "PyTexture.h"
class GameEngine; // forward declared (circular members) class GameEngine; // forward declared (circular members)
class McRFPy_API class McRFPy_API
@ -12,18 +15,16 @@ private:
texture_width = 12, texture_height = 11, // w & h sprite/frame count texture_width = 12, texture_height = 11, // w & h sprite/frame count
texture_sprite_count = 11 * 12; // t_width * t_height, minus blanks? texture_sprite_count = 11 * 12; // t_width * t_height, minus blanks?
// TODO: this is wrong, load resources @ GameEngineSprite sprite;
// sf::Texture texture;
//std::vector<PyMethodDef> mcrfpyMethodsVector;
//static PyObject* PyInit_mcrfpy();
McRFPy_API(); McRFPy_API();
public: public:
inline static sf::Sprite sprite; static PyObject* mcrf_module;
inline static sf::Texture texture; static std::shared_ptr<PyFont> default_font;
static void setSpriteTexture(int); static std::shared_ptr<PyTexture> default_texture;
//inline static sf::Sprite sprite;
//inline static sf::Texture texture;
//static void setSpriteTexture(int);
inline static GameEngine* game; inline static GameEngine* game;
static void api_init(); static void api_init();
static void api_shutdown(); static void api_shutdown();

114
src/PyCallable.cpp Normal file
View File

@ -0,0 +1,114 @@
#include "PyCallable.h"
PyCallable::PyCallable(PyObject* _target)
{
target = Py_XNewRef(_target);
}
PyCallable::~PyCallable()
{
if (target)
Py_DECREF(target);
}
PyObject* PyCallable::call(PyObject* args, PyObject* kwargs)
{
return PyObject_Call(target, args, kwargs);
}
bool PyCallable::isNone()
{
return (target == Py_None || target == NULL);
}
PyTimerCallable::PyTimerCallable(PyObject* _target, int _interval, int now)
: PyCallable(_target), interval(_interval), last_ran(now)
{}
PyTimerCallable::PyTimerCallable()
: PyCallable(Py_None), interval(0), last_ran(0)
{}
bool PyTimerCallable::hasElapsed(int now)
{
return now >= last_ran + interval;
}
void PyTimerCallable::call(int now)
{
PyObject* args = Py_BuildValue("(i)", now);
PyObject* retval = PyCallable::call(args, NULL);
if (!retval)
{
PyErr_Print();
PyErr_Clear();
} else if (retval != Py_None)
{
std::cout << "timer returned a non-None value. It's not an error, it's just not being saved or used." << std::endl;
std::cout << PyUnicode_AsUTF8(PyObject_Repr(retval)) << std::endl;
}
}
bool PyTimerCallable::test(int now)
{
if(hasElapsed(now))
{
call(now);
last_ran = now;
return true;
}
return false;
}
PyClickCallable::PyClickCallable(PyObject* _target)
: PyCallable(_target)
{}
PyClickCallable::PyClickCallable()
: PyCallable(Py_None)
{}
void PyClickCallable::call(sf::Vector2f mousepos, std::string button, std::string action)
{
PyObject* args = Py_BuildValue("(iiss)", (int)mousepos.x, (int)mousepos.y, button.c_str(), action.c_str());
PyObject* retval = PyCallable::call(args, NULL);
if (!retval)
{
std::cout << "ClickCallable has raised an exception. It's going to STDERR and being dropped:" << std::endl;
PyErr_Print();
PyErr_Clear();
} else if (retval != Py_None)
{
std::cout << "ClickCallable returned a non-None value. It's not an error, it's just not being saved or used." << std::endl;
std::cout << PyUnicode_AsUTF8(PyObject_Repr(retval)) << std::endl;
}
}
PyObject* PyClickCallable::borrow()
{
return target;
}
PyKeyCallable::PyKeyCallable(PyObject* _target)
: PyCallable(_target)
{}
PyKeyCallable::PyKeyCallable()
: PyCallable(Py_None)
{}
void PyKeyCallable::call(std::string key, std::string action)
{
if (target == Py_None || target == NULL) return;
PyObject* args = Py_BuildValue("(ss)", key.c_str(), action.c_str());
PyObject* retval = PyCallable::call(args, NULL);
if (!retval)
{
std::cout << "KeyCallable has raised an exception. It's going to STDERR and being dropped:" << std::endl;
PyErr_Print();
PyErr_Clear();
} else if (retval != Py_None)
{
std::cout << "KeyCallable returned a non-None value. It's not an error, it's just not being saved or used." << std::endl;
}
}

45
src/PyCallable.h Normal file
View File

@ -0,0 +1,45 @@
#pragma once
#include "Common.h"
#include "Python.h"
class PyCallable
{
protected:
PyObject* target;
PyCallable(PyObject*);
~PyCallable();
PyObject* call(PyObject*, PyObject*);
public:
bool isNone();
};
class PyTimerCallable: public PyCallable
{
private:
int interval;
int last_ran;
void call(int);
public:
bool hasElapsed(int);
bool test(int);
PyTimerCallable(PyObject*, int, int);
PyTimerCallable();
};
class PyClickCallable: public PyCallable
{
public:
void call(sf::Vector2f, std::string, std::string);
PyObject* borrow();
PyClickCallable(PyObject*);
PyClickCallable();
};
class PyKeyCallable: public PyCallable
{
public:
void call(std::string, std::string);
//PyObject* borrow(); // not yet implemented
PyKeyCallable(PyObject*);
PyKeyCallable();
};

150
src/PyColor.cpp Normal file
View File

@ -0,0 +1,150 @@
#include "PyColor.h"
#include "McRFPy_API.h"
PyGetSetDef PyColor::getsetters[] = {
{"r", (getter)PyColor::get_member, (setter)PyColor::set_member, "Red component", (void*)0},
{"g", (getter)PyColor::get_member, (setter)PyColor::set_member, "Green component", (void*)1},
{"b", (getter)PyColor::get_member, (setter)PyColor::set_member, "Blue component", (void*)2},
{"a", (getter)PyColor::get_member, (setter)PyColor::set_member, "Alpha component", (void*)3},
{NULL}
};
PyColor::PyColor(sf::Color target)
:data(target) {}
PyObject* PyColor::pyObject()
{
PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyColorType, 0);
Py_INCREF(obj);
PyColorObject* self = (PyColorObject*)obj;
self->data = data;
return obj;
}
sf::Color PyColor::fromPy(PyObject* obj)
{
PyColorObject* self = (PyColorObject*)obj;
return self->data;
}
sf::Color PyColor::fromPy(PyColorObject* self)
{
return self->data;
}
void PyColor::set(sf::Color color)
{
data = color;
}
sf::Color PyColor::get()
{
return data;
}
Py_hash_t PyColor::hash(PyObject* obj)
{
auto self = (PyColorObject*)obj;
Py_hash_t value = 0;
value += self->data.r;
value << 8; value += self->data.g;
value << 8; value += self->data.b;
value << 8; value += self->data.a;
return value;
}
PyObject* PyColor::repr(PyObject* obj)
{
PyColorObject* self = (PyColorObject*)obj;
std::ostringstream ss;
sf::Color c = self->data;
ss << "<Color (" << int(c.r) << ", " << int(c.g) << ", " << int(c.b) << ", " << int(c.a) << ")>";
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
int PyColor::init(PyColorObject* self, PyObject* args, PyObject* kwds) {
//using namespace mcrfpydef;
static const char* keywords[] = { "r", "g", "b", "a", nullptr };
PyObject* leader;
int r = -1, g = -1, b = -1, a = 255;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iii", const_cast<char**>(keywords), &leader, &g, &b, &a)) {
PyErr_SetString(PyExc_TypeError, "mcrfpy.Color requires a 3-tuple, 4-tuple, color name, or integer values within 0-255 (r, g, b, optionally a)");
return -1;
}
//std::cout << "Arg parsing succeeded. Values: " << r << " " << g << " " << b << " " << a <<std::endl;
//std::cout << PyUnicode_AsUTF8(PyObject_Repr(leader)) << std::endl;
// Tuple cases
if (PyTuple_Check(leader)) {
Py_ssize_t tupleSize = PyTuple_Size(leader);
if (tupleSize < 3 || tupleSize > 4) {
PyErr_SetString(PyExc_TypeError, "Invalid tuple length: mcrfpy.Color requires a 3-tuple, 4-tuple, color name, or integer values within 0-255 (r, g, b, optionally a)");
return -1;
}
r = PyLong_AsLong(PyTuple_GetItem(leader, 0));
g = PyLong_AsLong(PyTuple_GetItem(leader, 1));
b = PyLong_AsLong(PyTuple_GetItem(leader, 2));
if (tupleSize == 4) {
a = PyLong_AsLong(PyTuple_GetItem(leader, 3));
}
}
// Color name (not implemented yet)
else if (PyUnicode_Check(leader)) {
PyErr_SetString(PyExc_NotImplementedError, "Color names aren't ready yet");
return -1;
}
// Check if the leader is actually an integer for the r value
else if (PyLong_Check(leader)) {
r = PyLong_AsLong(leader);
// Additional validation not shown; g, b are required to be parsed
} else {
PyErr_SetString(PyExc_TypeError, "mcrfpy.Color requires a 3-tuple, 4-tuple, color name, or integer values within 0-255 (r, g, b, optionally a)");
return -1;
}
// Validate color values
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) {
PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255.");
return -1;
}
self->data = sf::Color(r, g, b, a);
return 0;
}
PyObject* PyColor::pynew(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
auto obj = (PyObject*)type->tp_alloc(type, 0);
//Py_INCREF(obj);
return obj;
}
PyObject* PyColor::get_member(PyObject* obj, void* closure)
{
// TODO
return Py_None;
}
int PyColor::set_member(PyObject* obj, PyObject* value, void* closure)
{
// TODO
return 0;
}
PyColorObject* PyColor::from_arg(PyObject* args)
{
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color");
if (PyObject_IsInstance(args, (PyObject*)type)) return (PyColorObject*)args;
auto obj = (PyColorObject*)type->tp_alloc(type, 0);
int err = init(obj, args, NULL);
if (err) {
Py_DECREF(obj);
return NULL;
}
return obj;
}

48
src/PyColor.h Normal file
View File

@ -0,0 +1,48 @@
#pragma once
#include "Common.h"
#include "Python.h"
class PyColor;
class UIDrawable; // forward declare for pointer
typedef struct {
PyObject_HEAD
sf::Color data;
} PyColorObject;
class PyColor
{
private:
public:
sf::Color data;
PyColor(sf::Color);
void set(sf::Color);
sf::Color get();
PyObject* pyObject();
static sf::Color fromPy(PyObject*);
static sf::Color fromPy(PyColorObject*);
static PyObject* repr(PyObject*);
static Py_hash_t hash(PyObject*);
static int init(PyColorObject*, PyObject*, PyObject*);
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 PyGetSetDef getsetters[];
static PyColorObject* from_arg(PyObject*);
};
namespace mcrfpydef {
static PyTypeObject PyColorType = {
.tp_name = "mcrfpy.Color",
.tp_basicsize = sizeof(PyColorObject),
.tp_itemsize = 0,
.tp_repr = PyColor::repr,
.tp_hash = PyColor::hash,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("SFML Color Object"),
.tp_getset = PyColor::getsetters,
.tp_init = (initproc)PyColor::init,
.tp_new = PyColor::pynew,
};
}

63
src/PyFont.cpp Normal file
View File

@ -0,0 +1,63 @@
#include "PyFont.h"
#include "McRFPy_API.h"
PyFont::PyFont(std::string filename)
: source(filename)
{
font = sf::Font();
font.loadFromFile(source);
}
PyObject* PyFont::pyObject()
{
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Font");
//PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyFontType, 0);
PyObject* obj = PyFont::pynew(type, Py_None, Py_None);
try {
((PyFontObject*)obj)->data = shared_from_this();
}
catch (std::bad_weak_ptr& e)
{
std::cout << "Bad weak ptr: shared_from_this() failed in PyFont::pyObject(); did you create a PyFont outside of std::make_shared? enjoy your segfault, soon!" << std::endl;
}
// TODO - shared_from_this will raise an exception if the object does not have a shared pointer. Constructor should be made private; write a factory function
return obj;
}
PyObject* PyFont::repr(PyObject* obj)
{
PyFontObject* self = (PyFontObject*)obj;
std::ostringstream ss;
if (!self->data)
{
ss << "<Font [invalid internal object]>";
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
auto& pfont = *(self->data);
ss << "<Font (family=" << pfont.font.getInfo().family << ") source=`" << pfont.source << "`>";
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
Py_hash_t PyFont::hash(PyObject* obj)
{
auto self = (PyFontObject*)obj;
return reinterpret_cast<Py_hash_t>(self->data.get());
}
int PyFont::init(PyFontObject* self, PyObject* args, PyObject* kwds)
{
static const char* keywords[] = { "filename", nullptr };
char* filename;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", const_cast<char**>(keywords), &filename))
return -1;
self->data = std::make_shared<PyFont>(filename);
return 0;
}
PyObject* PyFont::pynew(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
return (PyObject*)type->tp_alloc(type, 0);
}

39
src/PyFont.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include "Common.h"
#include "Python.h"
class PyFont;
typedef struct {
PyObject_HEAD
std::shared_ptr<PyFont> data;
} PyFontObject;
class PyFont : public std::enable_shared_from_this<PyFont>
{
private:
std::string source;
public:
PyFont(std::string filename);
sf::Font font;
PyObject* pyObject();
static PyObject* repr(PyObject*);
static Py_hash_t hash(PyObject*);
static int init(PyFontObject*, PyObject*, PyObject*);
static PyObject* pynew(PyTypeObject* type, PyObject* args=NULL, PyObject* kwds=NULL);
};
namespace mcrfpydef {
static PyTypeObject PyFontType = {
.tp_name = "mcrfpy.Font",
.tp_basicsize = sizeof(PyFontObject),
.tp_itemsize = 0,
.tp_repr = PyFont::repr,
//.tp_hash = PyFont::hash,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("SFML Font Object"),
//.tp_base = &PyBaseObject_Type,
.tp_init = (initproc)PyFont::init,
.tp_new = PyType_GenericNew, //PyFont::pynew,
};
}

View File

@ -1,6 +1,7 @@
#include "PyScene.h" #include "PyScene.h"
#include "ActionCode.h" #include "ActionCode.h"
#include "Resources.h" #include "Resources.h"
#include "PyCallable.h"
PyScene::PyScene(GameEngine* g) : Scene(g) PyScene::PyScene(GameEngine* g) : Scene(g)
{ {
@ -27,6 +28,7 @@ void PyScene::do_mouse_input(std::string button, std::string type)
target = d->click_at(sf::Vector2f(mousepos)); target = d->click_at(sf::Vector2f(mousepos));
if (target) if (target)
{ {
/*
PyObject* args = Py_BuildValue("(iiss)", (int)mousepos.x, (int)mousepos.y, button.c_str(), type.c_str()); PyObject* args = Py_BuildValue("(iiss)", (int)mousepos.x, (int)mousepos.y, button.c_str(), type.c_str());
PyObject* retval = PyObject_Call(target->click_callable, args, NULL); PyObject* retval = PyObject_Call(target->click_callable, args, NULL);
if (!retval) if (!retval)
@ -38,6 +40,8 @@ void PyScene::do_mouse_input(std::string button, std::string type)
{ {
std::cout << "click_callable returned a non-None value. It's not an error, it's just not being saved or used." << std::endl; std::cout << "click_callable returned a non-None value. It's not an error, it's just not being saved or used." << std::endl;
} }
*/
target->click_callable->call(mousepos, button, type);
} }
} }
} }
@ -55,7 +59,7 @@ void PyScene::doAction(std::string name, std::string type)
} }
} }
void PyScene::sRender() void PyScene::render()
{ {
game->getWindow().clear(); game->getWindow().clear();

View File

@ -11,7 +11,7 @@ public:
PyScene(GameEngine*); PyScene(GameEngine*);
void update() override final; void update() override final;
void doAction(std::string, std::string) override final; void doAction(std::string, std::string) override final;
void sRender() override final; void render() override final;
void do_mouse_input(std::string, std::string); void do_mouse_input(std::string, std::string);
}; };

82
src/PyTexture.cpp Normal file
View File

@ -0,0 +1,82 @@
#include "PyTexture.h"
#include "McRFPy_API.h"
PyTexture::PyTexture(std::string filename, int sprite_w, int sprite_h)
: source(filename), sprite_width(sprite_w), sprite_height(sprite_h)
{
texture = sf::Texture();
texture.loadFromFile(source);
auto size = texture.getSize();
sheet_width = (size.x / sprite_width);
sheet_height = (size.y / sprite_height);
if (size.x % sprite_width != 0 || size.y % sprite_height != 0)
{
std::cout << "Warning: Texture `" << source << "` is not an even number of sprite widths or heights across." << std::endl
<< "Sprite size given was " << sprite_w << "x" << sprite_h << "px but the file has a resolution of " << sheet_width << "x" << sheet_height << "px." << std::endl;
}
}
sf::Sprite PyTexture::sprite(int index, sf::Vector2f pos, sf::Vector2f s)
{
int tx = index % sheet_width, ty = index / sheet_width;
auto ir = sf::IntRect(tx * sprite_width, ty * sprite_height, sprite_width, sprite_height);
auto sprite = sf::Sprite(texture, ir);
sprite.setPosition(pos);
sprite.setScale(s);
return sprite;
}
PyObject* PyTexture::pyObject()
{
std::cout << "Find type" << std::endl;
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture");
PyObject* obj = PyTexture::pynew(type, Py_None, Py_None);
try {
((PyTextureObject*)obj)->data = shared_from_this();
}
catch (std::bad_weak_ptr& e)
{
std::cout << "Bad weak ptr: shared_from_this() failed in PyTexture::pyObject(); did you create a PyTexture outside of std::make_shared? enjoy your segfault, soon!" << std::endl;
}
// TODO - shared_from_this will raise an exception if the object does not have a shared pointer. Constructor should be made private; write a factory function
return obj;
}
PyObject* PyTexture::repr(PyObject* obj)
{
PyTextureObject* self = (PyTextureObject*)obj;
std::ostringstream ss;
if (!self->data)
{
ss << "<Texture [invalid internal object]>";
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
auto& ptex = *(self->data);
ss << "<Texture " << ptex.sheet_height << " rows, " << ptex.sheet_width << " columns; " << ptex.sprite_width << "x" << ptex.sprite_height << "px sprites. source='" << ptex.source << "'>";
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
Py_hash_t PyTexture::hash(PyObject* obj)
{
auto self = (PyTextureObject*)obj;
return reinterpret_cast<Py_hash_t>(self->data.get());
}
int PyTexture::init(PyTextureObject* self, PyObject* args, PyObject* kwds)
{
static const char* keywords[] = { "filename", "sprite_width", "sprite_height", nullptr };
char* filename;
int sprite_width, sprite_height;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "sii", const_cast<char**>(keywords), &filename, &sprite_width, &sprite_height))
return -1;
self->data = std::make_shared<PyTexture>(filename, sprite_width, sprite_height);
return 0;
}
PyObject* PyTexture::pynew(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
return (PyObject*)type->tp_alloc(type, 0);
}

43
src/PyTexture.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include "Common.h"
#include "Python.h"
class PyTexture;
typedef struct {
PyObject_HEAD
std::shared_ptr<PyTexture> data;
} PyTextureObject;
class PyTexture : public std::enable_shared_from_this<PyTexture>
{
private:
sf::Texture texture;
std::string source;
int sheet_width, sheet_height;
public:
int sprite_width, sprite_height; // just use them read only, OK?
PyTexture(std::string filename, int sprite_w, int sprite_h);
sf::Sprite sprite(int index, sf::Vector2f pos = sf::Vector2f(0, 0), sf::Vector2f s = sf::Vector2f(1.0, 1.0));
PyObject* pyObject();
static PyObject* repr(PyObject*);
static Py_hash_t hash(PyObject*);
static int init(PyTextureObject*, PyObject*, PyObject*);
static PyObject* pynew(PyTypeObject* type, PyObject* args=NULL, PyObject* kwds=NULL);
};
namespace mcrfpydef {
static PyTypeObject PyTextureType = {
.tp_name = "mcrfpy.Texture",
.tp_basicsize = sizeof(PyTextureObject),
.tp_itemsize = 0,
.tp_repr = PyTexture::repr,
.tp_hash = PyTexture::hash,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("SFML Texture Object"),
//.tp_base = &PyBaseObject_Type,
.tp_init = (initproc)PyTexture::init,
.tp_new = PyType_GenericNew, //PyTexture::pynew,
};
}

111
src/PyVector.cpp Normal file
View File

@ -0,0 +1,111 @@
#include "PyVector.h"
PyGetSetDef PyVector::getsetters[] = {
{"x", (getter)PyVector::get_member, (setter)PyVector::set_member, "X/horizontal component", (void*)0},
{"y", (getter)PyVector::get_member, (setter)PyVector::set_member, "Y/vertical component", (void*)1},
{NULL}
};
PyVector::PyVector(sf::Vector2f target)
:data(target) {}
PyObject* PyVector::pyObject()
{
PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyVectorType, 0);
Py_INCREF(obj);
PyVectorObject* self = (PyVectorObject*)obj;
self->data = data;
return obj;
}
sf::Vector2f PyVector::fromPy(PyObject* obj)
{
PyVectorObject* self = (PyVectorObject*)obj;
return self->data;
}
sf::Vector2f PyVector::fromPy(PyVectorObject* self)
{
return self->data;
}
Py_hash_t PyVector::hash(PyObject* obj)
{
auto self = (PyVectorObject*)obj;
Py_hash_t value = 0;
value += self->data.x;
value << 8; value += self->data.y;
return value;
}
PyObject* PyVector::repr(PyObject* obj)
{
PyVectorObject* self = (PyVectorObject*)obj;
std::ostringstream ss;
sf::Vector2f v = self->data;
ss << "<Vector (" << v.x << ", " << v.y << ")>";
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
int PyVector::init(PyVectorObject* self, PyObject* args, PyObject* kwds)
{
using namespace mcrfpydef;
static const char* keywords[] = { "x", "y", nullptr };
PyObject* leader = NULL;
float x=0, y=0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Of", const_cast<char**>(keywords), &leader, &y))
{
//PyErr_SetString(PyExc_TypeError, "mcrfpy.Vector requires a 2-tuple or two numeric values");
return -1;
}
if (leader == NULL || leader == Py_None)
{
self->data = sf::Vector2f();
return 0;
}
if (PyTuple_Check(leader))
{
if (PyTuple_Size(leader) != 2)
{
PyErr_SetString(PyExc_TypeError, "Invalid tuple length: mcrfpy.Vector requires a 2-tuple");
return -1;
}
x = PyFloat_AsDouble(PyTuple_GetItem(leader, 0));
y = PyFloat_AsDouble(PyTuple_GetItem(leader, 1));
self->data = sf::Vector2f(x, y);
return 0;
}
// else -
else if (!PyFloat_Check(leader) && !(PyLong_Check(leader)))
{
PyErr_SetString(PyExc_TypeError, "mcrfpy.Vector requires a 2-tuple or two numeric values");
return -1;
}
if (PyFloat_Check(leader)) x = PyFloat_AsDouble(leader);
else x = PyLong_AsDouble(leader);
self->data = sf::Vector2f(x, y);
return 0;
}
PyObject* PyVector::pynew(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
return (PyObject*)type->tp_alloc(type, 0);
}
PyObject* PyVector::get_member(PyObject* obj, void* closure)
{
// TODO
return Py_None;
}
int PyVector::set_member(PyObject* obj, PyObject* value, void* closure)
{
// TODO
return 0;
}

42
src/PyVector.h Normal file
View File

@ -0,0 +1,42 @@
#pragma once
#include "Common.h"
#include "Python.h"
typedef struct {
PyObject_HEAD
sf::Vector2f data;
} PyVectorObject;
class PyVector
{
public:
sf::Vector2f data;
PyVector(sf::Vector2f);
PyVector();
PyObject* pyObject();
static sf::Vector2f fromPy(PyObject*);
static sf::Vector2f fromPy(PyVectorObject*);
static PyObject* repr(PyObject*);
static Py_hash_t hash(PyObject*);
static int init(PyVectorObject*, PyObject*, PyObject*);
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 PyGetSetDef getsetters[];
};
namespace mcrfpydef {
static PyTypeObject PyVectorType = {
.tp_name = "mcrfpy.Vector",
.tp_basicsize = sizeof(PyVectorObject),
.tp_itemsize = 0,
.tp_repr = PyVector::repr,
.tp_hash = PyVector::hash,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("SFML Vector Object"),
.tp_getset = PyVector::getsetters,
.tp_init = (initproc)PyVector::init,
.tp_new = PyVector::pynew,
};
}

View File

@ -4,7 +4,7 @@
//Scene::Scene() { game = 0; std::cout << "WARN: default Scene constructor called. (game = " << game << ")" << std::endl;}; //Scene::Scene() { game = 0; std::cout << "WARN: default Scene constructor called. (game = " << game << ")" << std::endl;};
Scene::Scene(GameEngine* g) Scene::Scene(GameEngine* g)
{ {
key_callable = Py_None; key_callable = std::make_unique<PyKeyCallable>();
game = g; game = g;
ui_elements = std::make_shared<std::vector<std::shared_ptr<UIDrawable>>>(); ui_elements = std::make_shared<std::vector<std::shared_ptr<UIDrawable>>>();
} }
@ -43,6 +43,7 @@ bool Scene::unregisterActionInjected(int code, std::string name)
void Scene::key_register(PyObject* callable) void Scene::key_register(PyObject* callable)
{ {
/*
if (key_callable) if (key_callable)
{ {
// decrement reference before overwriting // decrement reference before overwriting
@ -50,11 +51,16 @@ void Scene::key_register(PyObject* callable)
} }
key_callable = callable; key_callable = callable;
Py_INCREF(key_callable); Py_INCREF(key_callable);
*/
key_callable = std::make_unique<PyKeyCallable>(callable);
} }
void Scene::key_unregister() void Scene::key_unregister()
{ {
/*
if (key_callable == NULL) return; if (key_callable == NULL) return;
Py_DECREF(key_callable); Py_DECREF(key_callable);
key_callable = NULL; key_callable = NULL;
*/
key_callable.reset();
} }

View File

@ -9,6 +9,7 @@
#include "Common.h" #include "Common.h"
#include <list> #include <list>
#include "UI.h" #include "UI.h"
#include "PyCallable.h"
//#include "GameEngine.h" //#include "GameEngine.h"
class GameEngine; // forward declare class GameEngine; // forward declare
@ -30,7 +31,7 @@ public:
//Scene(); //Scene();
Scene(GameEngine*); Scene(GameEngine*);
virtual void update() = 0; virtual void update() = 0;
virtual void sRender() = 0; virtual void render() = 0;
virtual void doAction(std::string, std::string) = 0; virtual void doAction(std::string, std::string) = 0;
bool hasAction(std::string); bool hasAction(std::string);
bool hasAction(int); bool hasAction(int);
@ -41,7 +42,8 @@ public:
std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>> ui_elements; std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>> ui_elements;
PyObject* key_callable; //PyObject* key_callable;
std::unique_ptr<PyKeyCallable> key_callable;
void key_register(PyObject*); void key_register(PyObject*);
void key_unregister(); void key_unregister();
}; };

View File

@ -1,465 +0,0 @@
#include "UI.h"
#include "Resources.h"
#include "GameEngine.h"
/* //callability fields & methods
PyObject* click_callable;
virtual UIDrawable* click_at(sf::Vector2f point);
void click_register(PyObject*);
void click_unregister();
*/
UIDrawable::UIDrawable() { click_callable = NULL; }
UIDrawable* UIFrame::click_at(sf::Vector2f point)
{
for (auto e: *children)
{
auto p = e->click_at(point + box.getPosition());
if (p)
return p;
}
if (click_callable)
{
float x = box.getPosition().x, y = box.getPosition().y, w = box.getSize().x, h = box.getSize().y;
if (point.x > x && point.y > y && point.x < x+w && point.y < y+h) return this;
}
return NULL;
}
UIDrawable* UICaption::click_at(sf::Vector2f point)
{
if (click_callable)
{
if (text.getGlobalBounds().contains(point)) return this;
}
return NULL;
}
UIDrawable* UISprite::click_at(sf::Vector2f point)
{
if (click_callable)
{
if(sprite.getGlobalBounds().contains(point)) return this;
}
return NULL;
}
UIDrawable* UIGrid::click_at(sf::Vector2f point)
{
if (click_callable)
{
if(box.getGlobalBounds().contains(point)) return this;
}
return NULL;
}
void UIDrawable::click_register(PyObject* callable)
{
if (click_callable)
{
// decrement reference before overwriting
Py_DECREF(click_callable);
}
click_callable = callable;
Py_INCREF(click_callable);
}
void UIDrawable::click_unregister()
{
if (click_callable == NULL) return;
Py_DECREF(click_callable);
click_callable = NULL;
}
void UIDrawable::render()
{
//std::cout << "Rendering base UIDrawable\n";
render(sf::Vector2f());
}
UIFrame::UIFrame():
//x(0), y(0), w(0), h(0),
outline(0)
{
children = std::make_shared<std::vector<std::shared_ptr<UIDrawable>>>();
box.setPosition(0, 0);
box.setSize(sf::Vector2f(0, 0));
/*
pyOutlineColor = NULL;
pyFillColor = NULL;
_outlineColor = NULL;
_fillColor = NULL;
*/
}
UIFrame::UIFrame(float _x, float _y, float _w, float _h):
//x(_x), y(_y), w(_w), h(_h),
outline(0)
{
box.setPosition(_x, _y);
box.setSize(sf::Vector2f(_w, _h));
children = std::make_shared<std::vector<std::shared_ptr<UIDrawable>>>();
/*
pyOutlineColor = NULL;
pyFillColor = NULL;
_outlineColor = NULL;
_fillColor = NULL;
*/
}
UIFrame::~UIFrame()
{
children.reset();
/*
if (pyOutlineColor) Py_DECREF(pyOutlineColor);
else if (_outlineColor) delete _outlineColor;
if (pyFillColor) Py_DECREF(pyFillColor);
else if (_fillColor) delete _fillColor;
*/
}
/*
sf::Color& fillColor(); // getter
void fillColor(sf::Color c); // C++ setter
void fillColor(PyObject* pyColor); // Python setter
sf::Color& outlineColor(); // getter
void outlineColor(sf::Color c); // C++ setter
void outlineColor(PyObject* pyColor); // Python setter
*/
PyObjectsEnum UIFrame::derived_type()
{
return PyObjectsEnum::UIFRAME;
}
void UIFrame::render(sf::Vector2f offset)
{
//std::cout << "Rendering UIFrame w/ offset " << offset.x << ", " << offset.y << "\n";
//std::cout << "position = " << x << ", " << y << "\n";
box.move(offset);
Resources::game->getWindow().draw(box);
box.move(-offset);
//sf::RectangleShape box = sf::RectangleShape(sf::Vector2f(w,h));
//sf::Vector2f pos = sf::Vector2f(x, y);
//box.setPosition(offset + pos);
//if (_fillColor) { box.setFillColor(fillColor()); }
//if (_outlineColor) { box.setOutlineColor(outlineColor()); }
//box.setOutlineThickness(outline);
//Resources::game->getWindow().draw(box);
for (auto drawable : *children) {
drawable->render(offset + box.getPosition());
}
}
void UICaption::render(sf::Vector2f offset)
{
//std::cout << "Rendering Caption with offset\n";
text.move(offset);
Resources::game->getWindow().draw(text);
text.move(-offset);
}
UISprite::UISprite() {}
UISprite::UISprite(IndexTexture* _itex, int _sprite_index, float x = 0.0, float y = 0.0, float s = 1.0)
: itex(_itex), sprite_index(_sprite_index)
{
sprite.setTexture(_itex->texture);
sprite.setTextureRect(_itex->spriteCoordinates(_sprite_index));
sprite.setPosition(sf::Vector2f(x, y));
sprite.setScale(sf::Vector2f(s, s));
}
UISprite::UISprite(IndexTexture* _itex, int _sprite_index, sf::Vector2f pos, float s = 1.0)
: itex(_itex), sprite_index(_sprite_index)
{
sprite.setTexture(_itex->texture);
sprite.setTextureRect(_itex->spriteCoordinates(_sprite_index));
sprite.setPosition(pos);
sprite.setScale(sf::Vector2f(s, s));
}
//void UISprite::update()
//{
//auto& tex = Resources::game->textures[texture_index];
//sprite.setTexture(tex.texture);
//sprite.setScale(sf::Vector2f(scale, scale));
//sprite.setPosition(sf::Vector2f(x, y));
//std::cout << "Drawable position: " << x << ", " << y << " -> " << s.getPosition().x << ", " << s.getPosition().y << std::endl;
//sprite.setTextureRect(tex.spriteCoordinates(sprite_index));
//}
void UISprite::render(sf::Vector2f offset)
{
sprite.move(offset);
Resources::game->getWindow().draw(sprite);
sprite.move(-offset);
}
// 7DRL hack; needed to draw entities to UIGrid. TODO, apply this technique to all UIDrawables
void UISprite::render(sf::Vector2f offset, sf::RenderTexture& target)
{
sprite.move(offset);
target.draw(sprite);
sprite.move(-offset);
}
void UISprite::setPosition(float x, float y)
{
setPosition(sf::Vector2f(x, y));
}
void UISprite::setPosition(sf::Vector2f pos)
{
sprite.setPosition(pos);
}
void UISprite::setScale(float s)
{
sprite.setScale(sf::Vector2f(s, s));
}
PyObjectsEnum UICaption::derived_type()
{
return PyObjectsEnum::UICAPTION;
}
PyObjectsEnum UISprite::derived_type()
{
return PyObjectsEnum::UISPRITE;
}
// UIGrid support classes' methods
UIGridPoint::UIGridPoint()
:color(1.0f, 1.0f, 1.0f), color_overlay(0.0f, 0.0f, 0.0f), walkable(false), transparent(false),
tilesprite(-1), tile_overlay(-1), uisprite(-1)
{
}
UIEntity::UIEntity() {} // this will not work lol. TODO remove default constructor by finding the shared pointer inits that use it
UIEntity::UIEntity(UIGrid& grid)
: gridstate(grid.grid_x * grid.grid_y)
{
}
// UIGrid methods
UIGrid::UIGrid()
{
}
UIGrid::UIGrid(int gx, int gy, IndexTexture* _itex, float _x, float _y, float _w, float _h)
: grid_x(gx), grid_y(gy),
zoom(1.0f), center_x((gx/2) * _itex->grid_size), center_y((gy/2) * _itex->grid_size),
itex(_itex), points(gx * gy)
{
// set up blank list of entities
entities = std::make_shared<std::list<std::shared_ptr<UIEntity>>>();
box.setSize(sf::Vector2f(_w, _h));
box.setPosition(sf::Vector2f(_x, _y));
box.setFillColor(sf::Color(0,0,0,0));
renderTexture.create(_w, _h);
sprite.setTexture(_itex->texture);
output.setTextureRect(
sf::IntRect(0, 0,
box.getSize().x, box.getSize().y));
output.setPosition(box.getPosition());
// textures are upside-down inside renderTexture
output.setTexture(renderTexture.getTexture());
}
UIGrid::UIGrid(int gx, int gy, IndexTexture* _itex, sf::Vector2f _xy, sf::Vector2f _wh)
: grid_x(gx), grid_y(gy),
zoom(1.0f), center_x((gx/2) * _itex->grid_size), center_y((gy/2) * _itex->grid_size),
itex(_itex), points(gx * gy)
{
// set up blank list of entities
entities = std::make_shared<std::list<std::shared_ptr<UIEntity>>>();
box.setSize(_wh);
box.setPosition(_xy);
box.setFillColor(sf::Color(0,0,0,0));
//renderTexture.create(_wh.x, _wh.y);
// 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
sprite.setTexture(_itex->texture);
output.setTextureRect(
sf::IntRect(0, 0,
box.getSize().x, box.getSize().y));
output.setPosition(box.getPosition());
// textures are upside-down inside renderTexture
output.setTexture(renderTexture.getTexture());
}
void UIGrid::update()
{
}
void UIGrid::setSprite(int ti)
{
int tx = ti % itex->grid_width, ty = ti / itex->grid_width;
sprite.setTextureRect(sf::IntRect(tx * itex->grid_size, ty * itex->grid_size, itex->grid_size, itex->grid_size));
}
void UIGrid::render(sf::Vector2f)
{
output.setPosition(box.getPosition()); // output sprite can move; update position when drawing
// output size can change; update size when drawing
output.setTextureRect(
sf::IntRect(0, 0,
box.getSize().x, box.getSize().y));
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 / itex->grid_size;
float center_y_sq = center_y / itex->grid_size;
float width_sq = box.getSize().x / (itex->grid_size * zoom);
float height_sq = box.getSize().y / (itex->grid_size * zoom);
float left_edge = center_x_sq - (width_sq / 2.0);
float top_edge = center_y_sq - (height_sq / 2.0);
int left_spritepixels = center_x - (box.getSize().x / 2.0 / zoom);
int top_spritepixels = center_y - (box.getSize().y / 2.0 / zoom);
sprite.setScale(sf::Vector2f(zoom, zoom));
sf::RectangleShape r; // for colors and overlays
r.setSize(sf::Vector2f(itex->grid_size * zoom, itex->grid_size * zoom));
r.setOutlineThickness(0);
int x_limit = left_edge + width_sq + 2;
if (x_limit > grid_x) x_limit = grid_x;
int y_limit = top_edge + height_sq + 2;
if (y_limit > grid_y) y_limit = grid_y;
// base layer - bottom color, tile sprite ("ground")
for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0);
x < x_limit; //x < view_width;
x+=1)
{
//for (float y = (top_edge >= 0 ? top_edge : 0);
for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0);
y < y_limit; //y < view_height;
y+=1)
{
auto pixel_pos = sf::Vector2f(
(x*itex->grid_size - left_spritepixels) * zoom,
(y*itex->grid_size - top_spritepixels) * zoom );
auto gridpoint = at(std::floor(x), std::floor(y));
sprite.setPosition(pixel_pos);
r.setPosition(pixel_pos);
r.setFillColor(gridpoint.color);
renderTexture.draw(r);
// tilesprite
// if discovered but not visible, set opacity to 90%
// if not discovered... just don't draw it?
if (gridpoint.tilesprite != -1) {
setSprite(gridpoint.tilesprite);
renderTexture.draw(sprite);
}
}
}
// middle layer - entities
// disabling entity rendering until I can render their UISprite inside the rendertexture (not directly to window)
for (auto e : *entities) {
// TODO skip out-of-bounds entities (grid square not visible at all, check for partially on visible grid squares / floating point grid position)
//auto drawent = e->cGrid->indexsprite.drawable();
auto drawent = e->sprite;
//drawent.setScale(zoom, zoom);
drawent.setScale(zoom);
auto pixel_pos = sf::Vector2f(
(e->position.x*itex->grid_size - left_spritepixels) * zoom,
(e->position.y*itex->grid_size - top_spritepixels) * zoom );
//drawent.setPosition(pixel_pos);
//renderTexture.draw(drawent);
drawent.render(pixel_pos, renderTexture);
}
// top layer - opacity for discovered / visible status (debug, basically)
/* // Disabled until I attach a "perspective"
for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0);
x < x_limit; //x < view_width;
x+=1)
{
//for (float y = (top_edge >= 0 ? top_edge : 0);
for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0);
y < y_limit; //y < view_height;
y+=1)
{
auto pixel_pos = sf::Vector2f(
(x*itex->grid_size - left_spritepixels) * zoom,
(y*itex->grid_size - top_spritepixels) * zoom );
auto gridpoint = at(std::floor(x), std::floor(y));
sprite.setPosition(pixel_pos);
r.setPosition(pixel_pos);
// visible & discovered layers for testing purposes
if (!gridpoint.discovered) {
r.setFillColor(sf::Color(16, 16, 20, 192)); // 255 opacity for actual blackout
renderTexture.draw(r);
} else if (!gridpoint.visible) {
r.setFillColor(sf::Color(32, 32, 40, 128));
renderTexture.draw(r);
}
// overlay
// uisprite
}
}
*/
// grid lines for testing & validation
/*
sf::Vertex line[] =
{
sf::Vertex(sf::Vector2f(0, 0), sf::Color::Red),
sf::Vertex(box.getSize(), sf::Color::Red),
};
renderTexture.draw(line, 2, sf::Lines);
sf::Vertex lineb[] =
{
sf::Vertex(sf::Vector2f(0, box.getSize().y), sf::Color::Blue),
sf::Vertex(sf::Vector2f(box.getSize().x, 0), sf::Color::Blue),
};
renderTexture.draw(lineb, 2, sf::Lines);
*/
// render to window
renderTexture.display();
Resources::game->getWindow().draw(output);
}
UIGridPoint& UIGrid::at(int x, int y)
{
return points[y * grid_x + x];
}
PyObjectsEnum UIGrid::derived_type()
{
return PyObjectsEnum::UIGRID;
}

2493
src/UI.h

File diff suppressed because it is too large Load Diff

32
src/UIBase.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
class UIEntity;
typedef struct {
PyObject_HEAD
std::shared_ptr<UIEntity> data;
} PyUIEntityObject;
class UIFrame;
typedef struct {
PyObject_HEAD
std::shared_ptr<UIFrame> data;
} PyUIFrameObject;
class UICaption;
typedef struct {
PyObject_HEAD
std::shared_ptr<UICaption> data;
PyObject* font;
} PyUICaptionObject;
class UIGrid;
typedef struct {
PyObject_HEAD
std::shared_ptr<UIGrid> data;
} PyUIGridObject;
class UISprite;
typedef struct {
PyObject_HEAD
std::shared_ptr<UISprite> data;
} PyUISpriteObject;

283
src/UICaption.cpp Normal file
View File

@ -0,0 +1,283 @@
#include "UICaption.h"
#include "GameEngine.h"
#include "PyColor.h"
#include "PyVector.h"
#include "PyFont.h"
UIDrawable* UICaption::click_at(sf::Vector2f point)
{
if (click_callable)
{
if (text.getGlobalBounds().contains(point)) return this;
}
return NULL;
}
void UICaption::render(sf::Vector2f offset)
{
text.move(offset);
Resources::game->getWindow().draw(text);
text.move(-offset);
}
PyObjectsEnum UICaption::derived_type()
{
return PyObjectsEnum::UICAPTION;
}
PyObject* UICaption::get_float_member(PyUICaptionObject* self, void* closure)
{
auto member_ptr = reinterpret_cast<long>(closure);
if (member_ptr == 0)
return PyFloat_FromDouble(self->data->text.getPosition().x);
else if (member_ptr == 1)
return PyFloat_FromDouble(self->data->text.getPosition().y);
else if (member_ptr == 4)
return PyFloat_FromDouble(self->data->text.getOutlineThickness());
else if (member_ptr == 5)
return PyLong_FromLong(self->data->text.getCharacterSize());
else
{
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
return nullptr;
}
}
int UICaption::set_float_member(PyUICaptionObject* self, PyObject* value, void* closure)
{
float val;
auto member_ptr = reinterpret_cast<long>(closure);
if (PyFloat_Check(value))
{
val = PyFloat_AsDouble(value);
}
else if (PyLong_Check(value))
{
val = PyLong_AsLong(value);
}
else
{
PyErr_SetString(PyExc_TypeError, "Value must be an integer.");
return -1;
}
if (member_ptr == 0) //x
self->data->text.setPosition(val, self->data->text.getPosition().y);
else if (member_ptr == 1) //y
self->data->text.setPosition(self->data->text.getPosition().x, val);
else if (member_ptr == 4) //outline
self->data->text.setOutlineThickness(val);
else if (member_ptr == 5) // character size
self->data->text.setCharacterSize(val);
return 0;
}
PyObject* UICaption::get_vec_member(PyUICaptionObject* self, void* closure)
{
return PyVector(self->data->text.getPosition()).pyObject();
}
int UICaption::set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure)
{
self->data->text.setPosition(PyVector::fromPy(value));
return 0;
}
PyObject* UICaption::get_color_member(PyUICaptionObject* self, void* closure)
{
// TODO: migrate this code to a switch statement - validate closure & return values in one tighter, more extensible structure
// validate closure (should be impossible to be wrong, but it's thorough)
auto member_ptr = reinterpret_cast<long>(closure);
if (member_ptr != 0 && member_ptr != 1)
{
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
return nullptr;
}
// TODO: manually calling tp_alloc to create a PyColorObject seems like an antipattern
// fetch correct member data
sf::Color color;
if (member_ptr == 0)
{
color = self->data->text.getFillColor();
}
else if (member_ptr == 1)
{
color = self->data->text.getOutlineColor();
}
return PyColor(color).pyObject();
}
int UICaption::set_color_member(PyUICaptionObject* self, PyObject* value, void* closure)
{
auto member_ptr = reinterpret_cast<long>(closure);
//TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse
int r, g, b, a;
if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color") /*(PyObject*)&mcrfpydef::PyColorType)*/))
{
// get value from mcrfpy.Color instance
auto c = ((PyColorObject*)value)->data;
r = c.r; g = c.g; b = c.b; a = c.a;
std::cout << "got " << int(r) << ", " << int(g) << ", " << int(b) << ", " << int(a) << std::endl;
}
else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4)
{
// reject non-Color, non-tuple value
PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object.");
return -1;
}
else // get value from tuples
{
r = PyLong_AsLong(PyTuple_GetItem(value, 0));
g = PyLong_AsLong(PyTuple_GetItem(value, 1));
b = PyLong_AsLong(PyTuple_GetItem(value, 2));
a = 255;
if (PyTuple_Size(value) == 4)
{
a = PyLong_AsLong(PyTuple_GetItem(value, 3));
}
}
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255)
{
PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255.");
return -1;
}
if (member_ptr == 0)
{
self->data->text.setFillColor(sf::Color(r, g, b, a));
}
else if (member_ptr == 1)
{
self->data->text.setOutlineColor(sf::Color(r, g, b, a));
}
else
{
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
return -1;
}
return 0;
}
//TODO: evaluate use of Resources::caption_buffer... can't I do this with a std::string?
PyObject* UICaption::get_text(PyUICaptionObject* self, void* closure)
{
Resources::caption_buffer = self->data->text.getString();
return PyUnicode_FromString(Resources::caption_buffer.c_str());
}
int UICaption::set_text(PyUICaptionObject* self, PyObject* value, void* closure)
{
PyObject* s = PyObject_Str(value);
PyObject * temp_bytes = PyUnicode_AsEncodedString(s, "UTF-8", "strict"); // Owned reference
if (temp_bytes != NULL) {
Resources::caption_buffer = PyBytes_AS_STRING(temp_bytes); // Borrowed pointer
Py_DECREF(temp_bytes);
}
self->data->text.setString(Resources::caption_buffer);
return 0;
}
PyGetSetDef UICaption::getsetters[] = {
{"x", (getter)UICaption::get_float_member, (setter)UICaption::set_float_member, "X coordinate of top-left corner", (void*)0},
{"y", (getter)UICaption::get_float_member, (setter)UICaption::set_float_member, "Y coordinate of top-left corner", (void*)1},
{"pos", (getter)UICaption::get_vec_member, (setter)UICaption::set_vec_member, "(x, y) vector", (void*)0},
//{"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2},
//{"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3},
{"outline", (getter)UICaption::get_float_member, (setter)UICaption::set_float_member, "Thickness of the border", (void*)4},
{"fill_color", (getter)UICaption::get_color_member, (setter)UICaption::set_color_member, "Fill color of the text", (void*)0},
{"outline_color", (getter)UICaption::get_color_member, (setter)UICaption::set_color_member, "Outline color of the text", (void*)1},
//{"children", (getter)PyUIFrame_get_children, NULL, "UICollection of objects on top of this one", NULL},
{"text", (getter)UICaption::get_text, (setter)UICaption::set_text, "The text displayed", NULL},
{"size", (getter)UICaption::get_float_member, (setter)UICaption::set_float_member, "Text size (integer) in points", (void*)5},
{"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UICAPTION},
{NULL}
};
PyObject* UICaption::repr(PyUICaptionObject* self)
{
std::ostringstream ss;
if (!self->data) ss << "<Caption (invalid internal object)>";
else {
auto text = self->data->text;
auto fc = text.getFillColor();
auto oc = text.getOutlineColor();
ss << "<Caption (x=" << text.getPosition().x << ", y=" << text.getPosition().y << ", " <<
"text='" << (std::string)text.getString() << "', " <<
"outline=" << text.getOutlineThickness() << ", " <<
"fill_color=(" << (int)fc.r << ", " << (int)fc.g << ", " << (int)fc.b << ", " << (int)fc.a <<"), " <<
"outline_color=(" << (int)oc.r << ", " << (int)oc.g << ", " << (int)oc.b << ", " << (int)oc.a <<"), " <<
")>";
}
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
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;
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))
{
return -1;
}
// check types for font, fill_color, outline_color
std::cout << PyUnicode_AsUTF8(PyObject_Repr(font)) << std::endl;
if (font != NULL && !PyObject_IsInstance(font, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Font")/*(PyObject*)&PyFontType)*/)){
PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance");
return -1;
} else if (font != NULL)
{
auto font_obj = (PyFontObject*)font;
self->data->text.setFont(font_obj->data->font);
self->font = font;
Py_INCREF(font);
} else
{
// default font
//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) {
auto fc = PyColor::from_arg(fill_color);
if (!fc) {
PyErr_SetString(PyExc_TypeError, "fill_color must be mcrfpy.Color or arguments to mcrfpy.Color.__init__");
return -1;
}
self->data->text.setFillColor(PyColor::fromPy(fc));
//Py_DECREF(fc);
} else {
self->data->text.setFillColor(sf::Color(0,0,0,255));
}
if (outline_color) {
auto oc = PyColor::from_arg(outline_color);
if (!oc) {
PyErr_SetString(PyExc_TypeError, "outline_color must be mcrfpy.Color or arguments to mcrfpy.Color.__init__");
return -1;
}
self->data->text.setOutlineColor(PyColor::fromPy(oc));
//Py_DECREF(oc);
} else {
self->data->text.setOutlineColor(sf::Color(128,128,128,255));
}
return 0;
}

62
src/UICaption.h Normal file
View File

@ -0,0 +1,62 @@
#pragma once
#include "Common.h"
#include "Python.h"
#include "UIDrawable.h"
class UICaption: public UIDrawable
{
public:
sf::Text text;
void render(sf::Vector2f) override final;
PyObjectsEnum derived_type() override final;
virtual UIDrawable* click_at(sf::Vector2f point) override final;
static PyObject* get_float_member(PyUICaptionObject* self, void* closure);
static int set_float_member(PyUICaptionObject* self, PyObject* value, void* closure);
static PyObject* get_vec_member(PyUICaptionObject* self, void* closure);
static int set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure);
static PyObject* get_color_member(PyUICaptionObject* self, void* closure);
static int set_color_member(PyUICaptionObject* self, PyObject* value, void* closure);
static PyObject* get_text(PyUICaptionObject* self, void* closure);
static int set_text(PyUICaptionObject* self, PyObject* value, void* closure);
static PyGetSetDef getsetters[];
static PyObject* repr(PyUICaptionObject* self);
static int init(PyUICaptionObject* self, PyObject* args, PyObject* kwds);
};
namespace mcrfpydef {
static PyTypeObject PyUICaptionType = {
.tp_name = "mcrfpy.Caption",
.tp_basicsize = sizeof(PyUICaptionObject),
.tp_itemsize = 0,
// TODO - move tp_dealloc to .cpp file as static function (UICaption::dealloc)
.tp_dealloc = (destructor)[](PyObject* self)
{
PyUICaptionObject* obj = (PyUICaptionObject*)self;
// TODO - reevaluate with PyFont usage; UICaption does not own the font
// release reference to font object
if (obj->font) Py_DECREF(obj->font);
obj->data.reset();
Py_TYPE(self)->tp_free(self);
},
.tp_repr = (reprfunc)UICaption::repr,
//.tp_hash = NULL,
//.tp_iter
//.tp_iternext
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("docstring"),
//.tp_methods = PyUIFrame_methods,
//.tp_members = PyUIFrame_members,
.tp_getset = UICaption::getsetters,
//.tp_base = NULL,
.tp_init = (initproc)UICaption::init,
// TODO - move tp_new to .cpp file as a static function (UICaption::new)
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
{
PyUICaptionObject* self = (PyUICaptionObject*)type->tp_alloc(type, 0);
if (self) self->data = std::make_shared<UICaption>();
return (PyObject*)self;
}
};
}

203
src/UICollection.cpp Normal file
View File

@ -0,0 +1,203 @@
#include "UICollection.h"
#include "UIFrame.h"
#include "UICaption.h"
#include "UISprite.h"
#include "UIGrid.h"
#include "McRFPy_API.h"
using namespace mcrfpydef;
int UICollectionIter::init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds)
{
PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required.");
return -1;
}
PyObject* UICollectionIter::next(PyUICollectionIterObject* self)
{
if (self->data->size() != self->start_size)
{
PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration");
return NULL;
}
if (self->index > self->start_size - 1)
{
PyErr_SetNone(PyExc_StopIteration);
return NULL;
}
self->index++;
auto vec = self->data.get();
if (!vec)
{
PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer");
return NULL;
}
auto target = (*vec)[self->index-1];
// TODO build PyObject* of the correct UIDrawable subclass to return
//return py_instance(target);
return NULL;
}
PyObject* UICollectionIter::repr(PyUICollectionIterObject* self)
{
std::ostringstream ss;
if (!self->data) ss << "<UICollectionIter (invalid internal object)>";
else {
ss << "<UICollectionIter (" << self->data->size() << " child objects, @ index " << self->index << ")>";
}
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
Py_ssize_t UICollection::len(PyUICollectionObject* self) {
return self->data->size();
}
PyObject* UICollection::getitem(PyUICollectionObject* self, Py_ssize_t index) {
// build a Python version of item at self->data[index]
// Copy pasted::
auto vec = self->data.get();
if (!vec)
{
PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer");
return NULL;
}
while (index < 0) index += self->data->size();
if (index > self->data->size() - 1)
{
PyErr_SetString(PyExc_IndexError, "UICollection index out of range");
return NULL;
}
auto target = (*vec)[index];
RET_PY_INSTANCE(target);
return NULL;
}
PySequenceMethods UICollection::sqmethods = {
.sq_length = (lenfunc)UICollection::len,
.sq_item = (ssizeargfunc)UICollection::getitem,
//.sq_item_by_index = PyUICollection_getitem
//.sq_slice - return a subset of the iterable
//.sq_ass_item - called when `o[x] = y` is executed (x is any object type)
//.sq_ass_slice - cool; no thanks, for now
//.sq_contains - called when `x in o` is executed
//.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer)
};
/* Idiomatic way to fetch complete types from the API rather than referencing their PyTypeObject struct
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture");
I never identified why `using namespace mcrfpydef;` doesn't solve the segfault issue.
The horrible macro in UIDrawable was originally a workaround for this, but as I interact with the types outside of the monster UI.h, a more general (and less icky) solution is required.
*/
PyObject* UICollection::append(PyUICollectionObject* self, PyObject* o)
{
// if not UIDrawable subclass, reject it
// self->data->push_back( c++ object inside o );
// this would be a great use case for .tp_base
if (!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) &&
!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) &&
!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) &&
!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))
)
{
PyErr_SetString(PyExc_TypeError, "Only Frame, Caption, Sprite, and Grid objects can be added to UICollection");
return NULL;
}
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")))
{
PyUIFrameObject* frame = (PyUIFrameObject*)o;
self->data->push_back(frame->data);
}
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")))
{
PyUICaptionObject* caption = (PyUICaptionObject*)o;
self->data->push_back(caption->data);
}
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")))
{
PyUISpriteObject* sprite = (PyUISpriteObject*)o;
self->data->push_back(sprite->data);
}
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid")))
{
PyUIGridObject* grid = (PyUIGridObject*)o;
self->data->push_back(grid->data);
}
Py_INCREF(Py_None);
return Py_None;
}
PyObject* UICollection::remove(PyUICollectionObject* self, PyObject* o)
{
if (!PyLong_Check(o))
{
PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove");
return NULL;
}
long index = PyLong_AsLong(o);
if (index >= self->data->size())
{
PyErr_SetString(PyExc_ValueError, "Index out of range");
return NULL;
}
else if (index < 0)
{
PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented.");
return NULL;
}
// release the shared pointer at self->data[index];
self->data->erase(self->data->begin() + index);
Py_INCREF(Py_None);
return Py_None;
}
PyMethodDef UICollection::methods[] = {
{"append", (PyCFunction)UICollection::append, METH_O},
//{"extend", (PyCFunction)PyUICollection_extend, METH_O}, // TODO
{"remove", (PyCFunction)UICollection::remove, METH_O},
{NULL, NULL, 0, NULL}
};
PyObject* UICollection::repr(PyUICollectionObject* self)
{
std::ostringstream ss;
if (!self->data) ss << "<UICollection (invalid internal object)>";
else {
ss << "<UICollection (" << self->data->size() << " child objects)>";
}
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
int UICollection::init(PyUICollectionObject* self, PyObject* args, PyObject* kwds)
{
PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required.");
return -1;
}
PyObject* UICollection::iter(PyUICollectionObject* self)
{
PyUICollectionIterObject* iterObj;
iterObj = (PyUICollectionIterObject*)PyUICollectionIterType.tp_alloc(&PyUICollectionIterType, 0);
if (iterObj == NULL) {
return NULL; // Failed to allocate memory for the iterator object
}
iterObj->data = self->data;
iterObj->index = 0;
iterObj->start_size = self->data->size();
return (PyObject*)iterObj;
}

88
src/UICollection.h Normal file
View File

@ -0,0 +1,88 @@
#pragma once
#include "Common.h"
#include "Python.h"
#include "UIDrawable.h"
class UICollectionIter
{
// really more of a namespace: all the members are public and static. But being consistent with other UI objects
public:
static int init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds);
static PyObject* next(PyUICollectionIterObject* self);
static PyObject* repr(PyUICollectionIterObject* self);
};
class UICollection
{
// really more of a namespace: all the members are public and static. But being consistent with other UI objects
public:
static Py_ssize_t len(PyUICollectionObject* self);
static PyObject* getitem(PyUICollectionObject* self, Py_ssize_t index);
static PySequenceMethods sqmethods;
static PyObject* append(PyUICollectionObject* self, PyObject* o);
static PyObject* remove(PyUICollectionObject* self, PyObject* o);
static PyMethodDef methods[];
static PyObject* repr(PyUICollectionObject* self);
static int init(PyUICollectionObject* self, PyObject* args, PyObject* kwds);
static PyObject* iter(PyUICollectionObject* self);
};
namespace mcrfpydef {
static PyTypeObject PyUICollectionIterType = {
//PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mcrfpy.UICollectionIter",
.tp_basicsize = sizeof(PyUICollectionIterObject),
.tp_itemsize = 0,
//TODO - as static method, not inline lambda def, please
.tp_dealloc = (destructor)[](PyObject* self)
{
PyUICollectionIterObject* obj = (PyUICollectionIterObject*)self;
obj->data.reset();
Py_TYPE(self)->tp_free(self);
},
.tp_repr = (reprfunc)UICollectionIter::repr,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("Iterator for a collection of UI objects"),
.tp_iternext = (iternextfunc)UICollectionIter::next,
//.tp_getset = PyUICollection_getset,
.tp_init = (initproc)UICollectionIter::init, // just raise an exception
//TODO - as static method, not inline lambda def, please
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
{
PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required.");
return NULL;
}
};
static PyTypeObject PyUICollectionType = {
//PyVarObject_/HEAD_INIT(NULL, 0)
.tp_name = "mcrfpy.UICollection",
.tp_basicsize = sizeof(PyUICollectionObject),
.tp_itemsize = 0,
//TODO - as static method, not inline lambda def, please
.tp_dealloc = (destructor)[](PyObject* self)
{
PyUICollectionObject* obj = (PyUICollectionObject*)self;
obj->data.reset();
Py_TYPE(self)->tp_free(self);
},
.tp_repr = (reprfunc)UICollection::repr,
.tp_as_sequence = &UICollection::sqmethods,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("Iterable, indexable collection of UI objects"),
.tp_iter = (getiterfunc)UICollection::iter,
.tp_methods = UICollection::methods, // append, remove
//.tp_getset = PyUICollection_getset,
.tp_init = (initproc)UICollection::init, // just raise an exception
//TODO - as static method, not inline lambda def, please
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
{
// Does PyUICollectionType need __new__ if it's not supposed to be instantiable by the user?
// Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker?
PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required.");
return NULL;
}
};
}

81
src/UIDrawable.cpp Normal file
View File

@ -0,0 +1,81 @@
#include "UIDrawable.h"
#include "UIFrame.h"
#include "UICaption.h"
#include "UISprite.h"
#include "UIGrid.h"
UIDrawable::UIDrawable() { click_callable = NULL; }
void UIDrawable::click_unregister()
{
click_callable.reset();
}
void UIDrawable::render()
{
render(sf::Vector2f());
}
PyObject* UIDrawable::get_click(PyObject* self, void* closure) {
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(closure)); // trust me bro, it's an Enum
PyObject* ptr;
switch (objtype)
{
case PyObjectsEnum::UIFRAME:
ptr = ((PyUIFrameObject*)self)->data->click_callable->borrow();
break;
case PyObjectsEnum::UICAPTION:
ptr = ((PyUICaptionObject*)self)->data->click_callable->borrow();
break;
case PyObjectsEnum::UISPRITE:
ptr = ((PyUISpriteObject*)self)->data->click_callable->borrow();
break;
case PyObjectsEnum::UIGRID:
ptr = ((PyUIGridObject*)self)->data->click_callable->borrow();
break;
default:
PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _get_click");
return NULL;
}
if (ptr && ptr != Py_None)
return ptr;
else
return Py_None;
}
int UIDrawable::set_click(PyObject* self, PyObject* value, void* closure) {
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(closure)); // trust me bro, it's an Enum
UIDrawable* target;
switch (objtype)
{
case PyObjectsEnum::UIFRAME:
target = (((PyUIFrameObject*)self)->data.get());
break;
case PyObjectsEnum::UICAPTION:
target = (((PyUICaptionObject*)self)->data.get());
break;
case PyObjectsEnum::UISPRITE:
target = (((PyUISpriteObject*)self)->data.get());
break;
case PyObjectsEnum::UIGRID:
target = (((PyUIGridObject*)self)->data.get());
break;
default:
PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _set_click");
return -1;
}
if (value == Py_None)
{
target->click_unregister();
} else {
target->click_register(value);
}
return 0;
}
void UIDrawable::click_register(PyObject* callable)
{
click_callable = std::make_unique<PyClickCallable>(callable);
}

176
src/UIDrawable.h Normal file
View File

@ -0,0 +1,176 @@
#pragma once
#include "Common.h"
#include "Python.h"
#include "structmember.h"
#include "IndexTexture.h"
#include "Resources.h"
#include <list>
#include "PyCallable.h"
#include "PyTexture.h"
#include "PyColor.h"
#include "PyVector.h"
#include "PyFont.h"
#include "Resources.h"
#include "UIBase.h"
class UIFrame; class UICaption; class UISprite; class UIEntity; class UIGrid;
enum PyObjectsEnum : int
{
UIFRAME = 1,
UICAPTION,
UISPRITE,
UIGRID
};
class UIDrawable
{
public:
void render();
virtual void render(sf::Vector2f) = 0;
virtual PyObjectsEnum derived_type() = 0;
// Mouse input handling - callable object, methods to find event's destination
std::unique_ptr<PyClickCallable> click_callable;
virtual UIDrawable* click_at(sf::Vector2f point) = 0;
void click_register(PyObject*);
void click_unregister();
UIDrawable();
static PyObject* get_click(PyObject* self, void* closure);
static int set_click(PyObject* self, PyObject* value, void* closure);
};
typedef struct {
PyObject_HEAD
std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>> data;
} PyUICollectionObject;
typedef struct {
PyObject_HEAD
std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>> data;
int index;
int start_size;
} PyUICollectionIterObject;
namespace mcrfpydef {
//PyObject* py_instance(std::shared_ptr<UIDrawable> source);
// This function segfaults on tp_alloc for an unknown reason, but works inline with mcrfpydef:: methods.
#define RET_PY_INSTANCE(target) { \
switch (target->derived_type()) \
{ \
case PyObjectsEnum::UIFRAME: \
{ \
PyUIFrameObject* o = (PyUIFrameObject*)((&PyUIFrameType)->tp_alloc(&PyUIFrameType, 0)); \
if (o) \
{ \
auto p = std::static_pointer_cast<UIFrame>(target); \
o->data = p; \
auto utarget = o->data; \
} \
return (PyObject*)o; \
} \
case PyObjectsEnum::UICAPTION: \
{ \
PyUICaptionObject* o = (PyUICaptionObject*)((&PyUICaptionType)->tp_alloc(&PyUICaptionType, 0)); \
if (o) \
{ \
auto p = std::static_pointer_cast<UICaption>(target); \
o->data = p; \
auto utarget = o->data; \
} \
return (PyObject*)o; \
} \
case PyObjectsEnum::UISPRITE: \
{ \
PyUISpriteObject* o = (PyUISpriteObject*)((&PyUISpriteType)->tp_alloc(&PyUISpriteType, 0)); \
if (o) \
{ \
auto p = std::static_pointer_cast<UISprite>(target); \
o->data = p; \
auto utarget = o->data; \
} \
return (PyObject*)o; \
} \
case PyObjectsEnum::UIGRID: \
{ \
PyUIGridObject* o = (PyUIGridObject*)((&PyUIGridType)->tp_alloc(&PyUIGridType, 0)); \
if (o) \
{ \
auto p = std::static_pointer_cast<UIGrid>(target); \
o->data = p; \
auto utarget = o->data; \
} \
return (PyObject*)o; \
} \
} \
}
// end macro definition
//TODO: add this method to class scope; move implementation to .cpp file
/*
static PyObject* PyUIDrawable_get_click(PyObject* self, void* closure) {
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(closure)); // trust me bro, it's an Enum
PyObject* ptr;
switch (objtype)
{
case PyObjectsEnum::UIFRAME:
ptr = ((PyUIFrameObject*)self)->data->click_callable->borrow();
break;
case PyObjectsEnum::UICAPTION:
ptr = ((PyUICaptionObject*)self)->data->click_callable->borrow();
break;
case PyObjectsEnum::UISPRITE:
ptr = ((PyUISpriteObject*)self)->data->click_callable->borrow();
break;
case PyObjectsEnum::UIGRID:
ptr = ((PyUIGridObject*)self)->data->click_callable->borrow();
break;
default:
PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _get_click");
return NULL;
}
if (ptr && ptr != Py_None)
return ptr;
else
return Py_None;
}*/
//TODO: add this method to class scope; move implementation to .cpp file
/*
static int PyUIDrawable_set_click(PyObject* self, PyObject* value, void* closure) {
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(closure)); // trust me bro, it's an Enum
UIDrawable* target;
switch (objtype)
{
case PyObjectsEnum::UIFRAME:
target = (((PyUIFrameObject*)self)->data.get());
break;
case PyObjectsEnum::UICAPTION:
target = (((PyUICaptionObject*)self)->data.get());
break;
case PyObjectsEnum::UISPRITE:
target = (((PyUISpriteObject*)self)->data.get());
break;
case PyObjectsEnum::UIGRID:
target = (((PyUIGridObject*)self)->data.get());
break;
default:
PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _set_click");
return -1;
}
if (value == Py_None)
{
target->click_unregister();
} else {
target->click_register(value);
}
return 0;
}
*/
}

177
src/UIEntity.cpp Normal file
View File

@ -0,0 +1,177 @@
#include "UIEntity.h"
#include "UIGrid.h"
#include "McRFPy_API.h"
UIEntity::UIEntity() {} // this will not work lol. TODO remove default constructor by finding the shared pointer inits that use it
UIEntity::UIEntity(UIGrid& grid)
: gridstate(grid.grid_x * grid.grid_y)
{
}
PyObject* UIEntity::at(PyUIEntityObject* self, PyObject* o) {
int x, y;
if (!PyArg_ParseTuple(o, "ii", &x, &y)) {
PyErr_SetString(PyExc_TypeError, "UIEntity.at requires two integer arguments: (x, y)");
return NULL;
}
if (self->data->grid == NULL) {
PyErr_SetString(PyExc_ValueError, "Entity cannot access surroundings because it is not associated with a grid");
return NULL;
}
/*
PyUIGridPointStateObject* obj = (PyUIGridPointStateObject*)((&mcrfpydef::PyUIGridPointStateType)->tp_alloc(&mcrfpydef::PyUIGridPointStateType, 0));
*/
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPointState");
auto obj = (PyUIGridPointStateObject*)type->tp_alloc(type, 0);
//auto target = std::static_pointer_cast<UIEntity>(target);
obj->data = &(self->data->gridstate[y + self->data->grid->grid_x * x]);
obj->grid = self->data->grid;
obj->entity = self->data;
return (PyObject*)obj;
}
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;
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))
{
return -1;
}
// check types for texture
//
// Set Texture
//
if (texture != NULL && !PyObject_IsInstance(texture, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))){
PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance");
return -1;
} /*else if (texture != NULL) // this section needs to go; texture isn't optional and isn't managed by the UI objects anymore
{
self->texture = texture;
Py_INCREF(texture);
} else
{
// default tex?
}*/
if (grid != NULL && !PyObject_IsInstance(grid, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) {
PyErr_SetString(PyExc_TypeError, "grid must be a mcrfpy.Grid instance");
return -1;
}
auto pytexture = (PyTextureObject*)texture;
if (grid == NULL)
self->data = std::make_shared<UIEntity>();
else
self->data = std::make_shared<UIEntity>(*((PyUIGridObject*)grid)->data);
// 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);
if (grid != NULL) {
PyUIGridObject* pygrid = (PyUIGridObject*)grid;
self->data->grid = pygrid->data;
// todone - on creation of Entity with Grid assignment, also append it to the entity list
pygrid->data->entities->push_back(self->data);
}
return 0;
}
PyObject* UIEntity::get_spritenumber(PyUIEntityObject* self, void* closure) {
return PyLong_FromDouble(self->data->sprite.getSpriteIndex());
}
PyObject* sfVector2f_to_PyObject(sf::Vector2f vector) {
return Py_BuildValue("(ff)", vector.x, vector.y);
}
sf::Vector2f PyObject_to_sfVector2f(PyObject* obj) {
float x, y;
if (!PyArg_ParseTuple(obj, "ff", &x, &y)) {
return sf::Vector2f(); // TODO / reconsider this default: Return default vector on parse error
}
return sf::Vector2f(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"));
}
PyObject* UIGridPointStateVector_to_PyList(const std::vector<UIGridPointState>& vec) {
PyObject* list = PyList_New(vec.size());
if (!list) return PyErr_NoMemory();
for (size_t i = 0; i < vec.size(); ++i) {
PyObject* obj = UIGridPointState_to_PyObject(vec[i]);
if (!obj) { // Cleanup on failure
Py_DECREF(list);
return NULL;
}
PyList_SET_ITEM(list, i, obj); // This steals a reference to obj
}
return list;
}
PyObject* UIEntity::get_position(PyUIEntityObject* self, void* closure) {
return sfVector2f_to_PyObject(self->data->position);
}
int UIEntity::set_position(PyUIEntityObject* self, PyObject* value, void* closure) {
self->data->position = PyObject_to_sfVector2f(value);
return 0;
}
PyObject* UIEntity::get_gridstate(PyUIEntityObject* self, void* closure) {
// Assuming a function to convert std::vector<UIGridPointState> to PyObject* list
return UIGridPointStateVector_to_PyList(self->data->gridstate);
}
int UIEntity::set_spritenumber(PyUIEntityObject* self, PyObject* value, void* closure) {
int val;
if (PyLong_Check(value))
val = PyLong_AsLong(value);
else
{
PyErr_SetString(PyExc_TypeError, "Value must be an integer.");
return -1;
}
//self->data->sprite.sprite_index = val;
self->data->sprite.setSpriteIndex(val); // todone - I don't like ".sprite.sprite" in this stack of UIEntity.UISprite.sf::Sprite
return 0;
}
PyMethodDef UIEntity::methods[] = {
{"at", (PyCFunction)UIEntity::at, METH_O},
{NULL, NULL, 0, NULL}
};
PyGetSetDef UIEntity::getsetters[] = {
{"position", (getter)UIEntity::get_position, (setter)UIEntity::set_position, "Entity position", NULL},
{"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 */
};
PyObject* UIEntity::repr(PyUIEntityObject* self) {
std::ostringstream ss;
if (!self->data) ss << "<Entity (invalid internal object)>";
else {
auto ent = self->data;
ss << "<Entity (x=" << self->data->position.x << ", y=" << self->data->position.y << ", sprite_number=" << self->data->sprite.getSpriteIndex() <<
")>";
}
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}

75
src/UIEntity.h Normal file
View File

@ -0,0 +1,75 @@
#pragma once
#include "Common.h"
#include "Python.h"
#include "structmember.h"
#include "IndexTexture.h"
#include "Resources.h"
#include <list>
#include "PyCallable.h"
#include "PyTexture.h"
#include "PyColor.h"
#include "PyVector.h"
#include "PyFont.h"
#include "UIGridPoint.h"
#include "UIDrawable.h"
#include "UIBase.h"
#include "UISprite.h"
class UIGrid;
//class UIEntity;
//typedef struct {
// PyObject_HEAD
// std::shared_ptr<UIEntity> data;
//} PyUIEntityObject;
// helper methods with no namespace requirement
static PyObject* sfVector2f_to_PyObject(sf::Vector2f vector);
static sf::Vector2f PyObject_to_sfVector2f(PyObject* obj);
static PyObject* UIGridPointState_to_PyObject(const UIGridPointState& state);
static PyObject* UIGridPointStateVector_to_PyList(const std::vector<UIGridPointState>& vec);
// TODO: make UIEntity a drawable
class UIEntity//: public UIDrawable
{
public:
//PyObject* self;
std::shared_ptr<UIGrid> grid;
std::vector<UIGridPointState> gridstate;
UISprite sprite;
sf::Vector2f position; //(x,y) in grid coordinates; float for animation
void render(sf::Vector2f); //override final;
UIEntity();
UIEntity(UIGrid&);
static PyObject* at(PyUIEntityObject* self, PyObject* o);
static int init(PyUIEntityObject* self, PyObject* args, PyObject* kwds);
static PyObject* get_position(PyUIEntityObject* self, void* closure);
static int set_position(PyUIEntityObject* self, PyObject* value, void* closure);
static PyObject* get_gridstate(PyUIEntityObject* self, void* closure);
static PyObject* get_spritenumber(PyUIEntityObject* self, void* closure);
static int set_spritenumber(PyUIEntityObject* self, PyObject* value, void* closure);
static PyMethodDef methods[];
static PyGetSetDef getsetters[];
static PyObject* repr(PyUIEntityObject* self);
};
namespace mcrfpydef {
static PyTypeObject PyUIEntityType = {
//PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mcrfpy.Entity",
.tp_basicsize = sizeof(PyUIEntityObject),
.tp_itemsize = 0,
.tp_repr = (reprfunc)UIEntity::repr,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "UIEntity objects",
.tp_methods = UIEntity::methods,
.tp_getset = UIEntity::getsetters,
.tp_init = (initproc)UIEntity::init,
.tp_new = PyType_GenericNew,
};
}

265
src/UIFrame.cpp Normal file
View File

@ -0,0 +1,265 @@
#include "UIFrame.h"
#include "UICollection.h"
#include "GameEngine.h"
UIDrawable* UIFrame::click_at(sf::Vector2f point)
{
for (auto e: *children)
{
auto p = e->click_at(point + box.getPosition());
if (p)
return p;
}
if (click_callable)
{
float x = box.getPosition().x, y = box.getPosition().y, w = box.getSize().x, h = box.getSize().y;
if (point.x > x && point.y > y && point.x < x+w && point.y < y+h) return this;
}
return NULL;
}
UIFrame::UIFrame()
: outline(0)
{
children = std::make_shared<std::vector<std::shared_ptr<UIDrawable>>>();
box.setPosition(0, 0);
box.setSize(sf::Vector2f(0, 0));
}
UIFrame::UIFrame(float _x, float _y, float _w, float _h)
: outline(0)
{
box.setPosition(_x, _y);
box.setSize(sf::Vector2f(_w, _h));
children = std::make_shared<std::vector<std::shared_ptr<UIDrawable>>>();
}
UIFrame::~UIFrame()
{
children.reset();
}
PyObjectsEnum UIFrame::derived_type()
{
return PyObjectsEnum::UIFRAME;
}
void UIFrame::render(sf::Vector2f offset)
{
box.move(offset);
Resources::game->getWindow().draw(box);
box.move(-offset);
for (auto drawable : *children) {
drawable->render(offset + box.getPosition());
}
}
PyObject* UIFrame::get_children(PyUIFrameObject* self, void* closure)
{
// create PyUICollection instance pointing to self->data->children
//PyUICollectionObject* o = (PyUICollectionObject*)mcrfpydef::PyUICollectionType.tp_alloc(&mcrfpydef::PyUICollectionType, 0);
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "UICollection");
auto o = (PyUICollectionObject*)type->tp_alloc(type, 0);
if (o)
o->data = self->data->children;
return (PyObject*)o;
}
PyObject* UIFrame::get_float_member(PyUIFrameObject* self, void* closure)
{
auto member_ptr = reinterpret_cast<long>(closure);
if (member_ptr == 0)
return PyFloat_FromDouble(self->data->box.getPosition().x);
else if (member_ptr == 1)
return PyFloat_FromDouble(self->data->box.getPosition().y);
else if (member_ptr == 2)
return PyFloat_FromDouble(self->data->box.getSize().x);
else if (member_ptr == 3)
return PyFloat_FromDouble(self->data->box.getSize().y);
else if (member_ptr == 4)
return PyFloat_FromDouble(self->data->box.getOutlineThickness());
else
{
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
return nullptr;
}
}
int UIFrame::set_float_member(PyUIFrameObject* self, PyObject* value, void* closure)
{
float val;
auto member_ptr = reinterpret_cast<long>(closure);
if (PyFloat_Check(value))
{
val = PyFloat_AsDouble(value);
}
else if (PyLong_Check(value))
{
val = PyLong_AsLong(value);
}
else
{
PyErr_SetString(PyExc_TypeError, "Value must be an integer.");
return -1;
}
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) //outline
self->data->box.setOutlineThickness(val);
return 0;
}
PyObject* UIFrame::get_color_member(PyUIFrameObject* self, void* closure)
{
// validate closure (should be impossible to be wrong, but it's thorough)
auto member_ptr = reinterpret_cast<long>(closure);
if (member_ptr != 0 && member_ptr != 1)
{
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
return nullptr;
}
//PyTypeObject* colorType = &PyColorType;
auto colorType = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color");
PyObject* pyColor = colorType->tp_alloc(colorType, 0);
if (pyColor == NULL)
{
std::cout << "failure to allocate mcrfpy.Color / PyColorType" << std::endl;
return NULL;
}
PyColorObject* pyColorObj = reinterpret_cast<PyColorObject*>(pyColor);
// fetch correct member data
sf::Color color;
if (member_ptr == 0)
{
color = self->data->box.getFillColor();
//return Py_BuildValue("(iii)", color.r, color.g, color.b);
}
else if (member_ptr == 1)
{
color = self->data->box.getOutlineColor();
//return Py_BuildValue("(iii)", color.r, color.g, color.b);
}
return PyColor(color).pyObject();
}
int UIFrame::set_color_member(PyUIFrameObject* self, PyObject* value, void* closure)
{
//TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse
auto member_ptr = reinterpret_cast<long>(closure);
int r, g, b, a;
if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color")))
{
sf::Color c = ((PyColorObject*)value)->data;
r = c.r; g = c.g; b = c.b; a = c.a;
}
else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4)
{
// reject non-Color, non-tuple value
PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object.");
return -1;
}
else // get value from tuples
{
r = PyLong_AsLong(PyTuple_GetItem(value, 0));
g = PyLong_AsLong(PyTuple_GetItem(value, 1));
b = PyLong_AsLong(PyTuple_GetItem(value, 2));
a = 255;
if (PyTuple_Size(value) == 4)
{
a = PyLong_AsLong(PyTuple_GetItem(value, 3));
}
}
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255)
{
PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255.");
return -1;
}
if (member_ptr == 0)
{
self->data->box.setFillColor(sf::Color(r, g, b, a));
}
else if (member_ptr == 1)
{
self->data->box.setOutlineColor(sf::Color(r, g, b, a));
}
else
{
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
return -1;
}
return 0;
}
PyGetSetDef UIFrame::getsetters[] = {
{"x", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "X coordinate of top-left corner", (void*)0},
{"y", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "Y coordinate of top-left corner", (void*)1},
{"w", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "width of the rectangle", (void*)2},
{"h", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "height of the rectangle", (void*)3},
{"outline", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "Thickness of the border", (void*)4},
{"fill_color", (getter)UIFrame::get_color_member, (setter)UIFrame::set_color_member, "Fill color of the rectangle", (void*)0},
{"outline_color", (getter)UIFrame::get_color_member, (setter)UIFrame::set_color_member, "Outline color of the rectangle", (void*)1},
{"children", (getter)UIFrame::get_children, NULL, "UICollection of objects on top of this one", NULL},
{"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIFRAME},
{NULL}
};
PyObject* UIFrame::repr(PyUIFrameObject* self)
{
std::ostringstream ss;
if (!self->data) ss << "<Frame (invalid internal object)>";
else {
auto box = self->data->box;
auto fc = box.getFillColor();
auto oc = box.getOutlineColor();
ss << "<Frame (x=" << box.getPosition().x << ", y=" << box.getPosition().y << ", w=" <<
box.getSize().x << ", w=" << box.getSize().y << ", " <<
"outline=" << box.getOutlineThickness() << ", " <<
"fill_color=(" << (int)fc.r << ", " << (int)fc.g << ", " << (int)fc.b << ", " << (int)fc.a <<"), " <<
"outline_color=(" << (int)oc.r << ", " << (int)oc.g << ", " << (int)oc.b << ", " << (int)oc.a <<"), " <<
self->data->children->size() << " child objects" <<
")>";
}
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
int UIFrame::init(PyUIFrameObject* self, PyObject* args, PyObject* kwds)
{
//std::cout << "Init called\n";
const char* keywords[] = { "x", "y", "w", "h", "fill_color", "outline_color", "outline", nullptr };
float x = 0.0f, y = 0.0f, w = 0.0f, h=0.0f, outline=0.0f;
PyObject* fill_color = 0;
PyObject* outline_color = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffff|OOf", const_cast<char**>(keywords), &x, &y, &w, &h, &fill_color, &outline_color, &outline))
{
return -1;
}
self->data->box.setPosition(sf::Vector2f(x, y));
self->data->box.setSize(sf::Vector2f(w, h));
self->data->box.setOutlineThickness(outline);
// getsetter abuse because I haven't standardized Color object parsing (TODO)
int err_val = 0;
if (fill_color && fill_color != Py_None) err_val = UIFrame::set_color_member(self, fill_color, (void*)0);
else self->data->box.setFillColor(sf::Color(0,0,0,255));
if (err_val) return err_val;
if (outline_color && outline_color != Py_None) err_val = UIFrame::set_color_member(self, outline_color, (void*)1);
else self->data->box.setOutlineColor(sf::Color(128,128,128,255));
if (err_val) return err_val;
return 0;
}

77
src/UIFrame.h Normal file
View File

@ -0,0 +1,77 @@
#pragma once
#include "Common.h"
#include "Python.h"
#include "structmember.h"
#include "IndexTexture.h"
#include "Resources.h"
#include <list>
#include "PyCallable.h"
#include "PyColor.h"
#include "PyVector.h"
#include "UIDrawable.h"
#include "UIBase.h"
//class UIFrame;
//
//typedef struct {
// PyObject_HEAD
// std::shared_ptr<UIFrame> data;
//} PyUIFrameObject;
class UIFrame: public UIDrawable
{
public:
UIFrame(float, float, float, float);
UIFrame();
~UIFrame();
sf::RectangleShape box;
float outline;
std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>> children;
void render(sf::Vector2f) override final;
void move(sf::Vector2f);
PyObjectsEnum derived_type() override final;
virtual UIDrawable* click_at(sf::Vector2f point) override final;
static PyObject* get_children(PyUIFrameObject* self, void* closure);
static PyObject* get_float_member(PyUIFrameObject* self, void* closure);
static int set_float_member(PyUIFrameObject* self, PyObject* value, void* closure);
static PyObject* get_color_member(PyUIFrameObject* self, void* closure);
static int set_color_member(PyUIFrameObject* self, PyObject* value, void* closure);
static PyGetSetDef getsetters[];
static PyObject* repr(PyUIFrameObject* self);
static int init(PyUIFrameObject* self, PyObject* args, PyObject* kwds);
};
namespace mcrfpydef {
static PyTypeObject PyUIFrameType = {
//PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mcrfpy.Frame",
.tp_basicsize = sizeof(PyUIFrameObject),
.tp_itemsize = 0,
.tp_dealloc = (destructor)[](PyObject* self)
{
PyUIFrameObject* obj = (PyUIFrameObject*)self;
obj->data.reset();
Py_TYPE(self)->tp_free(self);
},
.tp_repr = (reprfunc)UIFrame::repr,
//.tp_hash = NULL,
//.tp_iter
//.tp_iternext
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("docstring"),
//.tp_methods = PyUIFrame_methods,
//.tp_members = PyUIFrame_members,
.tp_getset = UIFrame::getsetters,
//.tp_base = NULL,
.tp_init = (initproc)UIFrame::init,
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
{
PyUIFrameObject* self = (PyUIFrameObject*)type->tp_alloc(type, 0);
if (self) self->data = std::make_shared<UIFrame>();
return (PyObject*)self;
}
};
}

639
src/UIGrid.cpp Normal file
View File

@ -0,0 +1,639 @@
#include "UIGrid.h"
#include "GameEngine.h"
#include "McRFPy_API.h"
UIGrid::UIGrid() {}
UIGrid::UIGrid(int gx, int gy, std::shared_ptr<PyTexture> _ptex, sf::Vector2f _xy, sf::Vector2f _wh)
: grid_x(gx), grid_y(gy),
zoom(1.0f), center_x((gx/2) * _ptex->sprite_width), center_y((gy/2) * _ptex->sprite_height),
ptex(_ptex), points(gx * gy)
{
entities = std::make_shared<std::list<std::shared_ptr<UIEntity>>>();
box.setSize(_wh);
box.setPosition(_xy);
box.setFillColor(sf::Color(0,0,0,0));
// 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
sprite = ptex->sprite(0);
output.setTextureRect(
sf::IntRect(0, 0,
box.getSize().x, box.getSize().y));
output.setPosition(box.getPosition());
// textures are upside-down inside renderTexture
output.setTexture(renderTexture.getTexture());
}
void UIGrid::update() {}
void UIGrid::render(sf::Vector2f)
{
output.setPosition(box.getPosition()); // output sprite can move; update position when drawing
// output size can change; update size when drawing
output.setTextureRect(
sf::IntRect(0, 0,
box.getSize().x, box.getSize().y));
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);
float height_sq = box.getSize().y / (ptex->sprite_height * zoom);
float left_edge = center_x_sq - (width_sq / 2.0);
float top_edge = center_y_sq - (height_sq / 2.0);
int left_spritepixels = center_x - (box.getSize().x / 2.0 / zoom);
int top_spritepixels = center_y - (box.getSize().y / 2.0 / zoom);
//sprite.setScale(sf::Vector2f(zoom, zoom));
sf::RectangleShape r; // for colors and overlays
r.setSize(sf::Vector2f(ptex->sprite_width * zoom, ptex->sprite_height * zoom));
r.setOutlineThickness(0);
int x_limit = left_edge + width_sq + 2;
if (x_limit > grid_x) x_limit = grid_x;
int y_limit = top_edge + height_sq + 2;
if (y_limit > grid_y) y_limit = grid_y;
// base layer - bottom color, tile sprite ("ground")
for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0);
x < x_limit; //x < view_width;
x+=1)
{
//for (float y = (top_edge >= 0 ? top_edge : 0);
for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0);
y < y_limit; //y < view_height;
y+=1)
{
auto pixel_pos = sf::Vector2f(
(x*ptex->sprite_width - left_spritepixels) * zoom,
(y*ptex->sprite_height - top_spritepixels) * zoom );
auto gridpoint = at(std::floor(x), std::floor(y));
//sprite.setPosition(pixel_pos);
r.setPosition(pixel_pos);
r.setFillColor(gridpoint.color);
renderTexture.draw(r);
// tilesprite
// if discovered but not visible, set opacity to 90%
// if not discovered... just don't draw it?
if (gridpoint.tilesprite != -1) {
sprite = ptex->sprite(gridpoint.tilesprite, pixel_pos, sf::Vector2f(zoom, zoom)); //setSprite(gridpoint.tilesprite);;
renderTexture.draw(sprite);
}
}
}
// middle layer - entities
// disabling entity rendering until I can render their UISprite inside the rendertexture (not directly to window)
for (auto e : *entities) {
// TODO skip out-of-bounds entities (grid square not visible at all, check for partially on visible grid squares / floating point grid position)
//auto drawent = e->cGrid->indexsprite.drawable();
auto& drawent = e->sprite;
//drawent.setScale(zoom, zoom);
drawent.setScale(sf::Vector2f(zoom, zoom));
auto pixel_pos = sf::Vector2f(
(e->position.x*ptex->sprite_width - left_spritepixels) * zoom,
(e->position.y*ptex->sprite_height - top_spritepixels) * zoom );
//drawent.setPosition(pixel_pos);
//renderTexture.draw(drawent);
drawent.render(pixel_pos, renderTexture);
}
// top layer - opacity for discovered / visible status (debug, basically)
/* // Disabled until I attach a "perspective"
for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0);
x < x_limit; //x < view_width;
x+=1)
{
//for (float y = (top_edge >= 0 ? top_edge : 0);
for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0);
y < y_limit; //y < view_height;
y+=1)
{
auto pixel_pos = sf::Vector2f(
(x*itex->grid_size - left_spritepixels) * zoom,
(y*itex->grid_size - top_spritepixels) * zoom );
auto gridpoint = at(std::floor(x), std::floor(y));
sprite.setPosition(pixel_pos);
r.setPosition(pixel_pos);
// visible & discovered layers for testing purposes
if (!gridpoint.discovered) {
r.setFillColor(sf::Color(16, 16, 20, 192)); // 255 opacity for actual blackout
renderTexture.draw(r);
} else if (!gridpoint.visible) {
r.setFillColor(sf::Color(32, 32, 40, 128));
renderTexture.draw(r);
}
// overlay
// uisprite
}
}
*/
// grid lines for testing & validation
/*
sf::Vertex line[] =
{
sf::Vertex(sf::Vector2f(0, 0), sf::Color::Red),
sf::Vertex(box.getSize(), sf::Color::Red),
};
renderTexture.draw(line, 2, sf::Lines);
sf::Vertex lineb[] =
{
sf::Vertex(sf::Vector2f(0, box.getSize().y), sf::Color::Blue),
sf::Vertex(sf::Vector2f(box.getSize().x, 0), sf::Color::Blue),
};
renderTexture.draw(lineb, 2, sf::Lines);
*/
// render to window
renderTexture.display();
Resources::game->getWindow().draw(output);
}
UIGridPoint& UIGrid::at(int x, int y)
{
return points[y * grid_x + x];
}
PyObjectsEnum UIGrid::derived_type()
{
return PyObjectsEnum::UIGRID;
}
std::shared_ptr<PyTexture> UIGrid::getTexture()
{
return ptex;
}
UIDrawable* UIGrid::click_at(sf::Vector2f point)
{
if (click_callable)
{
if(box.getGlobalBounds().contains(point)) return this;
}
return NULL;
}
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;
if (!PyArg_ParseTuple(args, "iiOffff", &grid_x, &grid_y, &textureObj, &box_x, &box_y, &box_w, &box_h)) {
return -1; // If parsing fails, return an error
}
// Convert PyObject texture to IndexTexture*
// This requires the texture object to have been initialized similar to UISprite's texture handling
//if (!PyObject_IsInstance(textureObj, (PyObject*)&PyTextureType)) {
if (!PyObject_IsInstance(textureObj, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))) {
PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance");
return -1;
}
PyTextureObject* pyTexture = reinterpret_cast<PyTextureObject*>(textureObj);
// TODO (7DRL day 2, item 4.) use shared_ptr / PyTextureObject on UIGrid
//IndexTexture* texture = pyTexture->data.get();
// 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));
return 0; // Success
}
PyObject* UIGrid::get_grid_size(PyUIGridObject* self, void* closure) {
return Py_BuildValue("(ii)", self->data->grid_x, self->data->grid_y);
}
PyObject* UIGrid::get_position(PyUIGridObject* self, void* closure) {
auto& box = self->data->box;
return Py_BuildValue("(ff)", box.getPosition().x, box.getPosition().y);
}
int UIGrid::set_position(PyUIGridObject* self, PyObject* value, void* closure) {
float x, y;
if (!PyArg_ParseTuple(value, "ff", &x, &y)) {
PyErr_SetString(PyExc_ValueError, "Position must be a tuple of two floats");
return -1;
}
self->data->box.setPosition(x, y);
return 0;
}
PyObject* UIGrid::get_size(PyUIGridObject* self, void* closure) {
auto& box = self->data->box;
return Py_BuildValue("(ff)", box.getSize().x, box.getSize().y);
}
int UIGrid::set_size(PyUIGridObject* self, PyObject* value, void* closure) {
float w, h;
if (!PyArg_ParseTuple(value, "ff", &w, &h)) {
PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats");
return -1;
}
self->data->box.setSize(sf::Vector2f(w, h));
return 0;
}
PyObject* UIGrid::get_center(PyUIGridObject* self, void* closure) {
return Py_BuildValue("(ff)", self->data->center_x, self->data->center_y);
}
int UIGrid::set_center(PyUIGridObject* self, PyObject* value, void* closure) {
float x, y;
if (!PyArg_ParseTuple(value, "ff", &x, &y)) {
PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats");
return -1;
}
self->data->center_x = x;
self->data->center_y = y;
return 0;
}
PyObject* UIGrid::get_float_member(PyUIGridObject* self, void* closure)
{
auto member_ptr = reinterpret_cast<long>(closure);
if (member_ptr == 0) // x
return PyFloat_FromDouble(self->data->box.getPosition().x);
else if (member_ptr == 1) // y
return PyFloat_FromDouble(self->data->box.getPosition().y);
else if (member_ptr == 2) // w
return PyFloat_FromDouble(self->data->box.getSize().x);
else if (member_ptr == 3) // h
return PyFloat_FromDouble(self->data->box.getSize().y);
else if (member_ptr == 4) // center_x
return PyFloat_FromDouble(self->data->center_x);
else if (member_ptr == 5) // center_y
return PyFloat_FromDouble(self->data->center_y);
else if (member_ptr == 6) // zoom
return PyFloat_FromDouble(self->data->zoom);
else
{
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
return nullptr;
}
}
int UIGrid::set_float_member(PyUIGridObject* self, PyObject* value, void* closure)
{
float val;
auto member_ptr = reinterpret_cast<long>(closure);
if (PyFloat_Check(value))
{
val = PyFloat_AsDouble(value);
}
else if (PyLong_Check(value))
{
val = PyLong_AsLong(value);
}
else
{
PyErr_SetString(PyExc_TypeError, "Value must be a floating point number.");
return -1;
}
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;
return 0;
}
// TODO (7DRL Day 2, item 5.) return Texture object
/*
PyObject* UIGrid::get_texture(PyUIGridObject* self, void* closure) {
Py_INCREF(self->texture);
return self->texture;
}
*/
PyObject* UIGrid::get_texture(PyUIGridObject* self, void* closure) {
//return self->data->getTexture()->pyObject();
// PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPointState")
//PyTextureObject* obj = (PyTextureObject*)((&PyTextureType)->tp_alloc(&PyTextureType, 0));
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture");
auto obj = (PyTextureObject*)type->tp_alloc(type, 0);
obj->data = self->data->getTexture();
return (PyObject*)obj;
}
PyObject* UIGrid::py_at(PyUIGridObject* self, PyObject* o)
{
int x, y;
if (!PyArg_ParseTuple(o, "ii", &x, &y)) {
PyErr_SetString(PyExc_TypeError, "UIGrid.at requires two integer arguments: (x, y)");
return NULL;
}
if (x < 0 || x >= self->data->grid_x) {
PyErr_SetString(PyExc_ValueError, "x value out of range (0, Grid.grid_y)");
return NULL;
}
if (y < 0 || y >= self->data->grid_y) {
PyErr_SetString(PyExc_ValueError, "y value out of range (0, Grid.grid_y)");
return NULL;
}
//PyUIGridPointObject* obj = (PyUIGridPointObject*)((&PyUIGridPointType)->tp_alloc(&PyUIGridPointType, 0));
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPoint");
auto obj = (PyUIGridPointObject*)type->tp_alloc(type, 0);
//auto target = std::static_pointer_cast<UIEntity>(target);
obj->data = &(self->data->points[x + self->data->grid_x * y]);
obj->grid = self->data;
return (PyObject*)obj;
}
PyMethodDef UIGrid::methods[] = {
{"at", (PyCFunction)UIGrid::py_at, METH_O},
{NULL, NULL, 0, NULL}
};
PyGetSetDef UIGrid::getsetters[] = {
// TODO - refactor into get_vector_member with field identifier values `(void*)n`
{"grid_size", (getter)UIGrid::get_grid_size, NULL, "Grid dimensions (grid_x, grid_y)", NULL},
{"position", (getter)UIGrid::get_position, (setter)UIGrid::set_position, "Position of the grid (x, y)", NULL},
{"size", (getter)UIGrid::get_size, (setter)UIGrid::set_size, "Size of the grid (width, height)", NULL},
{"center", (getter)UIGrid::get_center, (setter)UIGrid::set_center, "Grid coordinate at the center of the Grid's view (pan)", NULL},
{"entities", (getter)UIGrid::get_children, NULL, "EntityCollection of entities on this grid", NULL},
{"x", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "top-left corner X-coordinate", (void*)0},
{"y", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "top-left corner Y-coordinate", (void*)1},
{"w", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "visible widget width", (void*)2},
{"h", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "visible widget height", (void*)3},
{"center_x", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "center of the view X-coordinate", (void*)4},
{"center_y", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "center of the view Y-coordinate", (void*)5},
{"zoom", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "zoom factor for displaying the Grid", (void*)6},
{"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIGRID},
{"texture", (getter)UIGrid::get_texture, NULL, "Texture of the grid", NULL}, //TODO 7DRL-day2-item5
{NULL} /* Sentinel */
};
PyObject* UIGrid::get_children(PyUIGridObject* self, void* closure)
{
// create PyUICollection instance pointing to self->data->children
//PyUIEntityCollectionObject* o = (PyUIEntityCollectionObject*)PyUIEntityCollectionType.tp_alloc(&PyUIEntityCollectionType, 0);
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "EntityCollection");
auto o = (PyUIEntityCollectionObject*)type->tp_alloc(type, 0);
if (o) {
o->data = self->data->entities; // todone. / BUGFIX - entities isn't a shared pointer on UIGrid, what to do? -- I made it a sp<list<sp<UIEntity>>>
o->grid = self->data;
}
return (PyObject*)o;
}
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 {
auto grid = self->data;
auto box = grid->box;
ss << "<Grid (x=" << box.getPosition().x << ", y=" << box.getPosition().y << ", w=" << box.getSize().x << ", h=" << box.getSize().y << ", " <<
"center=(" << grid->center_x << ", " << grid->center_y << "), zoom=" << grid->zoom <<
")>";
}
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
/* // TODO standard pointer would need deleted, but I opted for a shared pointer. tp_dealloc currently not even defined in the PyTypeObject
void PyUIGrid_dealloc(PyUIGridObject* self) {
delete self->data; // Clean up the allocated UIGrid object
Py_TYPE(self)->tp_free((PyObject*)self);
}
*/
int UIEntityCollectionIter::init(PyUIEntityCollectionIterObject* self, PyObject* args, PyObject* kwds)
{
PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required.");
return -1;
}
PyObject* UIEntityCollectionIter::next(PyUIEntityCollectionIterObject* self)
{
if (self->data->size() != self->start_size)
{
PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration");
return NULL;
}
if (self->index > self->start_size - 1)
{
PyErr_SetNone(PyExc_StopIteration);
return NULL;
}
self->index++;
auto vec = self->data.get();
if (!vec)
{
PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer");
return NULL;
}
// Advance list iterator since Entities are not stored in a vector (if this code even worked)
// vectors only: //auto target = (*vec)[self->index-1];
//auto l_front = (*vec).begin();
//std::advance(l_front, self->index-1);
// TODO build PyObject* of the correct UIDrawable subclass to return
//return py_instance(target);
return NULL;
}
PyObject* UIEntityCollectionIter::repr(PyUIEntityCollectionIterObject* self)
{
std::ostringstream ss;
if (!self->data) ss << "<UICollectionIter (invalid internal object)>";
else {
ss << "<UICollectionIter (" << self->data->size() << " child objects, @ index " << self->index << ")>";
}
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
Py_ssize_t UIEntityCollection::len(PyUIEntityCollectionObject* self) {
return self->data->size();
}
PyObject* UIEntityCollection::getitem(PyUIEntityCollectionObject* self, Py_ssize_t index) {
// build a Python version of item at self->data[index]
// Copy pasted::
auto vec = self->data.get();
if (!vec)
{
PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer");
return NULL;
}
while (index < 0) index += self->data->size();
if (index > self->data->size() - 1)
{
PyErr_SetString(PyExc_IndexError, "EntityCollection index out of range");
return NULL;
}
auto l_begin = (*vec).begin();
std::advance(l_begin, index);
auto target = *l_begin; //auto target = (*vec)[index];
//RET_PY_INSTANCE(target);
// construct and return an entity object that points directly into the UIGrid's entity vector
//PyUIEntityObject* o = (PyUIEntityObject*)((&PyUIEntityType)->tp_alloc(&PyUIEntityType, 0));
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity");
auto o = (PyUIEntityObject*)type->tp_alloc(type, 0);
auto p = std::static_pointer_cast<UIEntity>(target);
o->data = p;
return (PyObject*)o;
return NULL;
}
PySequenceMethods UIEntityCollection::sqmethods = {
.sq_length = (lenfunc)UIEntityCollection::len,
.sq_item = (ssizeargfunc)UIEntityCollection::getitem,
//.sq_item_by_index = UIEntityCollection::getitem
//.sq_slice - return a subset of the iterable
//.sq_ass_item - called when `o[x] = y` is executed (x is any object type)
//.sq_ass_slice - cool; no thanks, for now
//.sq_contains - called when `x in o` is executed
//.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer)
};
PyObject* UIEntityCollection::append(PyUIEntityCollectionObject* self, PyObject* o)
{
// if not UIDrawable subclass, reject it
// self->data->push_back( c++ object inside o );
// this would be a great use case for .tp_base
//if (!PyObject_IsInstance(o, (PyObject*)&PyUIEntityType))
if (!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity")))
{
PyErr_SetString(PyExc_TypeError, "Only Entity objects can be added to EntityCollection");
return NULL;
}
PyUIEntityObject* entity = (PyUIEntityObject*)o;
self->data->push_back(entity->data);
entity->data->grid = self->grid;
Py_INCREF(Py_None);
return Py_None;
}
PyObject* UIEntityCollection::remove(PyUIEntityCollectionObject* self, PyObject* o)
{
if (!PyLong_Check(o))
{
PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove");
return NULL;
}
long index = PyLong_AsLong(o);
if (index >= self->data->size())
{
PyErr_SetString(PyExc_ValueError, "Index out of range");
return NULL;
}
else if (index < 0)
{
PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented.");
return NULL;
}
// release the shared pointer at correct part of the list
self->data->erase(std::next(self->data->begin(), index));
Py_INCREF(Py_None);
return Py_None;
}
PyMethodDef UIEntityCollection::methods[] = {
{"append", (PyCFunction)UIEntityCollection::append, METH_O},
//{"extend", (PyCFunction)UIEntityCollection::extend, METH_O}, // TODO
{"remove", (PyCFunction)UIEntityCollection::remove, METH_O},
{NULL, NULL, 0, NULL}
};
PyObject* UIEntityCollection::repr(PyUIEntityCollectionObject* self)
{
std::ostringstream ss;
if (!self->data) ss << "<UICollection (invalid internal object)>";
else {
ss << "<UICollection (" << self->data->size() << " child objects)>";
}
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
int UIEntityCollection::init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds)
{
PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required.");
return -1;
}
PyObject* UIEntityCollection::iter(PyUIEntityCollectionObject* self)
{
//PyUIEntityCollectionIterObject* iterObj;
//iterObj = (PyUIEntityCollectionIterObject*)PyUIEntityCollectionIterType.tp_alloc(&PyUIEntityCollectionIterType, 0);
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "EntityCollectionIter");
auto iterObj = (PyUIEntityCollectionIterObject*)type->tp_alloc(type, 0);
if (iterObj == NULL) {
return NULL; // Failed to allocate memory for the iterator object
}
iterObj->data = self->data;
iterObj->index = 0;
iterObj->start_size = self->data->size();
return (PyObject*)iterObj;
}

184
src/UIGrid.h Normal file
View File

@ -0,0 +1,184 @@
#pragma once
#include "Common.h"
#include "Python.h"
#include "structmember.h"
#include "IndexTexture.h"
#include "Resources.h"
#include <list>
#include "PyCallable.h"
#include "PyTexture.h"
#include "PyColor.h"
#include "PyVector.h"
#include "PyFont.h"
#include "UIGridPoint.h"
#include "UIEntity.h"
#include "UIDrawable.h"
#include "UIBase.h"
class UIGrid: public UIDrawable
{
private:
std::shared_ptr<PyTexture> ptex;
public:
UIGrid();
//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;
UIGridPoint& at(int, int);
PyObjectsEnum derived_type() override final;
//void setSprite(int);
virtual UIDrawable* click_at(sf::Vector2f point) override final;
int grid_x, grid_y;
//int grid_size; // grid sizes are implied by IndexTexture now
sf::RectangleShape box;
float center_x, center_y, zoom;
//IndexTexture* itex;
std::shared_ptr<PyTexture> getTexture();
sf::Sprite sprite, output;
sf::RenderTexture renderTexture;
std::vector<UIGridPoint> points;
std::shared_ptr<std::list<std::shared_ptr<UIEntity>>> entities;
static int init(PyUIGridObject* self, PyObject* args, PyObject* kwds);
static PyObject* get_grid_size(PyUIGridObject* self, void* closure);
static PyObject* get_position(PyUIGridObject* self, void* closure);
static int set_position(PyUIGridObject* self, PyObject* value, void* closure);
static PyObject* get_size(PyUIGridObject* self, void* closure);
static int set_size(PyUIGridObject* self, PyObject* value, void* closure);
static PyObject* get_center(PyUIGridObject* self, void* closure);
static int set_center(PyUIGridObject* self, PyObject* value, void* closure);
static PyObject* get_float_member(PyUIGridObject* self, void* closure);
static int set_float_member(PyUIGridObject* self, PyObject* value, void* closure);
static PyObject* get_texture(PyUIGridObject* self, void* closure);
static PyObject* py_at(PyUIGridObject* self, PyObject* o);
static PyMethodDef methods[];
static PyGetSetDef getsetters[];
static PyObject* get_children(PyUIGridObject* self, void* closure);
static PyObject* repr(PyUIGridObject* self);
};
typedef struct {
PyObject_HEAD
std::shared_ptr<std::list<std::shared_ptr<UIEntity>>> data;
std::shared_ptr<UIGrid> grid;
} PyUIEntityCollectionObject;
class UIEntityCollection {
public:
static PySequenceMethods sqmethods;
static PyObject* append(PyUIEntityCollectionObject* self, PyObject* o);
static PyObject* remove(PyUIEntityCollectionObject* self, PyObject* o);
static PyMethodDef methods[];
static PyObject* repr(PyUIEntityCollectionObject* self);
static int init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds);
static PyObject* iter(PyUIEntityCollectionObject* self);
static Py_ssize_t len(PyUIEntityCollectionObject* self);
static PyObject* getitem(PyUIEntityCollectionObject* self, Py_ssize_t index);
};
typedef struct {
PyObject_HEAD
std::shared_ptr<std::list<std::shared_ptr<UIEntity>>> data;
int index;
int start_size;
} PyUIEntityCollectionIterObject;
class UIEntityCollectionIter {
public:
static int init(PyUIEntityCollectionIterObject* self, PyObject* args, PyObject* kwds);
static PyObject* next(PyUIEntityCollectionIterObject* self);
static PyObject* repr(PyUIEntityCollectionIterObject* self);
static PyObject* getitem(PyUIEntityCollectionObject* self, Py_ssize_t index);
};
namespace mcrfpydef {
static PyTypeObject PyUIGridType = {
//PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mcrfpy.Grid",
.tp_basicsize = sizeof(PyUIGridObject),
.tp_itemsize = 0,
//.tp_dealloc = (destructor)[](PyObject* self)
//{
// PyUIGridObject* obj = (PyUIGridObject*)self;
// obj->data.reset();
// Py_TYPE(self)->tp_free(self);
//},
//TODO - PyUIGrid REPR def:
.tp_repr = (reprfunc)UIGrid::repr,
//.tp_hash = NULL,
//.tp_iter
//.tp_iternext
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("docstring"),
.tp_methods = UIGrid::methods,
//.tp_members = UIGrid::members,
.tp_getset = UIGrid::getsetters,
//.tp_base = NULL,
.tp_init = (initproc)UIGrid::init,
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
{
PyUIGridObject* self = (PyUIGridObject*)type->tp_alloc(type, 0);
if (self) self->data = std::make_shared<UIGrid>();
return (PyObject*)self;
}
};
static PyTypeObject PyUIEntityCollectionIterType = {
//PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mcrfpy.UICollectionIter",
.tp_basicsize = sizeof(PyUIEntityCollectionIterObject),
.tp_itemsize = 0,
.tp_dealloc = (destructor)[](PyObject* self)
{
PyUIEntityCollectionIterObject* obj = (PyUIEntityCollectionIterObject*)self;
obj->data.reset();
Py_TYPE(self)->tp_free(self);
},
.tp_repr = (reprfunc)UIEntityCollectionIter::repr,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("Iterator for a collection of UI objects"),
.tp_iternext = (iternextfunc)UIEntityCollectionIter::next,
//.tp_getset = UIEntityCollection::getset,
.tp_init = (initproc)UIEntityCollectionIter::init, // just raise an exception
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
{
PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required.");
return NULL;
}
};
static PyTypeObject PyUIEntityCollectionType = {
//PyVarObject_/HEAD_INIT(NULL, 0)
.tp_name = "mcrfpy.EntityCollection",
.tp_basicsize = sizeof(PyUIEntityCollectionObject),
.tp_itemsize = 0,
.tp_dealloc = (destructor)[](PyObject* self)
{
PyUIEntityCollectionObject* obj = (PyUIEntityCollectionObject*)self;
obj->data.reset();
Py_TYPE(self)->tp_free(self);
},
.tp_repr = (reprfunc)UIEntityCollection::repr,
.tp_as_sequence = &UIEntityCollection::sqmethods,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("Iterable, indexable collection of Entities"),
.tp_iter = (getiterfunc)UIEntityCollection::iter,
.tp_methods = UIEntityCollection::methods, // append, remove
//.tp_getset = UIEntityCollection::getset,
.tp_init = (initproc)UIEntityCollection::init, // just raise an exception
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
{
// Does PyUIEntityCollectionType need __new__ if it's not supposed to be instantiable by the user?
// Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker?
PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required.");
return NULL;
}
};
}

158
src/UIGridPoint.cpp Normal file
View File

@ -0,0 +1,158 @@
#include "UIGridPoint.h"
UIGridPoint::UIGridPoint()
: color(1.0f, 1.0f, 1.0f), color_overlay(0.0f, 0.0f, 0.0f), walkable(false), transparent(false),
tilesprite(-1), tile_overlay(-1), uisprite(-1)
{}
// Utility function to convert sf::Color to PyObject*
PyObject* sfColor_to_PyObject(sf::Color color) {
return Py_BuildValue("(iiii)", color.r, color.g, color.b, color.a);
}
// Utility function to convert PyObject* to sf::Color
sf::Color PyObject_to_sfColor(PyObject* obj) {
int r, g, b, a = 255; // Default alpha to fully opaque if not specified
if (!PyArg_ParseTuple(obj, "iii|i", &r, &g, &b, &a)) {
return sf::Color(); // Return default color on parse error
}
return sf::Color(r, g, b, a);
}
PyObject* UIGridPoint::get_color(PyUIGridPointObject* self, void* closure) {
if (reinterpret_cast<long>(closure) == 0) { // color
return sfColor_to_PyObject(self->data->color);
} else { // color_overlay
return sfColor_to_PyObject(self->data->color_overlay);
}
}
int UIGridPoint::set_color(PyUIGridPointObject* self, PyObject* value, void* closure) {
sf::Color color = PyObject_to_sfColor(value);
if (reinterpret_cast<long>(closure) == 0) { // color
self->data->color = color;
} else { // color_overlay
self->data->color_overlay = color;
}
return 0;
}
PyObject* UIGridPoint::get_bool_member(PyUIGridPointObject* self, void* closure) {
if (reinterpret_cast<long>(closure) == 0) { // walkable
return PyBool_FromLong(self->data->walkable);
} else { // transparent
return PyBool_FromLong(self->data->transparent);
}
}
int UIGridPoint::set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure) {
if (value == Py_True) {
if (reinterpret_cast<long>(closure) == 0) { // walkable
self->data->walkable = true;
} else { // transparent
self->data->transparent = true;
}
} else if (value == Py_False) {
if (reinterpret_cast<long>(closure) == 0) { // walkable
self->data->walkable = false;
} else { // transparent
self->data->transparent = false;
}
} else {
PyErr_SetString(PyExc_ValueError, "Expected a boolean value");
return -1;
}
return 0;
}
PyObject* UIGridPoint::get_int_member(PyUIGridPointObject* self, void* closure) {
switch(reinterpret_cast<long>(closure)) {
case 0: return PyLong_FromLong(self->data->tilesprite);
case 1: return PyLong_FromLong(self->data->tile_overlay);
case 2: return PyLong_FromLong(self->data->uisprite);
default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return nullptr;
}
}
int UIGridPoint::set_int_member(PyUIGridPointObject* self, PyObject* value, void* closure) {
long val = PyLong_AsLong(value);
if (PyErr_Occurred()) return -1;
switch(reinterpret_cast<long>(closure)) {
case 0: self->data->tilesprite = val; break;
case 1: self->data->tile_overlay = val; break;
case 2: self->data->uisprite = val; break;
default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return -1;
}
return 0;
}
PyGetSetDef UIGridPoint::getsetters[] = {
{"color", (getter)UIGridPoint::get_color, (setter)UIGridPoint::set_color, "GridPoint color", (void*)0},
{"color_overlay", (getter)UIGridPoint::get_color, (setter)UIGridPoint::set_color, "GridPoint color overlay", (void*)1},
{"walkable", (getter)UIGridPoint::get_bool_member, (setter)UIGridPoint::set_bool_member, "Is the GridPoint walkable", (void*)0},
{"transparent", (getter)UIGridPoint::get_bool_member, (setter)UIGridPoint::set_bool_member, "Is the GridPoint transparent", (void*)1},
{"tilesprite", (getter)UIGridPoint::get_int_member, (setter)UIGridPoint::set_int_member, "Tile sprite index", (void*)0},
{"tile_overlay", (getter)UIGridPoint::get_int_member, (setter)UIGridPoint::set_int_member, "Tile overlay sprite index", (void*)1},
{"uisprite", (getter)UIGridPoint::get_int_member, (setter)UIGridPoint::set_int_member, "UI sprite index", (void*)2},
{NULL} /* Sentinel */
};
PyObject* UIGridPoint::repr(PyUIGridPointObject* self) {
std::ostringstream ss;
if (!self->data) ss << "<GridPoint (invalid internal object)>";
else {
auto gp = self->data;
ss << "<GridPoint (walkable=" << (gp->walkable ? "True" : "False") << ", transparent=" << (gp->transparent ? "True" : "False") <<
", tilesprite=" << gp->tilesprite << ", tile_overlay=" << gp->tile_overlay << ", uisprite=" << gp->uisprite <<
")>";
}
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
PyObject* UIGridPointState::get_bool_member(PyUIGridPointStateObject* self, void* closure) {
if (reinterpret_cast<long>(closure) == 0) { // visible
return PyBool_FromLong(self->data->visible);
} else { // discovered
return PyBool_FromLong(self->data->discovered);
}
}
int UIGridPointState::set_bool_member(PyUIGridPointStateObject* self, PyObject* value, void* closure) {
if (!PyBool_Check(value)) {
PyErr_SetString(PyExc_TypeError, "Value must be a boolean");
return -1;
}
int truthValue = PyObject_IsTrue(value);
if (truthValue < 0) {
return -1; // PyObject_IsTrue returns -1 on error
}
if (reinterpret_cast<long>(closure) == 0) { // visible
self->data->visible = truthValue;
} else { // discovered
self->data->discovered = truthValue;
}
return 0;
}
PyGetSetDef UIGridPointState::getsetters[] = {
{"visible", (getter)UIGridPointState::get_bool_member, (setter)UIGridPointState::set_bool_member, "Is the GridPointState visible", (void*)0},
{"discovered", (getter)UIGridPointState::get_bool_member, (setter)UIGridPointState::set_bool_member, "Has the GridPointState been discovered", (void*)1},
{NULL} /* Sentinel */
};
PyObject* UIGridPointState::repr(PyUIGridPointStateObject* self) {
std::ostringstream ss;
if (!self->data) ss << "<GridPointState (invalid internal object)>";
else {
auto gps = self->data;
ss << "<GridPointState (visible=" << (gps->visible ? "True" : "False") << ", discovered=" << (gps->discovered ? "True" : "False") <<
")>";
}
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}

92
src/UIGridPoint.h Normal file
View File

@ -0,0 +1,92 @@
#pragma once
#include "Common.h"
#include "Python.h"
#include "structmember.h"
#include "IndexTexture.h"
#include "Resources.h"
#include <list>
#include "PyCallable.h"
#include "PyTexture.h"
#include "PyColor.h"
#include "PyVector.h"
#include "PyFont.h"
static PyObject* sfColor_to_PyObject(sf::Color color);
static sf::Color PyObject_to_sfColor(PyObject* obj);
class UIGrid;
class UIEntity;
class UIGridPoint;
class UIGridPointState;
typedef struct {
PyObject_HEAD
UIGridPoint* data;
std::shared_ptr<UIGrid> grid;
} PyUIGridPointObject;
typedef struct {
PyObject_HEAD
UIGridPointState* data;
std::shared_ptr<UIGrid> grid;
std::shared_ptr<UIEntity> entity;
} PyUIGridPointStateObject;
// UIGridPoint - revised grid data for each point
class UIGridPoint
{
public:
sf::Color color, color_overlay;
bool walkable, transparent;
int tilesprite, tile_overlay, uisprite;
UIGridPoint();
static int set_int_member(PyUIGridPointObject* self, PyObject* value, void* closure);
static PyGetSetDef getsetters[];
static PyObject* get_color(PyUIGridPointObject* self, void* closure);
static PyObject* get_int_member(PyUIGridPointObject* self, void* closure);
static int set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure);
static PyObject* get_bool_member(PyUIGridPointObject* self, void* closure);
static int set_color(PyUIGridPointObject* self, PyObject* value, void* closure);
static PyObject* repr(PyUIGridPointObject* self);
};
// UIGridPointState - entity-specific info for each cell
class UIGridPointState
{
public:
bool visible, discovered;
static PyObject* get_bool_member(PyUIGridPointStateObject* self, void* closure);
static int set_bool_member(PyUIGridPointStateObject* self, PyObject* value, void* closure);
static PyGetSetDef getsetters[];
static PyObject* repr(PyUIGridPointStateObject* self);
};
namespace mcrfpydef {
static PyTypeObject PyUIGridPointType = {
//PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mcrfpy.GridPoint",
.tp_basicsize = sizeof(PyUIGridPointObject),
.tp_itemsize = 0,
.tp_repr = (reprfunc)UIGridPoint::repr,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "UIGridPoint object",
.tp_getset = UIGridPoint::getsetters,
//.tp_init = (initproc)PyUIGridPoint_init, // TODO Define the init function
.tp_new = PyType_GenericNew,
};
static PyTypeObject PyUIGridPointStateType = {
//PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mcrfpy.GridPointState",
.tp_basicsize = sizeof(PyUIGridPointStateObject),
.tp_itemsize = 0,
.tp_repr = (reprfunc)UIGridPointState::repr,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "UIGridPointState object", // TODO: Add PyUIGridPointState tp_init
.tp_getset = UIGridPointState::getsetters,
.tp_new = PyType_GenericNew,
};
}

215
src/UISprite.cpp Normal file
View File

@ -0,0 +1,215 @@
#include "UISprite.h"
#include "GameEngine.h"
UIDrawable* UISprite::click_at(sf::Vector2f point)
{
if (click_callable)
{
if(sprite.getGlobalBounds().contains(point)) return this;
}
return NULL;
}
UISprite::UISprite() {}
UISprite::UISprite(std::shared_ptr<PyTexture> _ptex, int _sprite_index, sf::Vector2f _pos, float _scale)
: ptex(_ptex), sprite_index(_sprite_index)
{
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)
{
sprite.move(offset);
target.draw(sprite);
sprite.move(-offset);
}
void UISprite::setPosition(sf::Vector2f pos)
{
sprite.setPosition(pos);
}
void UISprite::setScale(sf::Vector2f s)
{
sprite.setScale(s);
}
void UISprite::setTexture(std::shared_ptr<PyTexture> _ptex, int _sprite_index)
{
ptex = _ptex;
if (_sprite_index != -1) // if you are changing textures, there's a good chance you need a new index too
sprite_index = _sprite_index;
sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale());
}
void UISprite::setSpriteIndex(int _sprite_index)
{
sprite_index = _sprite_index;
sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale());
}
sf::Vector2f UISprite::getScale()
{
return sprite.getScale();
}
sf::Vector2f UISprite::getPosition()
{
return sprite.getPosition();
}
std::shared_ptr<PyTexture> UISprite::getTexture()
{
return ptex;
}
int UISprite::getSpriteIndex()
{
return sprite_index;
}
PyObjectsEnum UISprite::derived_type()
{
return PyObjectsEnum::UISPRITE;
}
PyObject* UISprite::get_float_member(PyUISpriteObject* self, void* closure)
{
auto member_ptr = reinterpret_cast<long>(closure);
if (member_ptr == 0)
return PyFloat_FromDouble(self->data->getPosition().x);
else if (member_ptr == 1)
return PyFloat_FromDouble(self->data->getPosition().y);
else if (member_ptr == 2)
return PyFloat_FromDouble(self->data->getScale().x); // scale X and Y are identical, presently
else
{
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
return nullptr;
}
}
int UISprite::set_float_member(PyUISpriteObject* self, PyObject* value, void* closure)
{
float val;
auto member_ptr = reinterpret_cast<long>(closure);
if (PyFloat_Check(value))
{
val = PyFloat_AsDouble(value);
}
else if (PyLong_Check(value))
{
val = PyLong_AsLong(value);
}
else
{
PyErr_SetString(PyExc_TypeError, "Value must be a floating point number.");
return -1;
}
if (member_ptr == 0) //x
self->data->setPosition(sf::Vector2f(val, self->data->getPosition().y));
else if (member_ptr == 1) //y
self->data->setPosition(sf::Vector2f(self->data->getPosition().x, val));
else if (member_ptr == 2) // scale
self->data->setScale(sf::Vector2f(val, val));
return 0;
}
PyObject* UISprite::get_int_member(PyUISpriteObject* self, void* closure)
{
auto member_ptr = reinterpret_cast<long>(closure);
if (true) {}
else
{
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
return nullptr;
}
return PyLong_FromDouble(self->data->getSpriteIndex());
}
int UISprite::set_int_member(PyUISpriteObject* self, PyObject* value, void* closure)
{
int val;
auto member_ptr = reinterpret_cast<long>(closure);
if (PyLong_Check(value))
{
val = PyLong_AsLong(value);
}
else
{
PyErr_SetString(PyExc_TypeError, "Value must be an integer.");
return -1;
}
self->data->setSpriteIndex(val);
return 0;
}
PyObject* UISprite::get_texture(PyUISpriteObject* self, void* closure)
{
return self->data->getTexture()->pyObject();
}
int UISprite::set_texture(PyUISpriteObject* self, PyObject* value, void* closure)
{
return -1;
}
PyGetSetDef UISprite::getsetters[] = {
{"x", (getter)UISprite::get_float_member, (setter)UISprite::set_float_member, "X coordinate of top-left corner", (void*)0},
{"y", (getter)UISprite::get_float_member, (setter)UISprite::set_float_member, "Y coordinate of top-left corner", (void*)1},
{"scale", (getter)UISprite::get_float_member, (setter)UISprite::set_float_member, "Size factor", (void*)2},
{"sprite_number", (getter)UISprite::get_int_member, (setter)UISprite::set_int_member, "Which sprite on the texture is shown", NULL},
{"texture", (getter)UISprite::get_texture, (setter)UISprite::set_texture, "Texture object", NULL},
{"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UISPRITE},
{NULL}
};
PyObject* UISprite::repr(PyUISpriteObject* self)
{
std::ostringstream ss;
if (!self->data) ss << "<Sprite (invalid internal object)>";
else {
//auto sprite = self->data->sprite;
ss << "<Sprite (x=" << self->data->getPosition().x << ", y=" << self->data->getPosition().y << ", " <<
"scale=" << self->data->getScale().x << ", " <<
"sprite_number=" << self->data->getSpriteIndex() << ")>";
}
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
int UISprite::init(PyUISpriteObject* self, PyObject* args, PyObject* kwds)
{
//std::cout << "Init called\n";
static const char* keywords[] = { "x", "y", "texture", "sprite_index", "scale", nullptr };
float x = 0.0f, y = 0.0f, scale = 1.0f;
int sprite_index;
PyObject* texture;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffOif",
const_cast<char**>(keywords), &x, &y, &texture, &sprite_index, &scale))
{
return -1;
}
// check types for texture
//if (texture != NULL && !PyObject_IsInstance(texture, (PyObject*)&PyTextureType)){
if (texture != NULL && !PyObject_IsInstance(texture, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))){
PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance");
return -1;
}
auto pytexture = (PyTextureObject*)texture;
self->data = std::make_shared<UISprite>(pytexture->data, sprite_index, sf::Vector2f(x, y), scale);
self->data->setPosition(sf::Vector2f(x, y));
return 0;
}

90
src/UISprite.h Normal file
View File

@ -0,0 +1,90 @@
#pragma once
#include "Common.h"
#include "Python.h"
#include "structmember.h"
#include "IndexTexture.h"
#include "Resources.h"
#include <list>
#include "PyCallable.h"
#include "PyTexture.h"
#include "PyColor.h"
#include "PyVector.h"
#include "PyFont.h"
#include "UIDrawable.h"
#include "UIBase.h"
class UISprite: public UIDrawable
{
private:
int sprite_index;
sf::Sprite sprite;
protected:
std::shared_ptr<PyTexture> ptex;
public:
UISprite();
UISprite(std::shared_ptr<PyTexture>, int, sf::Vector2f, float);
void update();
void render(sf::Vector2f) override final;
virtual UIDrawable* click_at(sf::Vector2f point) override final;
void render(sf::Vector2f, sf::RenderTexture&);
void setPosition(sf::Vector2f);
sf::Vector2f getPosition();
void setScale(sf::Vector2f);
sf::Vector2f getScale();
void setSpriteIndex(int);
int getSpriteIndex();
void setTexture(std::shared_ptr<PyTexture> _ptex, int _sprite_index=-1);
std::shared_ptr<PyTexture> getTexture();
PyObjectsEnum derived_type() override final;
static PyObject* get_float_member(PyUISpriteObject* self, void* closure);
static int set_float_member(PyUISpriteObject* self, PyObject* value, void* closure);
static PyObject* get_int_member(PyUISpriteObject* self, void* closure);
static int set_int_member(PyUISpriteObject* self, PyObject* value, void* closure);
static PyObject* get_texture(PyUISpriteObject* self, void* closure);
static int set_texture(PyUISpriteObject* self, PyObject* value, void* closure);
static PyGetSetDef getsetters[];
static PyObject* repr(PyUISpriteObject* self);
static int init(PyUISpriteObject* self, PyObject* args, PyObject* kwds);
};
namespace mcrfpydef {
static PyTypeObject PyUISpriteType = {
//PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "mcrfpy.Sprite",
.tp_basicsize = sizeof(PyUISpriteObject),
.tp_itemsize = 0,
.tp_dealloc = (destructor)[](PyObject* self)
{
PyUISpriteObject* obj = (PyUISpriteObject*)self;
// release reference to font object
//if (obj->texture) Py_DECREF(obj->texture);
obj->data.reset();
Py_TYPE(self)->tp_free(self);
},
.tp_repr = (reprfunc)UISprite::repr,
//.tp_hash = NULL,
//.tp_iter
//.tp_iternext
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("docstring"),
//.tp_methods = PyUIFrame_methods,
//.tp_members = PyUIFrame_members,
.tp_getset = UISprite::getsetters,
//.tp_base = NULL,
.tp_init = (initproc)UISprite::init,
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
{
PyUISpriteObject* self = (PyUISpriteObject*)type->tp_alloc(type, 0);
//if (self) self->data = std::make_shared<UICaption>();
return (PyObject*)self;
}
};
}

View File

@ -61,15 +61,15 @@ UITestScene::UITestScene(GameEngine* g) : Scene(g)
//ui_elements.push_back(&e1); //ui_elements.push_back(&e1);
//ui_elements.push_back(&e2); //ui_elements.push_back(&e2);
t.loadFromFile("./assets/kenney_tinydungeon.png"); //t.loadFromFile("./assets/kenney_tinydungeon.png");
t.setSmooth(false); //t.setSmooth(false);
auto* indextex = new IndexTexture(t, 16, 12, 11); //auto* indextex = new IndexTexture(t, 16, 12, 11);
Resources::game->textures.push_back(*indextex); //Resources::game->textures.push_back(*indextex);
auto ptex = std::make_shared<PyTexture>("./assets/kenney_tinydungeon.png", 16, 16);
//std::cout << Resources::game->textures.size() << " textures loaded.\n"; //std::cout << Resources::game->textures.size() << " textures loaded.\n";
auto e3 = std::make_shared<UISprite>(); auto e3 = std::make_shared<UISprite>(ptex, 84, sf::Vector2f(10, 10), 4.0);
// Make UISprite more like IndexSprite: this is bad // Make UISprite more like IndexSprite: this is bad
//e3->x = 10; e3->y = 10; //e3->x = 10; e3->y = 10;
@ -79,19 +79,19 @@ UITestScene::UITestScene(GameEngine* g) : Scene(g)
//e3->update(); //e3->update();
// This goes to show how inconvenient the default constructor is. It should be removed // This goes to show how inconvenient the default constructor is. It should be removed
e3->itex = &Resources::game->textures[0]; //e3->itex = &Resources::game->textures[0];
e3->sprite.setTexture(e3->itex->texture); //e3->sprite.setTexture(e3->itex->texture);
e3->sprite_index = 84; //e3->sprite_index = 84;
e3->sprite.setTextureRect(e3->itex->spriteCoordinates(e3->sprite_index)); //e3->sprite.setTextureRect(e3->itex->spriteCoordinates(e3->sprite_index));
e3->setPosition(10, 10); //e3->setPosition(10, 10);
e3->setScale(4.0f); //e3->setScale(4.0f);
e1aa->children->push_back(e3); e1aa->children->push_back(e3);
auto e4 = std::make_shared<UISprite>( auto e4 = std::make_shared<UISprite>(
indextex, //&Resources::game->textures[0], ptex, //indextex, //&Resources::game->textures[0],
85, sf::Vector2f(90, 10), 4.0); 85, sf::Vector2f(90, 10), 4.0);
e1aa->children->push_back(e4); e1aa->children->push_back(e4);
//std::cout << "UISprite built: " << e4->sprite.getPosition().x << " " << e4->sprite.getPosition().y << " " << e4->sprite.getScale().x << " " << //std::cout << "UISprite built: " << e4->sprite.getPosition().x << " " << e4->sprite.getPosition().y << " " << e4->sprite.getScale().x << " " <<
@ -104,9 +104,9 @@ UITestScene::UITestScene(GameEngine* g) : Scene(g)
std::cout << "pointer to ui_elements now shows size=" << ui->size() << std::endl; std::cout << "pointer to ui_elements now shows size=" << ui->size() << std::endl;
*/ */
// UIGrid test: (in grid cells) (in screen pixels) // UIGrid test: (in grid cells) ( in screen pixels )
// constructor args: w h texture x y w h // constructor args: w h texture x y w h
auto e5 = std::make_shared<UIGrid>(4, 4, indextex, 550, 150, 200, 200); auto e5 = std::make_shared<UIGrid>(4, 4, ptex, sf::Vector2f(550, 150), sf::Vector2f(200, 200));
e5->zoom=2.0; e5->zoom=2.0;
e5->points[0].color = sf::Color(255, 0, 0); e5->points[0].color = sf::Color(255, 0, 0);
e5->points[1].tilesprite = 1; e5->points[1].tilesprite = 1;
@ -123,8 +123,9 @@ UITestScene::UITestScene(GameEngine* g) : Scene(g)
// TODO - reimplement UISprite style rendering within UIEntity class. Entities don't have a screen pixel position, they have a grid position, and grid sets zoom when rendering them. // TODO - reimplement UISprite style rendering within UIEntity class. Entities don't have a screen pixel position, they have a grid position, and grid sets zoom when rendering them.
auto e5a = std::make_shared<UIEntity>(*e5); // this basic constructor sucks: sprite position + zoom are irrelevant for UIEntity. auto e5a = std::make_shared<UIEntity>(*e5); // this basic constructor sucks: sprite position + zoom are irrelevant for UIEntity.
e5a->grid = e5; e5a->grid = e5;
auto e5as = UISprite(indextex, 85, sf::Vector2f(0, 0), 1.0); //auto e5as = UISprite(indextex, 85, sf::Vector2f(0, 0), 1.0);
e5a->sprite = e5as; // will copy constructor even exist for UISprite...? //e5a->sprite = e5as; // will copy constructor even exist for UISprite...?
e5a->sprite = UISprite(ptex, 85, sf::Vector2f(0, 0), 1.0);
e5a->position = sf::Vector2f(1, 0); e5a->position = sf::Vector2f(1, 0);
e5->entities->push_back(e5a); e5->entities->push_back(e5a);
@ -153,7 +154,7 @@ void UITestScene::doAction(std::string name, std::string type)
*/ */
} }
void UITestScene::sRender() void UITestScene::render()
{ {
game->getWindow().clear(); game->getWindow().clear();
game->getWindow().draw(text); game->getWindow().draw(text);

View File

@ -18,5 +18,5 @@ public:
UITestScene(GameEngine*); UITestScene(GameEngine*);
void update() override final; void update() override final;
void doAction(std::string, std::string) override final; void doAction(std::string, std::string) override final;
void sRender() override final; void render() override final;
}; };

View File

@ -1,7 +1,7 @@
import mcrfpy import mcrfpy
mcrfpy.createScene("play") mcrfpy.createScene("play")
ui = mcrfpy.sceneUI("play") ui = mcrfpy.sceneUI("play")
t = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 12, 11) t = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16) # 12, 11)
font = mcrfpy.Font("assets/JetbrainsMono.ttf") font = mcrfpy.Font("assets/JetbrainsMono.ttf")
frame_color = (64, 64, 128) frame_color = (64, 64, 128)

View File

@ -1,136 +1,60 @@
#print("Hello mcrogueface")
import mcrfpy import mcrfpy
import cos_play
# Universal stuff
font = mcrfpy.Font("assets/JetbrainsMono.ttf") font = mcrfpy.Font("assets/JetbrainsMono.ttf")
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 12, 11) texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
texture_cold = mcrfpy.Texture("assets/kenney_ice.png", 16, 12, 11)
texture_hot = mcrfpy.Texture("assets/kenney_lava.png", 16, 12, 11)
# Test stuff print("[game.py] Default texture:")
mcrfpy.createScene("boom") print(mcrfpy.default_texture)
#mcrfpy.setScene("boom") print(type(mcrfpy.default_texture))
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)) # build test widgets
#box.click = lambda x, y, btn, type: print("Hello callback: ", x, y, btn, type)
#box.children.append(caption)
test_sprite_number = 86 mcrfpy.createScene("pytest")
sprite = mcrfpy.Sprite(20, 60, texture, test_sprite_number, 4.0) mcrfpy.setScene("pytest")
spritecap = mcrfpy.Caption(5, 5, "60", font) ui = mcrfpy.sceneUI("pytest")
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 # Frame
box.children.append(sprite) f = mcrfpy.Frame(25, 19, 462, 346, fill_color=(255, 92, 92))
box.children.append(spritecap) print("Frame alive")
box.click = click_sprite # fill (LinkedColor / Color): f.fill_color
# outline (LinkedColor / Color): f.outline_color
# pos (LinkedVector / Vector): f.pos
# size (LinkedVector / Vector): f.size
# Loading Screen # Caption
mcrfpy.createScene("loading") print("Caption attempt w/ fill_color:")
ui = mcrfpy.sceneUI("loading") #c = mcrfpy.Caption(512+25, 19, "Hi.", font)
mcrfpy.setScene("loading") #c = mcrfpy.Caption(512+25, 19, "Hi.", font, fill_color=(255, 128, 128))
logo_texture = mcrfpy.Texture("assets/temp_logo.png", 1024, 1, 1) c = mcrfpy.Caption(512+25, 19, "Hi.", font, fill_color=mcrfpy.Color(255, 128, 128), outline_color=(128, 255, 128))
logo_sprite = mcrfpy.Sprite(50, 50, logo_texture, 0, 0.5) print("Caption alive")
ui.append(logo_sprite) # fill (LinkedColor / Color): c.fill_color
logo_sprite.click = lambda *args: mcrfpy.setScene("menu") #color_val = c.fill_color
logo_caption = mcrfpy.Caption(70, 600, "Click to Proceed", font, (255, 0, 0, 255), (0, 0, 0, 255)) print(c.fill_color)
logo_caption.fill_color =(255, 0, 0, 255) #print("Set a fill color")
ui.append(logo_caption) #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
# Sprite
s = mcrfpy.Sprite(25, 384+19, texture, 86, 9.0)
# pos (LinkedVector / Vector): s.pos
# texture (Texture): s.texture
# menu screen # Grid
mcrfpy.createScene("menu") 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 e in [ for _x in range(10):
mcrfpy.Caption(10, 10, "Crypt of Sokoban", font, (255, 255, 255), (0, 0, 0)), for _y in range(10):
mcrfpy.Caption(20, 55, "a McRogueFace demo project", font, (192, 192, 192), (0, 0, 0)), g.at((_x, _y)).color = (255 - _x*25, 255 - _y*25, 255)
mcrfpy.Frame(15, 70, 150, 60, fill_color=(64, 64, 128)), g.zoom = 2.0
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): [ui.append(d) for d in (f, c, s, g)]
def wraps(*args, **kwargs):
#print(args)
action = args[3]
if action != "start": return
return fn(*args, **kwargs)
return wraps
@click_once print("built!")
def asdf(x, y, btn, action):
print(f"clicky @({x},{y}) {action}->{btn}")
@click_once # tests
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

221
src/scripts/game_old.py Normal file
View File

@ -0,0 +1,221 @@
#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

168
wikicrayons_colors.txt Normal file
View File

@ -0,0 +1,168 @@
Red #ED0A3F
Maroon #C32148
Scarlet #FD0E35
Brick Red #C62D42
English Vermilion #CC474B
Madder Lake #CC3336
Permanent Geranium Lake #E12C2C
Maximum Red #D92121
Chestnut #B94E48
Orange-Red #FF5349
Sunset Orange #FE4C40
Bittersweet #FE6F5E
Dark Venetian Red #B33B24
Venetian Red #CC553D
Light Venetian Red #E6735C
Vivid Tangerine #FF9980
Middle Red #E58E73
Burnt Orange #FF7034
Red-Orange #FF3F34
Orange #FF8833
Macaroni and Cheese #FFB97B
Middle Yellow Red #ECAC76
Mango Tango #E77200
Yellow-Orange #FFAE42
Maximum Yellow Red #F2BA49
Banana Mania #FBE7B2
Maize #F2C649
Orange-Yellow #F8D568
Goldenrod #FCD667
Dandelion #FED85D
Yellow #FBE870
Green-Yellow #F1E788
Middle Yellow #FFEB00
Olive Green #B5B35C
Spring Green #ECEBBD
Maximum Yellow #FAFA37
Canary #FFFF99
Lemon Yellow #FFFF9F
Maximum Green Yellow #D9E650
Middle Green Yellow #ACBF60
Inchworm #B0E313
Light Chrome Green #BEE64B
Yellow-Green #C5E17A
Maximum Green #5E8C31
Asparagus #7BA05B
Granny Smith Apple #9DE093
Fern #63B76C
Middle Green #4D8C57
Green #01A638
Medium Chrome Green #6CA67C
Forest Green #5FA777
Sea Green #93DFB8
Shamrock #33CC99
Mountain Meadow #1AB385
Jungle Green #29AB87
Caribbean Green #00CC99
Tropical Rain Forest #00755E
Middle Blue Green #8DD9CC
Pine Green #01796F
Maximum Blue Green #30BFBF
Robin's Egg Blue #00CCCC
Teal Blue #008080
Light Blue #8FD8D8
Aquamarine #458B74
Turquoise Blue #6CDAE7
Outer Space #2D383A
Sky Blue #76D7EA
Middle Blue #7ED4E6
Blue-Green #0095B7
Pacific Blue #009DC4
Cerulean #02A4D3
Maximum Blue #47ABCC
Blue (I) #2EB4E6
Cerulean Blue #339ACC
Cornflower #93CCEA
Green-Blue #2887C8
Midnight Blue #003366
Navy Blue #0066CC
Denim #1560BD
Blue (III) #0066FF
Cadet Blue #A9B2C3
Periwinkle #C3CDE6
Blue (II) #4570E6
Bluetiful #3C69E7
Wild Blue Yonder #7A89B8
Indigo #4F69C6
Manatee #8D90A1
Cobalt Blue #8C90C8
Celestial Blue #7070CC
Blue Bell #9999CC
Maximum Blue Purple #ACACE6
Violet-Blue #766EC8
Blue-Violet #6456B7
Ultramarine Blue #3F26BF
Middle Blue Purple #8B72BE
Purple Heart #652DC1
Royal Purple #6B3FA0
Violet (II) #8359A3
Medium Violet #8F47B3
Wisteria #C9A0DC
Lavender (I) #BF8FCC
Vivid Violet #803790
Maximum Purple #733380
Purple Mountains' Majesty #D6AEDD
Fuchsia #C154C1
Pink Flamingo #F2583E
Violet (I) #732E6C
Brilliant Rose #E667CE
Orchid #E29CD2
Plum #843179
Medium Rose #D96CBE
Thistle #D8BFD8
Mulberry #C8509B
Red-Violet #BB3385
Middle Purple #D982B5
Maximum Red Purple #A63A79
Jazzberry Jam #A50B5E
Eggplant #614051
Magenta #F653A6
Cerise #DA3287
Wild Strawberry #FF3399
Lavender (II) #FBAED2
Cotton Candy #FFB7D5
Carnation Pink #FFA6C9
Violet-Red #F7468A
Razzmatazz #E30B5C
Piggy Pink #FDD7E4
Carmine #E62E6B
Blush #DB5079
Tickle Me Pink #FC80A5
Mauvelous #F091A9
Salmon #FF91A4
Middle Red Purple #A55353
Mahogany #CA3435
Melon #FEBAAD
Pink Sherbert #F7A38E
Burnt Sienna #E97451
Brown #AF593E
Sepia #9E5B40
Fuzzy Wuzzy #87421F
Beaver #926F5B
Tumbleweed #DEA681
Raw Sienna #D27D46
Van Dyke Brown #664228
Tan #FA9D5A
Desert Sand #EDC9AF
Peach #FFCBA4
Burnt Umber #805533
Apricot #FDD5B1
Almond #EED9C4
Raw Umber #665233
Shadow #837050
Raw Sienna (I) #E6BC5C
Gold (I) #92926E
Gold (II) #E6BE8A
Silver #C9C0BB
Copper #DA8A67
Antique Brass #C88A65
Black #000000
Charcoal Gray #736A62
Gray #8B8680
Blue-Gray #C8C8CD
Timberwolf #D9D6CF
White #FFFFFF
Crayellow #F1D651[6]
Cool Mint #DDEBEC[6]
Oatmeal #D9DAD2[6]
Powder Blue #C0D5F0[6]

949
xkcd_colors.txt Normal file
View File

@ -0,0 +1,949 @@
cloudy blue #acc2d9
dark pastel green #56ae57
dust #b2996e
electric lime #a8ff04
fresh green #69d84f
light eggplant #894585
nasty green #70b23f
really light blue #d4ffff
tea #65ab7c
warm purple #952e8f
yellowish tan #fcfc81
cement #a5a391
dark grass green #388004
dusty teal #4c9085
grey teal #5e9b8a
macaroni and cheese #efb435
pinkish tan #d99b82
spruce #0a5f38
strong blue #0c06f7
toxic green #61de2a
windows blue #3778bf
blue blue #2242c7
blue with a hint of purple #533cc6
booger #9bb53c
bright sea green #05ffa6
dark green blue #1f6357
deep turquoise #017374
green teal #0cb577
strong pink #ff0789
bland #afa88b
deep aqua #08787f
lavender pink #dd85d7
light moss green #a6c875
light seafoam green #a7ffb5
olive yellow #c2b709
pig pink #e78ea5
deep lilac #966ebd
desert #ccad60
dusty lavender #ac86a8
purpley grey #947e94
purply #983fb2
candy pink #ff63e9
light pastel green #b2fba5
boring green #63b365
kiwi green #8ee53f
light grey green #b7e1a1
orange pink #ff6f52
tea green #bdf8a3
very light brown #d3b683
egg shell #fffcc4
eggplant purple #430541
powder pink #ffb2d0
reddish grey #997570
baby shit brown #ad900d
liliac #c48efd
stormy blue #507b9c
ugly brown #7d7103
custard #fffd78
darkish pink #da467d
deep brown #410200
greenish beige #c9d179
manilla #fffa86
off blue #5684ae
battleship grey #6b7c85
browny green #6f6c0a
bruise #7e4071
kelley green #009337
sickly yellow #d0e429
sunny yellow #fff917
azul #1d5dec
darkgreen #054907
green/yellow #b5ce08
lichen #8fb67b
light light green #c8ffb0
pale gold #fdde6c
sun yellow #ffdf22
tan green #a9be70
burple #6832e3
butterscotch #fdb147
toupe #c7ac7d
dark cream #fff39a
indian red #850e04
light lavendar #efc0fe
poison green #40fd14
baby puke green #b6c406
bright yellow green #9dff00
charcoal grey #3c4142
squash #f2ab15
cinnamon #ac4f06
light pea green #c4fe82
radioactive green #2cfa1f
raw sienna #9a6200
baby purple #ca9bf7
cocoa #875f42
light royal blue #3a2efe
orangeish #fd8d49
rust brown #8b3103
sand brown #cba560
swamp #698339
tealish green #0cdc73
burnt siena #b75203
camo #7f8f4e
dusk blue #26538d
fern #63a950
old rose #c87f89
pale light green #b1fc99
peachy pink #ff9a8a
rosy pink #f6688e
light bluish green #76fda8
light bright green #53fe5c
light neon green #4efd54
light seafoam #a0febf
tiffany blue #7bf2da
washed out green #bcf5a6
browny orange #ca6b02
nice blue #107ab0
sapphire #2138ab
greyish teal #719f91
orangey yellow #fdb915
parchment #fefcaf
straw #fcf679
very dark brown #1d0200
terracota #cb6843
ugly blue #31668a
clear blue #247afd
creme #ffffb6
foam green #90fda9
grey/green #86a17d
light gold #fddc5c
seafoam blue #78d1b6
topaz #13bbaf
violet pink #fb5ffc
wintergreen #20f986
yellow tan #ffe36e
dark fuchsia #9d0759
indigo blue #3a18b1
light yellowish green #c2ff89
pale magenta #d767ad
rich purple #720058
sunflower yellow #ffda03
green/blue #01c08d
leather #ac7434
racing green #014600
vivid purple #9900fa
dark royal blue #02066f
hazel #8e7618
muted pink #d1768f
booger green #96b403
canary #fdff63
cool grey #95a3a6
dark taupe #7f684e
darkish purple #751973
true green #089404
coral pink #ff6163
dark sage #598556
dark slate blue #214761
flat blue #3c73a8
mushroom #ba9e88
rich blue #021bf9
dirty purple #734a65
greenblue #23c48b
icky green #8fae22
light khaki #e6f2a2
warm blue #4b57db
dark hot pink #d90166
deep sea blue #015482
carmine #9d0216
dark yellow green #728f02
pale peach #ffe5ad
plum purple #4e0550
golden rod #f9bc08
neon red #ff073a
old pink #c77986
very pale blue #d6fffe
blood orange #fe4b03
grapefruit #fd5956
sand yellow #fce166
clay brown #b2713d
dark blue grey #1f3b4d
flat green #699d4c
light green blue #56fca2
warm pink #fb5581
dodger blue #3e82fc
gross green #a0bf16
ice #d6fffa
metallic blue #4f738e
pale salmon #ffb19a
sap green #5c8b15
algae #54ac68
bluey grey #89a0b0
greeny grey #7ea07a
highlighter green #1bfc06
light light blue #cafffb
light mint #b6ffbb
raw umber #a75e09
vivid blue #152eff
deep lavender #8d5eb7
dull teal #5f9e8f
light greenish blue #63f7b4
mud green #606602
pinky #fc86aa
red wine #8c0034
shit green #758000
tan brown #ab7e4c
darkblue #030764
rosa #fe86a4
lipstick #d5174e
pale mauve #fed0fc
claret #680018
dandelion #fedf08
orangered #fe420f
poop green #6f7c00
ruby #ca0147
dark #1b2431
greenish turquoise #00fbb0
pastel red #db5856
piss yellow #ddd618
bright cyan #41fdfe
dark coral #cf524e
algae green #21c36f
darkish red #a90308
reddy brown #6e1005
blush pink #fe828c
camouflage green #4b6113
lawn green #4da409
putty #beae8a
vibrant blue #0339f8
dark sand #a88f59
purple/blue #5d21d0
saffron #feb209
twilight #4e518b
warm brown #964e02
bluegrey #85a3b2
bubble gum pink #ff69af
duck egg blue #c3fbf4
greenish cyan #2afeb7
petrol #005f6a
royal #0c1793
butter #ffff81
dusty orange #f0833a
off yellow #f1f33f
pale olive green #b1d27b
orangish #fc824a
leaf #71aa34
light blue grey #b7c9e2
dried blood #4b0101
lightish purple #a552e6
rusty red #af2f0d
lavender blue #8b88f8
light grass green #9af764
light mint green #a6fbb2
sunflower #ffc512
velvet #750851
brick orange #c14a09
lightish red #fe2f4a
pure blue #0203e2
twilight blue #0a437a
violet red #a50055
yellowy brown #ae8b0c
carnation #fd798f
muddy yellow #bfac05
dark seafoam green #3eaf76
deep rose #c74767
dusty red #b9484e
grey/blue #647d8e
lemon lime #bffe28
purple/pink #d725de
brown yellow #b29705
purple brown #673a3f
wisteria #a87dc2
banana yellow #fafe4b
lipstick red #c0022f
water blue #0e87cc
brown grey #8d8468
vibrant purple #ad03de
baby green #8cff9e
barf green #94ac02
eggshell blue #c4fff7
sandy yellow #fdee73
cool green #33b864
pale #fff9d0
blue/grey #758da3
hot magenta #f504c9
greyblue #77a1b5
purpley #8756e4
baby shit green #889717
brownish pink #c27e79
dark aquamarine #017371
diarrhea #9f8303
light mustard #f7d560
pale sky blue #bdf6fe
turtle green #75b84f
bright olive #9cbb04
dark grey blue #29465b
greeny brown #696006
lemon green #adf802
light periwinkle #c1c6fc
seaweed green #35ad6b
sunshine yellow #fffd37
ugly purple #a442a0
medium pink #f36196
puke brown #947706
very light pink #fff4f2
viridian #1e9167
bile #b5c306
faded yellow #feff7f
very pale green #cffdbc
vibrant green #0add08
bright lime #87fd05
spearmint #1ef876
light aquamarine #7bfdc7
light sage #bcecac
yellowgreen #bbf90f
baby poo #ab9004
dark seafoam #1fb57a
deep teal #00555a
heather #a484ac
rust orange #c45508
dirty blue #3f829d
fern green #548d44
bright lilac #c95efb
weird green #3ae57f
peacock blue #016795
avocado green #87a922
faded orange #f0944d
grape purple #5d1451
hot green #25ff29
lime yellow #d0fe1d
mango #ffa62b
shamrock #01b44c
bubblegum #ff6cb5
purplish brown #6b4247
vomit yellow #c7c10c
pale cyan #b7fffa
key lime #aeff6e
tomato red #ec2d01
lightgreen #76ff7b
merlot #730039
night blue #040348
purpleish pink #df4ec8
apple #6ecb3c
baby poop green #8f9805
green apple #5edc1f
heliotrope #d94ff5
yellow/green #c8fd3d
almost black #070d0d
cool blue #4984b8
leafy green #51b73b
mustard brown #ac7e04
dusk #4e5481
dull brown #876e4b
frog green #58bc08
vivid green #2fef10
bright light green #2dfe54
fluro green #0aff02
kiwi #9cef43
seaweed #18d17b
navy green #35530a
ultramarine blue #1805db
iris #6258c4
pastel orange #ff964f
yellowish orange #ffab0f
perrywinkle #8f8ce7
tealish #24bca8
dark plum #3f012c
pear #cbf85f
pinkish orange #ff724c
midnight purple #280137
light urple #b36ff6
dark mint #48c072
greenish tan #bccb7a
light burgundy #a8415b
turquoise blue #06b1c4
ugly pink #cd7584
sandy #f1da7a
electric pink #ff0490
muted purple #805b87
mid green #50a747
greyish #a8a495
neon yellow #cfff04
banana #ffff7e
carnation pink #ff7fa7
tomato #ef4026
sea #3c9992
muddy brown #886806
turquoise green #04f489
buff #fef69e
fawn #cfaf7b
muted blue #3b719f
pale rose #fdc1c5
dark mint green #20c073
amethyst #9b5fc0
blue/green #0f9b8e
chestnut #742802
sick green #9db92c
pea #a4bf20
rusty orange #cd5909
stone #ada587
rose red #be013c
pale aqua #b8ffeb
deep orange #dc4d01
earth #a2653e
mossy green #638b27
grassy green #419c03
pale lime green #b1ff65
light grey blue #9dbcd4
pale grey #fdfdfe
asparagus #77ab56
blueberry #464196
purple red #990147
pale lime #befd73
greenish teal #32bf84
caramel #af6f09
deep magenta #a0025c
light peach #ffd8b1
milk chocolate #7f4e1e
ocher #bf9b0c
off green #6ba353
purply pink #f075e6
lightblue #7bc8f6
dusky blue #475f94
golden #f5bf03
light beige #fffeb6
butter yellow #fffd74
dusky purple #895b7b
french blue #436bad
ugly yellow #d0c101
greeny yellow #c6f808
orangish red #f43605
shamrock green #02c14d
orangish brown #b25f03
tree green #2a7e19
deep violet #490648
gunmetal #536267
blue/purple #5a06ef
cherry #cf0234
sandy brown #c4a661
warm grey #978a84
dark indigo #1f0954
midnight #03012d
bluey green #2bb179
grey pink #c3909b
soft purple #a66fb5
blood #770001
brown red #922b05
medium grey #7d7f7c
berry #990f4b
poo #8f7303
purpley pink #c83cb9
light salmon #fea993
snot #acbb0d
easter purple #c071fe
light yellow green #ccfd7f
dark navy blue #00022e
drab #828344
light rose #ffc5cb
rouge #ab1239
purplish red #b0054b
slime green #99cc04
baby poop #937c00
irish green #019529
pink/purple #ef1de7
dark navy #000435
greeny blue #42b395
light plum #9d5783
pinkish grey #c8aca9
dirty orange #c87606
rust red #aa2704
pale lilac #e4cbff
orangey red #fa4224
primary blue #0804f9
kermit green #5cb200
brownish purple #76424e
murky green #6c7a0e
wheat #fbdd7e
very dark purple #2a0134
bottle green #044a05
watermelon #fd4659
deep sky blue #0d75f8
fire engine red #fe0002
yellow ochre #cb9d06
pumpkin orange #fb7d07
pale olive #b9cc81
light lilac #edc8ff
lightish green #61e160
carolina blue #8ab8fe
mulberry #920a4e
shocking pink #fe02a2
auburn #9a3001
bright lime green #65fe08
celadon #befdb7
pinkish brown #b17261
poo brown #885f01
bright sky blue #02ccfe
celery #c1fd95
dirt brown #836539
strawberry #fb2943
dark lime #84b701
copper #b66325
medium brown #7f5112
muted green #5fa052
robin's egg #6dedfd
bright aqua #0bf9ea
bright lavender #c760ff
ivory #ffffcb
very light purple #f6cefc
light navy #155084
pink red #f5054f
olive brown #645403
poop brown #7a5901
mustard green #a8b504
ocean green #3d9973
very dark blue #000133
dusty green #76a973
light navy blue #2e5a88
minty green #0bf77d
adobe #bd6c48
barney #ac1db8
jade green #2baf6a
bright light blue #26f7fd
light lime #aefd6c
dark khaki #9b8f55
orange yellow #ffad01
ocre #c69c04
maize #f4d054
faded pink #de9dac
british racing green #05480d
sandstone #c9ae74
mud brown #60460f
light sea green #98f6b0
robin egg blue #8af1fe
aqua marine #2ee8bb
dark sea green #11875d
soft pink #fdb0c0
orangey brown #b16002
cherry red #f7022a
burnt yellow #d5ab09
brownish grey #86775f
camel #c69f59
purplish grey #7a687f
marine #042e60
greyish pink #c88d94
pale turquoise #a5fbd5
pastel yellow #fffe71
bluey purple #6241c7
canary yellow #fffe40
faded red #d3494e
sepia #985e2b
coffee #a6814c
bright magenta #ff08e8
mocha #9d7651
ecru #feffca
purpleish #98568d
cranberry #9e003a
darkish green #287c37
brown orange #b96902
dusky rose #ba6873
melon #ff7855
sickly green #94b21c
silver #c5c9c7
purply blue #661aee
purpleish blue #6140ef
hospital green #9be5aa
shit brown #7b5804
mid blue #276ab3
amber #feb308
easter green #8cfd7e
soft blue #6488ea
cerulean blue #056eee
golden brown #b27a01
bright turquoise #0ffef9
red pink #fa2a55
red purple #820747
greyish brown #7a6a4f
vermillion #f4320c
russet #a13905
steel grey #6f828a
lighter purple #a55af4
bright violet #ad0afd
prussian blue #004577
slate green #658d6d
dirty pink #ca7b80
dark blue green #005249
pine #2b5d34
yellowy green #bff128
dark gold #b59410
bluish #2976bb
darkish blue #014182
dull red #bb3f3f
pinky red #fc2647
bronze #a87900
pale teal #82cbb2
military green #667c3e
barbie pink #fe46a5
bubblegum pink #fe83cc
pea soup green #94a617
dark mustard #a88905
shit #7f5f00
medium purple #9e43a2
very dark green #062e03
dirt #8a6e45
dusky pink #cc7a8b
red violet #9e0168
lemon yellow #fdff38
pistachio #c0fa8b
dull yellow #eedc5b
dark lime green #7ebd01
denim blue #3b5b92
teal blue #01889f
lightish blue #3d7afd
purpley blue #5f34e7
light indigo #6d5acf
swamp green #748500
brown green #706c11
dark maroon #3c0008
hot purple #cb00f5
dark forest green #002d04
faded blue #658cbb
drab green #749551
light lime green #b9ff66
snot green #9dc100
yellowish #faee66
light blue green #7efbb3
bordeaux #7b002c
light mauve #c292a1
ocean #017b92
marigold #fcc006
muddy green #657432
dull orange #d8863b
steel #738595
electric purple #aa23ff
fluorescent green #08ff08
yellowish brown #9b7a01
blush #f29e8e
soft green #6fc276
bright orange #ff5b00
lemon #fdff52
purple grey #866f85
acid green #8ffe09
pale lavender #eecffe
violet blue #510ac9
light forest green #4f9153
burnt red #9f2305
khaki green #728639
cerise #de0c62
faded purple #916e99
apricot #ffb16d
dark olive green #3c4d03
grey brown #7f7053
green grey #77926f
true blue #010fcc
pale violet #ceaefa
periwinkle blue #8f99fb
light sky blue #c6fcff
blurple #5539cc
green brown #544e03
bluegreen #017a79
bright teal #01f9c6
brownish yellow #c9b003
pea soup #929901
forest #0b5509
barney purple #a00498
ultramarine #2000b1
purplish #94568c
puke yellow #c2be0e
bluish grey #748b97
dark periwinkle #665fd1
dark lilac #9c6da5
reddish #c44240
light maroon #a24857
dusty purple #825f87
terra cotta #c9643b
avocado #90b134
marine blue #01386a
teal green #25a36f
slate grey #59656d
lighter green #75fd63
electric green #21fc0d
dusty blue #5a86ad
golden yellow #fec615
bright yellow #fffd01
light lavender #dfc5fe
umber #b26400
poop #7f5e00
dark peach #de7e5d
jungle green #048243
eggshell #ffffd4
denim #3b638c
yellow brown #b79400
dull purple #84597e
chocolate brown #411900
wine red #7b0323
neon blue #04d9ff
dirty green #667e2c
light tan #fbeeac
ice blue #d7fffe
cadet blue #4e7496
dark mauve #874c62
very light blue #d5ffff
grey purple #826d8c
pastel pink #ffbacd
very light green #d1ffbd
dark sky blue #448ee4
evergreen #05472a
dull pink #d5869d
aubergine #3d0734
mahogany #4a0100
reddish orange #f8481c
deep green #02590f
vomit green #89a203
purple pink #e03fd8
dusty pink #d58a94
faded green #7bb274
camo green #526525
pinky purple #c94cbe
pink purple #db4bda
brownish red #9e3623
dark rose #b5485d
mud #735c12
brownish #9c6d57
emerald green #028f1e
pale brown #b1916e
dull blue #49759c
burnt umber #a0450e
medium green #39ad48
clay #b66a50
light aqua #8cffdb
light olive green #a4be5c
brownish orange #cb7723
dark aqua #05696b
purplish pink #ce5dae
dark salmon #c85a53
greenish grey #96ae8d
jade #1fa774
ugly green #7a9703
dark beige #ac9362
emerald #01a049
pale red #d9544d
light magenta #fa5ff7
sky #82cafc
light cyan #acfffc
yellow orange #fcb001
reddish purple #910951
reddish pink #fe2c54
orchid #c875c4
dirty yellow #cdc50a
orange red #fd411e
deep red #9a0200
orange brown #be6400
cobalt blue #030aa7
neon pink #fe019a
rose pink #f7879a
greyish purple #887191
raspberry #b00149
aqua green #12e193
salmon pink #fe7b7c
tangerine #ff9408
brownish green #6a6e09
red brown #8b2e16
greenish brown #696112
pumpkin #e17701
pine green #0a481e
charcoal #343837
baby pink #ffb7ce
cornflower #6a79f7
blue violet #5d06e9
chocolate #3d1c02
greyish green #82a67d
scarlet #be0119
green yellow #c9ff27
dark olive #373e02
sienna #a9561e
pastel purple #caa0ff
terracotta #ca6641
aqua blue #02d8e9
sage green #88b378
blood red #980002
deep pink #cb0162
grass #5cac2d
moss #769958
pastel blue #a2bffe
bluish green #10a674
green blue #06b48b
dark tan #af884a
greenish blue #0b8b87
pale orange #ffa756
vomit #a2a415
forrest green #154406
dark lavender #856798
dark violet #34013f
purple blue #632de9
dark cyan #0a888a
olive drab #6f7632
pinkish #d46a7e
cobalt #1e488f
neon purple #bc13fe
light turquoise #7ef4cc
apple green #76cd26
dull green #74a662
wine #80013f
powder blue #b1d1fc
off white #ffffe4
electric blue #0652ff
dark turquoise #045c5a
blue purple #5729ce
azure #069af3
bright red #ff000d
pinkish red #f10c45
cornflower blue #5170d7
light olive #acbf69
grape #6c3461
greyish blue #5e819d
purplish blue #601ef9
yellowish green #b0dd16
greenish yellow #cdfd02
medium blue #2c6fbb
dusty rose #c0737a
light violet #d6b4fc
midnight blue #020035
bluish purple #703be7
red orange #fd3c06
dark magenta #960056
greenish #40a368
ocean blue #03719c
coral #fc5a50
cream #ffffc2
reddish brown #7f2b0a
burnt sienna #b04e0f
brick #a03623
sage #87ae73
grey green #789b73
white #ffffff
robin's egg blue #98eff9
moss green #658b38
steel blue #5a7d9a
eggplant #380835
light yellow #fffe7a
leaf green #5ca904
light grey #d8dcd6
puke #a5a502
pinkish purple #d648d7
sea blue #047495
pale purple #b790d4
slate blue #5b7c99
blue grey #607c8e
hunter green #0b4008
fuchsia #ed0dd9
crimson #8c000f
pale yellow #ffff84
ochre #bf9005
mustard yellow #d2bd0a
light red #ff474c
cerulean #0485d1
pale pink #ffcfdc
deep blue #040273
rust #a83c09
light teal #90e4c1
slate #516572
goldenrod #fac205
dark yellow #d5b60a
dark grey #363737
army green #4b5d16
grey blue #6b8ba4
seafoam #80f9ad
puce #a57e52
spring green #a9f971
dark orange #c65102
sand #e2ca76
pastel green #b0ff9d
mint #9ffeb0
light orange #fdaa48
bright pink #fe01b1
chartreuse #c1f80a
deep purple #36013f
dark brown #341c02
taupe #b9a281
pea green #8eab12
puke green #9aae07
kelly green #02ab2e
seafoam green #7af9ab
blue green #137e6d
khaki #aaa662
burgundy #610023
dark teal #014d4e
brick red #8f1402
royal purple #4b006e
plum #580f41
mint green #8fff9f
gold #dbb40c
baby blue #a2cffe
yellow green #c0fb2d
bright purple #be03fd
dark red #840000
pale blue #d0fefe
grass green #3f9b0b
navy #01153e
aquamarine #04d8b2
burnt orange #c04e01
neon green #0cff0c
bright blue #0165fc
rose #cf6275
light pink #ffd1df
mustard #ceb301
indigo #380282
lime #aaff32
sea green #53fca1
periwinkle #8e82fe
dark pink #cb416b
olive green #677a04
peach #ffb07c
pale green #c7fdb5
light brown #ad8150
hot pink #ff028d
black #000000
lilac #cea2fd
navy blue #001146
royal blue #0504aa
beige #e6daa6
salmon #ff796c
olive #6e750e
maroon #650021
bright green #01ff07
dark purple #35063e
mauve #ae7181
forest green #06470c
aqua #13eac9
cyan #00ffff
tan #d1b26f
dark blue #00035b
lavender #c79fef
turquoise #06c2ac
dark green #033500
violet #9a0eea
light purple #bf77f6
lime green #89fe05
grey #929591
sky blue #75bbfd
yellow #ffff14
magenta #c20078
light green #96f97b
orange #f97306
teal #029386
light blue #95d0fc
red #e50000
brown #653700
pink #ff81c0
blue #0343df
green #15b01a
purple #7e1e9c