Compare commits

..

85 Commits

Author SHA1 Message Date
John McCardle 3325e4895e Refactor: Python 3.12, build libtcod & SFML from source. Cmake build. Directory cleanup
directories needed:
* build - for cmake output
* deps - stuff needed to compile McRogueface (headers)
	libtcod -> ../modules/libtcod/src/libtcod
	sfml -> ../modules/SFML/include/SFML
	python -> ../modules/cpython/Include
* lib - stuff needed to link McRogueFace (shared objects); also required at runtime
	libtcod -> `../modules/libtcod/buildsys/autotools/.libs/libtcod.so.1.0.24`
	sfml -> `../modules/SFML/build/lib/*`
	python -> `../modules/cpython/libpython3.12.so`; standard lib at ../modules/cpython/build/lib.linux-x86_64-3.12 & ../modules/cpython/Lib

You can get dependencies by:
 - Build from source (i.e. all submodules)
 - Go download them from each project's website
 - install packages from your distro and symlink them to deps/lib directories
2024-02-24 20:40:40 -05:00
John McCardle 705943abba initial cmake config (builds, python standard library not available) 2024-02-23 21:55:16 -05:00
John McCardle 47b485ca7d Submodules: Add dependencies
This is the prerequisite to:
* complete from-source build
* removing compilation libraries from the git repo
* rebasing the project for "alpha 0.1" release
2024-02-23 08:37:36 -05:00
John McCardle f2eaee95ec (minor) sprite object enabled in collection 2023-10-20 22:23:20 -04:00
John McCardle 494658e5c3 Sprite fixes. Changing sprite_number now changes the visible texture. Repr fixed. 2023-09-15 22:09:29 -04:00
John McCardle 6e820af8c4 mcrfpy.Sprite / PyUISpriteType compiles; too excited to fully test it (todo: modify the macro to get instances into and out of collections) 2023-09-13 23:23:08 -04:00
John McCardle cf485ef327 Refactoring UISprite to play well with Python API and match existing code styles. Default constructor will have to go, which complicates the Python class slightly for mcrfpy.Sprite 2023-09-11 20:30:10 -04:00
John McCardle 3a1432212f UICollection.remove 2023-09-09 10:14:11 -04:00
John McCardle 38b6a3cade UICollection.append, tests are good for Caption and Frame objects created by Python to be drawn by the UITestScene 2023-09-09 08:49:02 -04:00
John McCardle 1bbb0aa5b8 Caption object seems to be instantiable with a Font object now. Can't test actual rendering without a way to add objects to a collection. 2023-09-07 23:10:21 -04:00
John McCardle bec2b3294d Added PyFont/mcrfpy.Font object 2023-09-07 22:25:19 -04:00
John McCardle 9486104377 Converted py_instance to a macro (don't ask me why it doesn't work as a function) and first pass at UICaption functionality. UISprite C++ tests. 2023-09-03 20:40:52 -04:00
John McCardle 5267287b05 checkpoint: found that py_instance causes segfaults when called separately, but the same exact code inside of the _getitem method works fine. I can't explain that. I guess I'll turn it into a macro so the actions are inline and I can move on to finishing the other UI classes. 2023-09-03 12:46:23 -04:00
John McCardle b8af8bc870 UIDrawable to Python object (untested, but compiling) 2023-09-02 20:02:07 -04:00
John McCardle 0ef0a5d506 Switched UIFrame and Scene to store their UIDrawables in shared_ptr to vector, instead of directly to vector. Every object that can be exposed to Python has to be safely shareable so it doesn't become a segfault, and that includes the UIDrawable collections AND the UIDrawable members. So we get the terrifying type for collections of child elements: 'std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>>'. May I be forgiven for my sins 2023-09-02 14:00:48 -04:00
John McCardle a41d3d4a54 Iterator class fleshed out. Several implementations left to do, but it compiles, and I think every function definition for the PyUICollectionType and PyUICollectionIterType is now in place 2023-09-02 09:22:34 -04:00
John McCardle 6d4bc2989c UICollection work: fixed compilation, still quite a bit of stubs 2023-09-02 04:40:05 -04:00
John McCardle d5a7cbca85 In progress: UICollection. mcrfpydef::PyUICollection_sqmethods needs to be made static or moved somewhere it won't be multiply defined 2023-09-01 23:31:31 -04:00
John McCardle 5d8510747c Color and Frame classes pretty well integrated from the Python perspective now 2023-09-01 18:28:59 -04:00
John McCardle 06052c81c9 PyUIFrame looking not half bad. There seems to be a glitch with the color values. 2023-09-01 12:14:24 -04:00
John McCardle 6fe7b842ef Successful use of a copy-modify-paste template of Python type (UIFrame -> PyUIFrameObject) 2023-08-31 22:32:58 -04:00
John McCardle 50d926fe37 Aug 30/31 updates. Tinkering with shared_ptr. Working towards exposing UI objects to Python UI. Color object updated for shared_ptr structure that will be repeated for the rest of the UI objects. Still a lot of open questions, but committing here to get back on track after a few hours wasted trying to solve this problem too generally via templates. 2023-08-31 13:51:56 -04:00
John McCardle 795701c986 Cleanup: less curly braces in destructor 2023-08-30 14:50:27 -04:00
John McCardle 884a49a63a Switching UIFrame to sf::Color* for outline and fill members. Haven't tested with Python integration, but I wrote the methods to take a crack at it 2023-08-30 14:38:49 -04:00
John McCardle c4d5a497d4 Color container type for Python working. I still need to implement UIFrame using it. 2023-08-28 05:44:26 -04:00
John McCardle ba97aebf3e Showing FPS on title bar (GameEngine) 2023-08-27 19:58:15 -04:00
John McCardle ac0ec4bb71 In-work: Python segfaults when adding new objects to module 2023-08-25 21:57:42 -04:00
John McCardle a455c44b34 Debugging & build with debug symbols scripts 2023-08-25 21:56:27 -04:00
John McCardle 96e78e6150 Prepwork: marked the spot for adding more types to the Python module 2023-08-21 20:27:47 -04:00
John McCardle 0dd86056a8 Cleanup: remove python embedding test file 2023-08-12 20:06:16 -04:00
John McCardle 26cb410b8e Cleanup: Remove UITestScene. I believe test functionality will be better expressed as Python scripts 2023-08-12 19:48:29 -04:00
John McCardle d09fc87499 Cleanup: remove Item class/component. This may be added back later, but it's not in the EngJam 2023 plan and is being removed as a noisy, underdeveloped concept. 2023-08-12 15:37:46 -04:00
John McCardle b022dfa6e8 Cleanup: remove VectorShape class (it'll return, someday...) 2023-08-12 15:07:48 -04:00
John McCardle 232ce34d54 Cleanup: remove references to DrawSprite API method (debug method to draw on SFML window for a single frame) 2023-08-12 10:57:53 -04:00
John McCardle c1c17bab69 Basic, buggy movement purely from Python API 2023-07-17 22:08:06 -04:00
John McCardle e85861cbb2 I've worked keybinding functionality into Python, but there are some workarounds and notes (See the Jankfile) 2023-07-17 16:15:35 -04:00
John McCardle d6446e18ea Tinkering with input
I want to move keyboard input defs to the Python API. I laid the groundwork for it today.

From the JANKFILE:

- working on API endpoint `_registerInputAction`.

it will add "_py" as a suffix to the action string and register it along with other scene actions.

- Adding public Scene methods. These are on the base class with default of return `false`.

`bool Scene::registerActionInjected(int code, std::string name)` and `unregisterActionInjected`

the PythonScene (and other scenes that support injected user input) can override this method, check existing registrations, and return `true` when succeeding.

Also, upgraded to C++20 (g++ `c++2a`), mostly because I want to use map::contains.
2023-07-13 23:01:09 -04:00
John McCardle d3826804a0 (Minor) It's CO*M*P4300 2023-07-08 20:00:30 -04:00
John McCardle b4c49c4619 Giving myself credit for LGJ2023 tech demo features 2023-07-08 19:47:06 -04:00
John McCardle 76ac236be3 Linux Game Jam 2023 mini-contribution 2023-07-08 19:42:47 -04:00
John McCardle 97793fb26b Clean up console output for 7DRL submission 2023-03-12 00:32:27 -05:00
John McCardle b3134f0890 Basic hallways 2023-03-11 23:34:34 -05:00
John McCardle dfcc39dd43 toggleable camera following that allows for pan action 2023-03-11 21:54:54 -05:00
John McCardle 29ac89b489 bugfix: use float instead of int for modMenu/listMenus API calls, as this was corrupting the coordinates of sprites on uimenus 2023-03-11 17:15:06 -05:00
John McCardle b4daac6e0c Camera following functionality, first pass 2023-03-11 16:11:10 -05:00
John McCardle 3fd60d76ea Making empty space transparent to fix FOV, more generous FOV algorithm 2023-03-10 21:42:50 -05:00
John McCardle 99fa92f8ba Field of view, discovered tiles, opaque fog of war rendering 2023-03-10 19:39:44 -05:00
John McCardle 9441f357df 'entity only' grid update, saves lots of throughput on larger grids 2023-03-10 12:50:49 -05:00
John McCardle 34feb226e4 collision 2023-03-10 11:35:46 -05:00
John McCardle 486a1cd17c Keyboard control 2023-03-10 09:21:56 -05:00
John McCardle 8d9148b88d Music tester & animation work 2023-03-09 20:40:47 -05:00
John McCardle f1798189f0 Animation testing w/ Miniworld sprites 2023-03-09 14:29:37 -05:00
John McCardle 5b168737ce License update and bug list. Final Github release during 7DRL 2023. 2023-03-09 08:50:11 -05:00
John McCardle 6875cb5fe1 Spawning & drawing entities from Python API 2023-03-09 08:44:04 -05:00
John McCardle 87483cc8ad Sound APIs for Python 2023-03-08 12:34:03 -05:00
John McCardle 620def19f1 Fixed animations, jank-noted some issues & workarounds with the Animation python API 2023-03-08 09:20:57 -05:00
John McCardle c9b97b9b35 TestScene script for animation testing 2023-03-07 20:11:11 -05:00
John McCardle 8e59152a8f Windows fixes 2023-03-07 17:09:54 -08:00
John McCardle fedfcd46a3 Test animation now moves the entire UIMenu object (and children) 2023-03-07 20:03:09 -05:00
John McCardle c551c721ce Animation work: removing pointers from the entire class in favor of std::function/lambdas to write values. This actually works with SFML classes, because I can wrap the setter class 2023-03-07 07:39:41 -05:00
John McCardle d74635ee4e Check in... animations are roughly half built 2023-03-06 20:54:23 -05:00
John McCardle 47e823d5b9 Animation class (not tested) 2023-03-05 22:44:39 -05:00
John McCardle 6dbf8a5119 Pan/Zoom grids, Python basic generation template provided 2023-03-05 19:58:20 -05:00
John McCardle a53ae29467 Python object models 2023-03-04 23:18:21 -05:00
John McCardle 257aa3c3d2 Fully python-driven scene. Lots of interaction needs testing but the broad strokes are there for mouse pan/zoom on multiple grids and any number of UIs 2023-03-04 23:04:16 -05:00
John McCardle a4b6c2c428 Pan, zoom, and mouse-to-gridsquare translation that I can be proud of. Cleanup required, though 2023-03-04 19:04:05 -05:00
John McCardle b0ef1d2303 Tweak point conversion to prevent off-grid (negative) points from registering as on the grid 2023-03-04 00:18:19 -05:00
John McCardle b3f946ecb2 API endpoints: Create and retrieve grid/gridpoint objects 2023-03-03 23:57:42 -05:00
John McCardle 6a4150ec05 Screen to Grid is working pretty reliably, even if switching to float coordinates did make zoom at high values a bit wobbly. 2023-03-03 22:26:38 -05:00
John McCardle e295bfb742 python callbacks, working on grid 2023-03-03 22:16:47 -05:00
John McCardle 2ec97dfb1c Full modification of UI items seems to be working 2023-03-02 22:07:23 -05:00
John McCardle f89896176c Modify UI objects by calling mcrfpy.modMenu(m) 2023-03-02 20:41:43 -05:00
John McCardle de753713d5 UI from Python now working fairly comprehensively 2023-03-02 18:57:09 -05:00
John McCardle c8124e84dc Updated UIMenu to a map on the C++ side, Python gets the title property so changes can be properly, jankily, looked up 2023-03-02 06:35:13 -05:00
John McCardle a1e9129923 Send Menu color to Python 2023-03-02 05:53:17 -05:00
John McCardle f23dfbe4ba Another checkpoint. Compiling/building/running - Python API tests from in-engine REPL are not passing, though 2023-03-01 21:37:42 -05:00
John McCardle 1e9fd77a13 JANK MODE: Messy / broken commit - in progress
Needed to make a checkpoint, gods forgive me for committing known broken code straight to master. The jam has in a sense already begun.

I tested a smaller solution in the xplat_concept repo earlier today.
In short, I'm going to build a janky method to add new + report existing UI elements. After that's done, the UI building should be done from python modules, hastening the UI design.

This is ugly and bad, I am truly sorry. We just need to get through 7DRL, so I can't make it pretty today.
2023-02-28 23:19:43 -05:00
John McCardle 2c1946c29b Grid - widget for holding multi-layer map data 2023-02-27 07:02:34 -05:00
John McCardle 6d05f8bc63 Return NONE properly from test API point 2023-02-27 07:01:46 -05:00
John McCardle a4d0efe334 To-do list progress updated 2023-02-26 10:53:58 -05:00
John McCardle 6a47bc1e28 Windows/msvc lockstep changes 2023-02-26 07:51:03 -08:00
John McCardle 6a2c3c6c36 McRogueFace Python API (McRFPy_API) demo class 2023-02-26 10:23:44 -05:00
John McCardle a6f59085eb Update MSVC project for Windows build 2023-02-25 03:17:05 -08:00
John McCardle d2499a67f8 Porting in old gamejam code. Removed SOME cruft, more likely remains. Sound + sprite test. 2023-02-24 23:46:34 -05:00
John McCardle 1784489dfb Windows / MSVC commit. Bring your own Python PCBuild directory, I don't want to upload it to git. 2023-02-23 19:38:41 -08:00
85 changed files with 5629 additions and 6301 deletions

View File

@ -17,6 +17,10 @@ include_directories(${CMAKE_SOURCE_DIR}/deps/libtcod)
include_directories(${CMAKE_SOURCE_DIR}/deps/cpython)
include_directories(${CMAKE_SOURCE_DIR}/deps/Python)
# TODO: Move this into the WIN32 if block below (as 'else')
#include_directories(${CMAKE_SOURCE_DIR}/platform/linux)
include_directories(${CMAKE_SOURCE_DIR}/deps/platform/linux)
# Collect all the source files
file(GLOB_RECURSE SOURCES "src/*.cpp")
@ -39,9 +43,6 @@ if(WIN32)
# include_directories(path_to_additional_includes)
# link_directories(path_to_additional_libs)
# list(APPEND LINK_LIBS additional_windows_libs)
include_directories(${CMAKE_SOURCE_DIR}/deps/platform/windows)
else()
include_directories(${CMAKE_SOURCE_DIR}/deps/platform/linux)
endif()
# Add the directory where the linker should look for the libraries

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

View File

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

View File

@ -1,40 +0,0 @@
#ifndef __PLATFORM
#define __PLATFORM
#define __PLATFORM_SET_PYTHON_SEARCH_PATHS 1
std::wstring executable_path()
{
/*
wchar_t buffer[MAX_PATH];
GetModuleFileName(NULL, buffer, MAX_PATH);
std::wstring exec_path = buffer;
*/
auto exec_path = std::filesystem::canonical("/proc/self/exe").parent_path();
return exec_path.wstring();
//size_t path_index = exec_path.find_last_of('/');
//return exec_path.substr(0, path_index);
}
std::wstring executable_filename()
{
auto exec_path = std::filesystem::canonical("/proc/self/exe");
return exec_path.wstring();
}
std::wstring working_path()
{
auto cwd = std::filesystem::current_path();
return cwd.wstring();
}
std::string narrow_string(std::wstring convertme)
{
//setup converter
using convert_type = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_type, wchar_t> converter;
//use converter (.to_bytes: wstr->str, .from_bytes: str->wstr)
return converter.to_bytes(convertme);
}
#endif

View File

@ -1,39 +0,0 @@
#ifndef __PLATFORM
#define __PLATFORM
#define __PLATFORM_SET_PYTHON_SEARCH_PATHS 0
#include <Windows.h>
std::wstring executable_path()
{
wchar_t buffer[MAX_PATH];
GetModuleFileName(NULL, buffer, MAX_PATH);
std::wstring exec_path = buffer;
size_t path_index = exec_path.find_last_of(L"\\/");
return exec_path.substr(0, path_index);
}
std::wstring executable_filename()
{
wchar_t buffer[MAX_PATH];
GetModuleFileName(NULL, buffer, MAX_PATH);
std::wstring exec_path = buffer;
return exec_path;
}
std::wstring working_path()
{
auto cwd = std::filesystem::current_path();
return cwd.wstring();
}
std::string narrow_string(std::wstring convertme)
{
//setup converter
using convert_type = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_type, wchar_t> converter;
//use converter (.to_bytes: wstr->str, .from_bytes: str->wstr)
return converter.to_bytes(convertme);
}
#endif

View File

@ -1,111 +0,0 @@
# 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?)

50
scripts/Grid.py Normal file
View File

@ -0,0 +1,50 @@
class GridPoint:
def __init__(self, color, walkable, tilesprite, transparent, visible, discovered, color_overlay, tile_overlay, uisprite):
self.color = color
self.walkable = walkable
self.tilesprite = tilesprite
self.transparent = transparent
self.visible = visible
self.discovered = discovered
self.color_overlay = color_overlay
self.tile_overlay = tile_overlay
self.uisprite = uisprite
def __repr__(self):
return f"<GridPoint {self.color}, {self.tilesprite}/{self.uisprite} {'W' if self.walkable else '-'}{'T' if self.transparent else '-'}{'V' if self.visible else '-'}{'D' if self.discovered else '-'} {self.color_overlay}/{self.tile_overlay}>"
class Grid:
def __init__(self, title, gx, gy, gs, x, y, w, h, visible=False):
self.title = title
self.grid_x = gx
self.grid_y = gy
self.grid_size = gs
self.x = x
self.y = y
self.w = w
self.h = h
self.visible = visible
self.points = []
self.entities = []
def at(self, x, y):
if not (x > 0 and y > 0 and x < self.grid_x and y < self.grid_y): return None
return self.points[y * self.grid_y + x]
def __repr__(self):
return f"<Grid {self.grid_x}x{self.grid_y}, grid_size={self.grid_size}, (({self.x},{self.y}), ({self.w}, {self.h})), visible={self.visible}>"
# CGrid(Grid* _g, int _ti, int _si, int _x, int _y, bool _v)
class Entity:
def __init__(self, parent, tex_index, sprite_index, x, y, visible=True):
self.parent = parent
self.tex_index = tex_index
self.sprite_index = sprite_index
self.x = x
self.y = y
self.visible = visible
def __repr__(self):
return f"<Entity on grid {repr(self.parent)}@({self.x},{self.y}), TI={self.tex_index}, SI={self.sprite_index}, visible={self.visible}>"

79
scripts/MusicScene.py Normal file
View File

@ -0,0 +1,79 @@
import mcrfpy
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED, GREEN, BLUE = (255, 0, 0), (0, 255, 0), (0, 0, 255)
DARKRED, DARKGREEN, DARKBLUE = (192, 0, 0), (0, 192, 0), (0, 0, 192)
class MusicScene:
def __init__(self, ui_name = "demobox1", grid_name = "demogrid"):
# Texture & Sound Loading
print("Load textures")
mcrfpy.createTexture("./assets/test_portraits.png", 32, 8, 8) #0 - portraits
mcrfpy.createTexture("./assets/alives_other.png", 16, 64, 64) #1 - TinyWorld NPCs
mcrfpy.createTexture("./assets/alives_other.png", 32, 32, 32) #2 - TinyWorld NPCs - 2x2 sprite
# {"createSoundBuffer", McRFPy_API::_createSoundBuffer, METH_VARARGS, "(filename)"},
#{"loadMusic", McRFPy_API::_loadMusic, METH_VARARGS, "(filename)"},
#{"setMusicVolume", McRFPy_API::_setMusicVolume, METH_VARARGS, "(int)"},
#{"setSoundVolume", McRFPy_API::_setSoundVolume, METH_VARARGS, "(int)"},
#{"playSound", McRFPy_API::_playSound, METH_VARARGS, "(int)"},
#{"getMusicVolume", McRFPy_API::_getMusicVolume, METH_VARARGS, ""},
#{"getSoundVolume", McRFPy_API::_getSoundVolume, METH_VARARGS, ""},
mcrfpy.loadMusic("./assets/ultima.ogg")
mcrfpy.createSoundBuffer("./assets/boom.wav")
self.ui_name = ui_name
self.grid_name = grid_name
print("Create UI")
# Create dialog UI
mcrfpy.createMenu(ui_name, 20, 540, 500, 200)
mcrfpy.createCaption(ui_name, f"Music Volume: {mcrfpy.getMusicVolume()}", 24, RED)
mcrfpy.createCaption(ui_name, f"SFX Volume: {mcrfpy.getSoundVolume()}", 24, RED)
#mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKBLUE, (0, 0, 0), "clicky", "testaction")
mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKRED, (0, 0, 0), "Music+", "mvol+")
mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKGREEN, (0, 0, 0), "Music-", "mvol-")
mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKBLUE, GREEN, "SFX+", "svol+")
mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKBLUE, RED, "SFX-", "svol-")
mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKRED, (0, 0, 0), "REPL", "startrepl")
mcrfpy.createSprite(ui_name, 1, 0, 20, 40, 3.0)
print("Create UI 2")
entitymenu = "entitytestmenu"
mcrfpy.createMenu(entitymenu, 840, 20, 20, 500)
mcrfpy.createButton(entitymenu, 0, 10, 150, 40, DARKBLUE, BLACK, "PlayM", "playm")
mcrfpy.createButton(entitymenu, 0, 60, 150, 40, DARKBLUE, BLACK, "StopM", "stopm")
mcrfpy.createButton(entitymenu, 0, 110, 150, 40, DARKBLUE, BLACK, "SFX", "boom")
print("Make UIs visible")
self.menus = mcrfpy.listMenus()
self.menus[0].visible = True
self.menus[1].w = 170
self.menus[1].visible = True
mcrfpy.modMenu(self.menus[0])
mcrfpy.modMenu(self.menus[1])
self.mvol = mcrfpy.getMusicVolume()
self.svol = mcrfpy.getSoundVolume()
mcrfpy.registerPyAction("mvol+", lambda: self.setmvol(self.mvol+10))
mcrfpy.registerPyAction("mvol-", lambda: self.setmvol(self.mvol-10))
mcrfpy.registerPyAction("svol+", lambda: self.setsvol(self.svol+10))
mcrfpy.registerPyAction("svol-", lambda: self.setsvol(self.svol-10))
mcrfpy.registerPyAction("playm", lambda: None)
mcrfpy.registerPyAction("stopm", lambda: None)
mcrfpy.registerPyAction("boom", lambda: mcrfpy.playSound(0))
def setmvol(self, v):
mcrfpy.setMusicVolume(int(v))
self.menus[0].captions[0].text = f"Music Volume: {mcrfpy.getMusicVolume():.1f}"
mcrfpy.modMenu(self.menus[0])
self.mvol = mcrfpy.getMusicVolume()
def setsvol(self, v):
mcrfpy.setSoundVolume(int(v))
self.menus[0].captions[1].text = f"Sound Volume: {mcrfpy.getSoundVolume():.1f}"
mcrfpy.modMenu(self.menus[0])
self.svol = mcrfpy.getSoundVolume()
scene = None
def start():
global scene
scene = MusicScene()

575
scripts/TestScene.py Normal file
View File

@ -0,0 +1,575 @@
import UIMenu
import Grid
import mcrfpy
from random import randint, choice
from pprint import pprint
#print("TestScene imported")
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED, GREEN, BLUE = (255, 0, 0), (0, 255, 0), (0, 0, 255)
DARKRED, DARKGREEN, DARKBLUE = (192, 0, 0), (0, 192, 0), (0, 0, 192)
animations_in_progress = 0
# don't load grid over and over, use the global scene
scene = None
class TestEntity:
def __init__(self, grid, label, tex_index, basesprite, x, y, texture_width=64, walk_frames=5, attack_frames=6, do_fov=False):
self.grid = grid
self.basesprite = basesprite
self.texture_width = texture_width
self.walk_frames = walk_frames
self.attack_frames = attack_frames
self.x = x
self.y = y
self.facing_direction = 0
self.do_fov = do_fov
self.label = label
self.inventory = []
#print(f"Calling C++ with: {repr((self.grid, label, tex_index, self.basesprite, x, y, self))}")
grids = mcrfpy.listGrids()
for g in grids:
if g.title == self.grid:
self.entity_index = len(g.entities)
mcrfpy.createEntity(self.grid, label, tex_index, self.basesprite, x, y, self)
def ai_act(self):
return # no AI motion
#if self.label == "player": return
self.move(randint(-1, 1), randint(-1, 1))
scene.actors += 1
def player_act(self):
#print("I'M INTERVENING")
mcrfpy.unlockPlayerInput()
scene.updatehints()
def die(self):
#self.x = -2
#self.y = -2
self.move(-1000,-1000)
self.animate(0,animove=(-1000,-1000))
self.x = -1000
self.y = -1000
self.label = "dead"
def interact(self, initiator, callback):
print(f"Interacted with {self.label}. ", end='')
if self.label == 'item':
print("'taking' item.")
callback()
self.die()
else:
print("blocking movement.")
def move(self, dx, dy, force=False):
# select animation direction
# prefer left or right for diagonals.
#grids = mcrfpy.listGrids()
for g in scene.grids:
if g.title == self.grid:
if not force and g.at(self.x + dx, self.y + dy) is None or not g.at(self.x + dx, self.y + dy).walkable:
#print("Blocked at target location.")
return
if not force: # entities can be stepped on when force=True (like collecting items!)
for entity in scene.tes:
if (entity.x, entity.y) == (self.x + dx, self.y + dy):
print(f"Blocked by entity {entity.label} at ({entity.x}, {entity.y})")
return entity.interact(self, lambda: self.move(dx, dy, force=True))
if self.label == "player":
mcrfpy.lockPlayerInput()
scene.updatehints()
if (dx == 0 and dy == 0):
direction = self.facing_direction # TODO, jump straight to computer turn
elif (dx):
direction = 2 if dx == +1 else 3
else:
direction = 0 if dy == +1 else 1
self.animate(direction, move=True, animove=(self.x + dx, self.y+dy))
self.facing_direction = direction
if (self.do_fov): mcrfpy.refreshFov()
def animate(self, direction, attacking=False, move=False, animove=None):
start_sprite = self.basesprite + (self.texture_width * (direction + (4 if attacking else 0)))
animation_frames = [start_sprite + i for i in range((self.attack_frames if attacking else self.walk_frames))]
mcrfpy.createAnimation(
0.15, # duration, seconds
self.grid, # parent: a UIMenu or Grid key
"entity", # target type: 'menu', 'grid', 'caption', 'button', 'sprite', or 'entity'
self.entity_index, # target id: integer index for menu or grid objs; None for grid/menu
"sprite", # field: 'position', 'size', 'bgcolor', 'textcolor', or 'sprite'
self.animation_done, #callback: callable once animation is complete
False, #loop: repeat indefinitely
animation_frames # values: iterable of frames for 'sprite', lerp target for others
)
#global animations_in_progress
#animations_in_progress += 1
if move:
pos = [self.x, self.y]
if (direction == 0): pos[1] += 1
elif (direction == 1): pos[1] -= 1
elif (direction == 2): pos[0] += 1
elif (direction == 3): pos[0] -= 1
if not animove:
self.x, self.y = pos
animove = pos
else:
pos = animove
self.x, self.y = animove
#scene.move_entity(self.grid, self.entity_index, pos)
#for g in mcrfpy.listGrids():
for g in scene.grids:
if g.title == self.grid:
g.entities[self.entity_index].x = pos[0]
g.entities[self.entity_index].y = pos[1]
mcrfpy.modGrid(g, True)
if animove:
mcrfpy.createAnimation(
0.25, # duration, seconds
self.grid, # parent: a UIMenu or Grid key
"entity", # target type: 'menu', 'grid', 'caption', 'button', 'sprite', or 'entity'
self.entity_index, # target id: integer index for menu or grid objs; None for grid/menu
"position", # field: 'position', 'size', 'bgcolor', 'textcolor', or 'sprite'
self.animation_done, #callback: callable once animation is complete
False, #loop: repeat indefinitely
animove # values: iterable of frames for 'sprite', lerp target for others
)
#animations_in_progress += 1
def animation_done(self):
#global animations_in_progress
#animations_in_progress -= 1
scene.actors -= 1
#print(f"{self} done animating - {scene.actors} remaining")
if scene.actors <= 0:
scene.actors = 0
mcrfpy.unlockPlayerInput()
scene.updatehints()
class TestItemEntity(TestEntity):
def __init__(self, grid, label, tex_index, basesprite, x, y, texture_width=64, walk_frames=5, attack_frames=6, do_fov=False, item="Nothing"):
super().__init__(grid, label, tex_index, basesprite, x, y, texture_width, walk_frames, attack_frames, do_fov)
self.item = item
def interact(self, initiator, callback):
if self.label == 'dead': return super().interact(initiator, callback)
print(f"Interacted with {self.label}, an item. Adding {self.item} to {initiator.label}'s inventory")
initiator.inventory.append(self.item)
callback()
scene.itemguis()
self.die()
class TestDoorEntity(TestEntity):
def __init__(self, grid, label, tex_index, basesprite, x, y, texture_width=64, walk_frames=5, attack_frames=6, do_fov=False, key="Nothing"):
super().__init__(grid, label, tex_index, basesprite, x, y, texture_width, walk_frames, attack_frames, do_fov)
self.key = key
def interact(self, initiator, callback):
if self.label == 'dead': return super().interact(initiator, callback)
print(f"Interacted with {self.label}, a Door. ", end='')
if self.key in initiator.inventory:
initiator.inventory.remove(self.key)
print("Taking key & passing.")
callback()
scene.itemguis()
self.die()
else:
print("The door is locked.")
class TestScene:
def __init__(self, ui_name = "demobox1", grid_name = "demogrid"):
# Texture & Sound Loading
self.actors = 0
#print("Load textures")
mcrfpy.createTexture("./assets/test_portraits.png", 32, 8, 8) #0 - portraits
mcrfpy.createTexture("./assets/alives_other.png", 16, 64, 64) #1 - TinyWorld NPCs
mcrfpy.createTexture("./assets/alives_other.png", 32, 32, 32) #2 - TinyWorld NPCs - 2x2 sprite
mcrfpy.createTexture("./assets/custom_player.png", 16, 5, 13) #3 - player
mcrfpy.createTexture("./assets/Sprite-0001.png", 80, 10, 10) #4 - LGJ2023 keycard + other icons
mcrfpy.createTexture("./assets/tiny_keycards.png", 16, 8, 8) #5 - tiny keycards (ground items)
self.ui_name = ui_name
self.grid_name = grid_name
# Menu index = 0
#print("Create UI")
# Create dialog UI
mcrfpy.createMenu(ui_name, 20, 540, 500, 200)
#mcrfpy.createCaption(ui_name, "Hello There", 18, BLACK)
mcrfpy.createCaption(ui_name, "", 24, RED)
#mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKBLUE, (0, 0, 0), "clicky", "testaction")
mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKRED, (0, 0, 0), "REPL", "startrepl")
##mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKGREEN, (0, 0, 0), "map gen", "gridgen")
#mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKGREEN, (0, 0, 0), "mapL", "gridgen2")
#mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKBLUE, (192, 0, 0), "a_menu", "animtest")
#mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKRED, GREEN, "a_spr", "animtest2")
#mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKBLUE, GREEN, "Next sp", "nextsp")
#mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKBLUE, RED, "Prev sp", "prevsp")
#mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKBLUE, DARKGREEN, "+16 sp", "skipsp")
mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKGREEN, (0, 0, 0), "Next", "nextsp")
mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKBLUE, (0, 0, 0), "Prev", "prevsp")
mcrfpy.createSprite(ui_name, 4, 1, 10, 10, 2.0)
# Menu index = 1
#print("Create UI 2")
entitymenu = "entitytestmenu"
mcrfpy.createMenu(entitymenu, 840, 20, 20, 500)
mcrfpy.createButton(entitymenu, 0, 10, 150, 40, DARKBLUE, BLACK, "Up", "test_up")
mcrfpy.createButton(entitymenu, 0, 60, 150, 40, DARKBLUE, BLACK, "Down", "test_down")
mcrfpy.createButton(entitymenu, 0, 110, 150, 40, DARKBLUE, BLACK, "Left", "test_left")
mcrfpy.createButton(entitymenu, 0, 160, 150, 40, DARKBLUE, BLACK, "Right", "test_right")
mcrfpy.createButton(entitymenu, 0, 210, 150, 40, DARKBLUE, BLACK, "Attack", "test_attack")
mcrfpy.createButton(entitymenu, 0, 210, 150, 40, DARKBLUE, RED, "TE", "testent")
# Menu index = 2
mcrfpy.createMenu( "gridtitlemenu", 0, -10, 0, 0)
mcrfpy.createCaption("gridtitlemenu", "<grid name>", 18, WHITE)
#mcrfpy.createCaption("gridtitlemenu", "<camstate>", 16, WHITE)
# Menu index = 3
mcrfpy.createMenu( "hintsmenu", 0, 505, 0, 0)
mcrfpy.createCaption("hintsmenu", "<hintline>", 16, WHITE)
# Menu index = 4
# menu names must be created in alphabetical order (?!) - thanks, C++ hash map
mcrfpy.createMenu( "i", 600, 20, 0, 0)
#mcrfpy.createMenu( "camstatemenu", 600, 20, 0, 0)
mcrfpy.createCaption("i", "<camstate>", 16, WHITE)
# Menu index = 5
mcrfpy.createMenu( "j", 600, 500, 0, 0)
mcrfpy.createButton( "j", 0, 0, 80, 40, DARKBLUE, WHITE, "Recenter", "activatecamfollow")
# Menu index = 6, 7, 8, 9, 10: keycard sprites
mcrfpy.createMenu("k", 540, 540, 80, 80) #6
mcrfpy.createSprite("k", 4, 0, 10, 10, 1.0)
mcrfpy.createMenu("l", 540 + (80 * 1), 540, 80, 80) #7
mcrfpy.createSprite("l", 4, 1, 10, 10, 1.0)
mcrfpy.createMenu("m", 540 + (80 * 2), 540, 80, 80) #8
mcrfpy.createSprite("m", 4, 2, 10, 10, 1.0)
mcrfpy.createMenu("n", 540 + (80 * 3), 540, 80, 80) #9
mcrfpy.createSprite("n", 4, 3, 10, 10, 1.0)
mcrfpy.createMenu("o", 540 + (80 * 4), 540, 80, 80) #10
mcrfpy.createSprite("o", 4, 4, 10, 10, 1.0)
mcrfpy.createMenu("p", 20, 20, 40, 40) #11
#mcrfpy.createButton("p", 0, 0, 130, 40, DARKGREEN, (0, 0, 0), "Register", "keyregistration")
mcrfpy.createButton("p", 0, 0, 130, 40, DARKGREEN, (0, 0, 0), "Register", "startrepl")
mcrfpy.registerPyAction("keyregistration", keyregistration)
#print("Make UIs visible")
self.menus = mcrfpy.listMenus()
self.menus[0].visible = True
self.menus[1].w = 170
self.menus[1].visible = True
self.menus[2].visible = True
for mn in range(2, 6):
self.menus[mn].bgcolor = BLACK
self.menus[mn].visible = True
mcrfpy.modMenu(self.menus[mn])
for mn in range(6, 11):
self.menus[mn].bgcolor = BLACK
self.menus[mn].visible = False
mcrfpy.modMenu(self.menus[mn])
self.menus[11].visible=True
mcrfpy.modMenu(self.menus[11])
#self.menus[2].bgcolor = BLACK
#self.menus[3].visible = True
#self.menus[3].bgcolor = BLACK
#self.menus[4].visible = True
#self.menus[4].bgcolor = BLACK
#self.menus[5].visible = True
#mcrfpy.modMenu(self.menus[0])
#mcrfpy.modMenu(self.menus[1])
#mcrfpy.modMenu(self.menus[2])
#mcrfpy.modMenu(self.menus[3])
#mcrfpy.modMenu(self.menus[4])
#mcrfpy.modMenu(self.menus[5])
#pprint(mcrfpy.listMenus())
#print(f"UI 1 gave back this sprite: {self.menus[0].sprites}")
#print("Create grid")
# create grid (title, gx, gy, gs, x, y, w, h)
mcrfpy.createGrid(grid_name, 100, 100, 16, 20, 20, 800, 500)
self.grids = mcrfpy.listGrids()
#print(self.grids)
#print("Create entities")
#mcrfpy.createEntity("demogrid", "dragon", 2, 545, 10, 10, lambda: None)
#mcrfpy.createEntity("demogrid", "tinyenemy", 1, 1538, 3, 6, lambda: None)
#print("Create fancy entity")
self.player = TestEntity("demogrid", "player", 3, 20, 17, 3, 5, walk_frames=4, attack_frames=5, do_fov=True)
self.tes = [
TestEntity("demogrid", "classtest", 1, 1538, 5, 7, 64, walk_frames=5, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 1545, 7, 9, 64, walk_frames=5, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 1552, 9, 11, 64, walk_frames=5, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 1566, 11, 13, 64, walk_frames=4, attack_frames=6),
#TestEntity("demogrid", "item", 1, 1573, 13, 15, 64, walk_frames=4, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 1583, 15, 17, 64, walk_frames=4, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 130, 9, 7, 64, walk_frames=5, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 136, 11, 9, 64, walk_frames=5, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 143, 13, 11, 64, walk_frames=5, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 158, 15, 13, 64, walk_frames=5, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 165, 17, 15, 64, walk_frames=5, attack_frames=6),
self.player,
TestItemEntity("demogrid", "GreenKeyCard", 5, 0, 19, 3, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, item="Green Keycard"),
TestItemEntity("demogrid", "BlueKeyCard", 5, 1, 21, 3, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, item="Blue Keycard"),
TestItemEntity("demogrid", "RedKeyCard", 5, 2, 23, 3, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, item="Red Keycard"),
TestItemEntity("demogrid", "OrangeKeyCard", 5, 3, 25, 3, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, item="Orange Keycard"),
TestItemEntity("demogrid", "DevilKeyCard", 5, 4, 27, 3, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, item="Devil Keycard"),
TestDoorEntity("demogrid", "GreenKeyDoor", 5, 8, 19, 5, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, key="Green Keycard"),
TestDoorEntity("demogrid", "BlueKeyDoor", 5, 9, 21, 5, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, key="Blue Keycard"),
TestDoorEntity("demogrid", "RedKeyDoor", 5, 10, 23, 5, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, key="Red Keycard"),
TestDoorEntity("demogrid", "OrangeKeyDoor", 5, 11, 25, 5, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, key="Orange Keycard"),
TestDoorEntity("demogrid", "DevilKeyDoor", 5, 12, 27, 5, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, key="Devil Keycard")
]
self.grids = mcrfpy.listGrids()
self.entity_direction = 0
mcrfpy.registerPyAction("test_down", lambda: [te.animate(0, False, True) for te in self.tes])
mcrfpy.registerPyAction("test_up", lambda: [te.animate(1, False, True) for te in self.tes])
mcrfpy.registerPyAction("test_right", lambda: [te.animate(2, False, True) for te in self.tes])
mcrfpy.registerPyAction("test_left", lambda: [te.animate(3, False, True) for te in self.tes])
mcrfpy.registerPyAction("test_attack", lambda: [te.animate(0, True) for te in self.tes])
mcrfpy.registerPyAction("testent", lambda: [te.animate(2, True) for te in self.tes])
mcrfpy.registerPyAction("activatecamfollow", lambda: mcrfpy.camFollow(True))
# Button behavior
self.clicks = 0
self.sprite_index = 0
#mcrfpy.registerPyAction("testaction", self.click)
mcrfpy.registerPyAction("gridgen", self.gridgen)
#mcrfpy.registerPyAction("gridgen2", lambda: self.gridgen())
#mcrfpy.registerPyAction("animtest", lambda: self.createAnimation())
#mcrfpy.registerPyAction("animtest2", lambda: self.createAnimation2())
mcrfpy.registerPyAction("nextsp", lambda: self.changesprite(1))
mcrfpy.registerPyAction("prevsp", lambda: self.changesprite(-1))
mcrfpy.registerPyAction("skipsp", lambda: self.changesprite(16))
mcrfpy.unlockPlayerInput()
mcrfpy.setActiveGrid("demogrid")
self.gridgen()
self.updatehints()
mcrfpy.camFollow(True)
def itemguis(self):
print(self.player.inventory)
items = ["Green Keycard", "Blue Keycard", "Red Keycard", "Orange Keycard", "Devil Keycard"]
for mn in range(6, 11):
self.menus[mn].visible = items[mn-6] in self.player.inventory
mcrfpy.modMenu(self.menus[mn])
def animate_entity(self, direction, attacking=False):
if direction is None:
direction = self.entity_direction
else:
self.entity_direction = direction
dragon_sprite = 545 + (32 * (direction + (4 if attacking else 0)))
dragon_animation = [dragon_sprite + i for i in range((5 if attacking else 4))]
mcrfpy.createAnimation(
1.0, # duration, seconds
"demogrid", # parent: a UIMenu or Grid key
"entity", # target type: 'menu', 'grid', 'caption', 'button', 'sprite', or 'entity'
0, # target id: integer index for menu or grid objs; None for grid/menu
"sprite", # field: 'position', 'size', 'bgcolor', 'textcolor', or 'sprite'
lambda: self.animation_done("demobox1"), #callback: callable once animation is complete
False, #loop: repeat indefinitely
dragon_animation # values: iterable of frames for 'sprite', lerp target for others
)
orc_sprite = 1538 + (64 * (direction + (4 if attacking else 0)))
orc_animation = [orc_sprite + i for i in range((5 if attacking else 4))]
mcrfpy.createAnimation(
1.0, # duration, seconds
"demogrid", # parent: a UIMenu or Grid key
"entity", # target type: 'menu', 'grid', 'caption', 'button', 'sprite', or 'entity'
1, # target id: integer index for menu or grid objs; None for grid/menu
"sprite", # field: 'position', 'size', 'bgcolor', 'textcolor', or 'sprite'
lambda: self.animation_done("demobox1"), #callback: callable once animation is complete
False, #loop: repeat indefinitely
orc_animation # values: iterable of frames for 'sprite', lerp target for others
)
#def move_entity(self, targetgrid, entity_index, position):
# for i, g in enumerate(self.grids):
# if g.title == targetgrid:
# g.entities[entity_index].x = position[0]
# g.entities[entity_index].y = position[1]
# #pts = g.points
# g.visible = True
# mcrfpy.modGrid(g)
# self.grids = mcrfpy.listGrids()
# #self.grids[i].points = pts
# return
def changesprite(self, n):
self.sprite_index += n
self.menus[0].captions[0].text = f"Sprite #{self.sprite_index}"
self.menus[0].sprites[0].sprite_index = self.sprite_index
mcrfpy.modMenu(self.menus[0])
def click(self):
self.clicks += 1
self.menus[0].captions[0].text = f"Clicks: {self.clicks}"
self.menus[0].sprites[0].sprite_index = randint(0, 3)
mcrfpy.modMenu(self.menus[0])
def updatehints(self):
self.menus[2].captions[0].text=mcrfpy.activeGrid()
mcrfpy.modMenu(self.menus[2])
self.menus[3].captions[0].text=mcrfpy.inputMode()
mcrfpy.modMenu(self.menus[3])
#self.menus[4].captions[0].text=f"follow: {mcrfpy.camFollow()}"
self.menus[4].captions[0].text="following" if mcrfpy.camFollow() else "free"
mcrfpy.modMenu(self.menus[4])
self.menus[5].visible = not mcrfpy.camFollow()
mcrfpy.modMenu(self.menus[5])
def gridgen(self):
#print(f"[Python] modifying {len(self.grids[0].points)} grid points")
for p in self.grids[0].points:
#p.color = (randint(0, 255), randint(64, 192), 0)
p.color = (0,0,0)
p.walkable = False
p.transparent = False
room_centers = [(randint(0, self.grids[0].grid_x-1), randint(0, self.grids[0].grid_y-1)) for i in range(20)] + [(17, 3), (20,10) + (20,5)]
#room_centers.append((3, 5))
for r in room_centers:
# random hallway
target = choice(room_centers)
length1 = abs(target[0] - r[0])
xbase = min(target[0], r[0])
for x in range(length1):
gpoint = self.grids[0].at(x, r[1])
if gpoint is None: continue
gpoint.walkable = True
gpoint.transparent = True
gpoint.color = (192, 192, 192)
length2 = abs(target[1] - r[1])
ybase = min(target[1], r[1])
for y in range(length2):
gpoint = self.grids[0].at(r[0], y)
if gpoint is None: continue
gpoint.walkable = True
gpoint.transparent = True
gpoint.color = (192, 192, 192)
for r in room_centers:
#print(r)
room_color = (randint(16, 24)*8, randint(16, 24)*8, randint(16, 24)*8)
#self.grids[0].at(r[0], r[1]).walkable = True
#self.grids[0].at(r[0], r[1]).color = room_color
halfx, halfy = randint(2, 11), randint(2,11)
for p_x in range(r[0] - halfx, r[0] + halfx):
for p_y in range(r[1] - halfy, r[1] + halfy):
gpoint = self.grids[0].at(p_x, p_y)
if gpoint is None: continue
gpoint.walkable = True
gpoint.transparent = True
gpoint.color = room_color
#print()
#print("[Python] Modifying:")
self.grids[0].at(10, 10).color = (255, 255, 255)
self.grids[0].at(10, 10).walkable = False
self.grids[0].visible = True
mcrfpy.modGrid(self.grids[0])
#self.grids = mcrfpy.listGrids()
#print(f"Sent grid: {repr(self.grids[0])}")
#print(f"Received grid: {repr(mcrfpy.listGrids()[0])}")
def animation_done(self, s):
print(f"The `{s}` animation completed.")
#self.menus = mcrfpy.listMenus()
# if (!PyArg_ParseTuple(args, "fsssiOOO", &duration, &parent, &target_type, &target_id, &field, &callback, &loop_obj, &values_obj)) return NULL;
def createAnimation(self):
print(self.menus)
self.menus = mcrfpy.listMenus()
self.menus[0].w = 500
self.menus[0].h = 200
print(self.menus)
mcrfpy.modMenu(self.menus[0])
print(self.menus)
mcrfpy.createAnimation(
3.0, # duration, seconds
"demobox1", # parent: a UIMenu or Grid key
"menu", # target type: 'menu', 'grid', 'caption', 'button', 'sprite', or 'entity'
0, # target id: integer index for menu or grid objs; None for grid/menu
"size", # field: 'position', 'size', 'bgcolor', 'textcolor', or 'sprite'
lambda: self.animation_done("demobox1"), #callback: callable once animation is complete
False, #loop: repeat indefinitely
[150, 100] # values: iterable of frames for 'sprite', lerp target for others
)
def createAnimation2(self):
mcrfpy.createAnimation(
5,
"demobox1",
"sprite",
0,
"sprite",
lambda: self.animation_done("sprite change"),
False,
[0, 1, 2, 1, 2, 0]
)
def keytest():
print("Key tested.")
def keyregistration():
print("Registering 'keytest'")
mcrfpy.registerPyAction("keytest", keytest)
print("Registering input")
print(mcrfpy.registerInputAction(15, "keytest")) # 15 = P
mcrfpy.registerPyAction("player_move_up", lambda: scene.player.move(0, -1))
mcrfpy.registerPyAction("player_move_left", lambda: scene.player.move(-1, 0))
mcrfpy.registerPyAction("player_move_down", lambda: scene.player.move(0, 1))
mcrfpy.registerPyAction("player_move_right", lambda: scene.player.move(1, 0))
mcrfpy.registerInputAction(ord('w') - ord('a'), "player_move_up")
mcrfpy.registerInputAction(ord('a') - ord('a'), "player_move_left")
mcrfpy.registerInputAction(ord('s') - ord('a'), "player_move_down")
mcrfpy.registerInputAction(ord('d') - ord('a'), "player_move_right")
def start():
global scene
#print("TestScene.start called")
scene = TestScene()
mcrfpy.refreshFov()
scene.updatehints()

48
scripts/UIMenu.py Normal file
View File

@ -0,0 +1,48 @@
class Caption:
def __init__(self, text, textsize, color):
self.text = text
self.textsize = textsize
self.color = color
def __repr__(self):
return f"<Caption text={self.text}, textsize={self.textsize}, color={self.color}>"
class Button:
def __init__(self, x, y, w, h, bgcolor, textcolor, text, actioncode):
self.x = x
self.y = y
self.w = w
self.h = h
self.bgcolor = bgcolor
self.textcolor = textcolor
self.text = text
self.actioncode = actioncode
def __repr__(self):
return f"<Button ({self.x}, {self.y}, {self.w}, {self.h}), bgcolor={self.bgcolor}, textcolor={self.textcolor}, actioncode={self.actioncode}>"
class Sprite:
def __init__(self, tex_index, sprite_index, x, y):
self.tex_index = tex_index
self.sprite_index = sprite_index
self.x = x
self.y = y
def __repr__(self):
return f"<Sprite tex_index={self.tex_index}, self.sprite_index={self.sprite_index}, x={self.x}, y={self.y}>"
class UIMenu:
def __init__(self, title, x, y, w, h, bgcolor, visible=False):
self.title = title
self.x = x
self.y = y
self.w = w
self.h = h
self.bgcolor = bgcolor
self.visible = visible
self.captions = []
self.buttons = []
self.sprites = []
def __repr__(self):
return f"<UIMenu title={repr(self.title)}, x={self.x}, y={self.y}, w={self.w}, h={self.h}, bgcolor={self.bgcolor}, visible={self.visible}, {len(self.captions)} captions, {len(self.buttons)} buttons, {len(self.sprites)} sprites>"

10
scripts/engine_user.py Normal file
View File

@ -0,0 +1,10 @@
print("[Python] Attempting import")
import scriptable
print(f"[Python] calling fibonacci(8): {scriptable.fibonacci(8)}")
print(f"[Python] calling fibonacci(15): {scriptable.fibonacci(15)}")
import venv
print("[Python] Importing library installed with pip")
import numpy

37
scripts/test_ui.py Normal file
View File

@ -0,0 +1,37 @@
import mcrfpy
mcrfpy.createTexture("./assets/test_portraits.png", 32, 8, 8)
from random import choice, randint
box_colors = [
(0, 0, 192),
(0, 192, 0),
(192, 0, 0),
(192, 192, 0),
(0, 192, 192),
(192, 0, 192)
]
text_colors = [
(0, 0, 255),
(0, 255, 0),
(255, 0, 0),
(255, 255, 0),
(0, 255, 255),
(255, 0, 255)
]
test_x = 500
test_y = 10
for i in range(40):
ui_name = f"test{i}"
mcrfpy.createMenu(ui_name, test_x, test_y, 400, 200)
mcrfpy.createCaption(ui_name, "Hello There", 18, choice(text_colors))
mcrfpy.createButton(ui_name, 250, 20, 100, 50, choice(box_colors), (0, 0, 0), "asdf", "testaction")
mcrfpy.createSprite(ui_name, 0, randint(0, 3), 650, 60, 5.0)
test_x -= 50
test_y += 50
if (test_x <= 50):
test_x = 500
#print(test_x)

View File

@ -31,116 +31,4 @@ public:
if (a & WHEEL_NEG) factor = -1;
return (a & WHEEL_DEL) * factor;
}
static std::string key_str(sf::Keyboard::Key& keycode)
{
switch(keycode)
{
case sf::Keyboard::Key::Unknown: return "Unknown"; break;
case sf::Keyboard::Key::A: return "A"; break;
case sf::Keyboard::Key::B: return "B"; break;
case sf::Keyboard::Key::C: return "C"; break;
case sf::Keyboard::Key::D: return "D"; break;
case sf::Keyboard::Key::E: return "E"; break;
case sf::Keyboard::Key::F: return "F"; break;
case sf::Keyboard::Key::G: return "G"; break;
case sf::Keyboard::Key::H: return "H"; break;
case sf::Keyboard::Key::I: return "I"; break;
case sf::Keyboard::Key::J: return "J"; break;
case sf::Keyboard::Key::K: return "K"; break;
case sf::Keyboard::Key::L: return "L"; break;
case sf::Keyboard::Key::M: return "M"; break;
case sf::Keyboard::Key::N: return "N"; break;
case sf::Keyboard::Key::O: return "O"; break;
case sf::Keyboard::Key::P: return "P"; break;
case sf::Keyboard::Key::Q: return "Q"; break;
case sf::Keyboard::Key::R: return "R"; break;
case sf::Keyboard::Key::S: return "S"; break;
case sf::Keyboard::Key::T: return "T"; break;
case sf::Keyboard::Key::U: return "U"; break;
case sf::Keyboard::Key::V: return "V"; break;
case sf::Keyboard::Key::W: return "W"; break;
case sf::Keyboard::Key::X: return "X"; break;
case sf::Keyboard::Key::Y: return "Y"; break;
case sf::Keyboard::Key::Z: return "Z"; break;
case sf::Keyboard::Key::Num0: return "Num0"; break;
case sf::Keyboard::Key::Num1: return "Num1"; break;
case sf::Keyboard::Key::Num2: return "Num2"; break;
case sf::Keyboard::Key::Num3: return "Num3"; break;
case sf::Keyboard::Key::Num4: return "Num4"; break;
case sf::Keyboard::Key::Num5: return "Num5"; break;
case sf::Keyboard::Key::Num6: return "Num6"; break;
case sf::Keyboard::Key::Num7: return "Num7"; break;
case sf::Keyboard::Key::Num8: return "Num8"; break;
case sf::Keyboard::Key::Num9: return "Num9"; break;
case sf::Keyboard::Key::Escape: return "Escape"; break;
case sf::Keyboard::Key::LControl: return "LControl"; break;
case sf::Keyboard::Key::LShift: return "LShift"; break;
case sf::Keyboard::Key::LAlt: return "LAlt"; break;
case sf::Keyboard::Key::LSystem: return "LSystem"; break;
case sf::Keyboard::Key::RControl: return "RControl"; break;
case sf::Keyboard::Key::RShift: return "RShift"; break;
case sf::Keyboard::Key::RAlt: return "RAlt"; break;
case sf::Keyboard::Key::RSystem: return "RSystem"; break;
case sf::Keyboard::Key::Menu: return "Menu"; break;
case sf::Keyboard::Key::LBracket: return "LBracket"; break;
case sf::Keyboard::Key::RBracket: return "RBracket"; break;
case sf::Keyboard::Key::Semicolon: return "Semicolon"; break;
case sf::Keyboard::Key::Comma: return "Comma"; break;
case sf::Keyboard::Key::Period: return "Period"; break;
case sf::Keyboard::Key::Apostrophe: return "Apostrophe"; break;
case sf::Keyboard::Key::Slash: return "Slash"; break;
case sf::Keyboard::Key::Backslash: return "Backslash"; break;
case sf::Keyboard::Key::Grave: return "Grave"; break;
case sf::Keyboard::Key::Equal: return "Equal"; break;
case sf::Keyboard::Key::Hyphen: return "Hyphen"; break;
case sf::Keyboard::Key::Space: return "Space"; break;
case sf::Keyboard::Key::Enter: return "Enter"; break;
case sf::Keyboard::Key::Backspace: return "Backspace"; break;
case sf::Keyboard::Key::Tab: return "Tab"; break;
case sf::Keyboard::Key::PageUp: return "PageUp"; break;
case sf::Keyboard::Key::PageDown: return "PageDown"; break;
case sf::Keyboard::Key::End: return "End"; break;
case sf::Keyboard::Key::Home: return "Home"; break;
case sf::Keyboard::Key::Insert: return "Insert"; break;
case sf::Keyboard::Key::Delete: return "Delete"; break;
case sf::Keyboard::Key::Add: return "Add"; break;
case sf::Keyboard::Key::Subtract: return "Subtract"; break;
case sf::Keyboard::Key::Multiply: return "Multiply"; break;
case sf::Keyboard::Key::Divide: return "Divide"; break;
case sf::Keyboard::Key::Left: return "Left"; break;
case sf::Keyboard::Key::Right: return "Right"; break;
case sf::Keyboard::Key::Up: return "Up"; break;
case sf::Keyboard::Key::Down: return "Down"; break;
case sf::Keyboard::Key::Numpad0: return "Numpad0"; break;
case sf::Keyboard::Key::Numpad1: return "Numpad1"; break;
case sf::Keyboard::Key::Numpad2: return "Numpad2"; break;
case sf::Keyboard::Key::Numpad3: return "Numpad3"; break;
case sf::Keyboard::Key::Numpad4: return "Numpad4"; break;
case sf::Keyboard::Key::Numpad5: return "Numpad5"; break;
case sf::Keyboard::Key::Numpad6: return "Numpad6"; break;
case sf::Keyboard::Key::Numpad7: return "Numpad7"; break;
case sf::Keyboard::Key::Numpad8: return "Numpad8"; break;
case sf::Keyboard::Key::Numpad9: return "Numpad9"; break;
case sf::Keyboard::Key::F1: return "F1"; break;
case sf::Keyboard::Key::F2: return "F2"; break;
case sf::Keyboard::Key::F3: return "F3"; break;
case sf::Keyboard::Key::F4: return "F4"; break;
case sf::Keyboard::Key::F5: return "F5"; break;
case sf::Keyboard::Key::F6: return "F6"; break;
case sf::Keyboard::Key::F7: return "F7"; break;
case sf::Keyboard::Key::F8: return "F8"; break;
case sf::Keyboard::Key::F9: return "F9"; break;
case sf::Keyboard::Key::F10: return "F10"; break;
case sf::Keyboard::Key::F11: return "F11"; break;
case sf::Keyboard::Key::F12: return "F12"; break;
case sf::Keyboard::Key::F13: return "F13"; break;
case sf::Keyboard::Key::F14: return "F14"; break;
case sf::Keyboard::Key::F15: return "F15"; break;
case sf::Keyboard::Key::Pause: return "Pause"; break;
default:
return "Any";
break;
}
}
};

124
src/Animation.cpp Normal file
View File

@ -0,0 +1,124 @@
#include "Animation.h"
Animation::Animation(float _d, std::function<void()> _cb, bool _l)
:duration(_d), callback(_cb), loop(_l), elapsed(0.0f) {}
// linear interpolation constructor
template<typename T>
LerpAnimation<T>::LerpAnimation(float _d, T _ev, T _sv, std::function<void()> _cb, std::function<void(T)> _w, bool _l)
:Animation(_d, _cb, _l), //duration(_d), target(_t), callback(_cb), loop(_l),elapsed(0.0f),
startvalue(_sv), endvalue(_ev), write(_w) {}
// discrete values constructor
template<typename T>
DiscreteAnimation<T>::DiscreteAnimation(float _d, std::vector<T> _v, std::function<void()> _cb, std::function<void(T)> _w, bool _l)
:Animation(_d, _cb, _l), //duration(_d), target(_t), callback(_cb), loop(_l), elapsed(0.0f),
index(0), nonelapsed(0.0f), values(_v), write(_w) {
timestep = _d / _v.size();
}
/* // don't call virtual functions (like cancel()) from base class destructor
* // child classes destructors' are called first anyway
Animation::~Animation() {
// deconstructor sets target to desired end state (no partial values)
cancel();
}
*/
template<>
void LerpAnimation<std::string>::lerp() {
//*(std::string*)target = ;
write(endvalue.substr(0, endvalue.length() * (elapsed / duration)));
}
template<>
void LerpAnimation<int>::lerp() {
int delta = endvalue - startvalue;
//*(int*)target = ;
write(startvalue + (elapsed / duration * delta));
}
template<>
void LerpAnimation<float>::lerp() {
int delta = endvalue - startvalue;
//*(float*)target = ;
write(startvalue + (elapsed / duration * delta));
}
template<>
void LerpAnimation<sf::Vector2f>::lerp() {
//std::cout << "sf::Vector2f implementation of lerp." << std::endl;
int delta_x = endvalue.x - startvalue.x;
int delta_y = endvalue.y - startvalue.y;
//std::cout << "Start: " << startvalue.x << ", " << startvalue.y << "; End: " << endvalue.x << ", " << endvalue.y << std::endl;
//std::cout << "Delta: " << delta_x << ", " << delta_y << std::endl;
//((sf::Vector2f*)target)->x = startvalue.x + (elapsed / duration * delta_x);
//((sf::Vector2f*)target)->y = startvalue.y + (elapsed / duration * delta_y);
write(sf::Vector2f(startvalue.x + (elapsed / duration * delta_x),
startvalue.y + (elapsed / duration * delta_y)));
}
template<>
void LerpAnimation<sf::Vector2i>::lerp() {
int delta_x = endvalue.x - startvalue.y;
int delta_y = endvalue.y - startvalue.y;
//((sf::Vector2i*)target)->x = startvalue.x + (elapsed / duration * delta_x);
//((sf::Vector2i*)target)->y = startvalue.y + (elapsed / duration * delta_y);
write(sf::Vector2i(startvalue.x + (elapsed / duration * delta_x),
startvalue.y + (elapsed / duration * delta_y)));
}
template<typename T>
void LerpAnimation<T>::step(float delta) {
if (complete) return;
elapsed += delta;
//std::cout << "LerpAnimation step function. Elapsed: " << elapsed <<std::endl;
lerp();
if (isDone()) { callback(); complete = true; cancel(); }; //use the exact value, not my math
}
template<typename T>
void DiscreteAnimation<T>::step(float delta)
{
if (complete) return;
nonelapsed += delta;
//std::cout << "Nonelapsed: " << nonelapsed << " elapsed (pre-add): " << elapsed << " timestep: " << timestep << " duration: " << duration << " index: " << index << std::endl;
if (nonelapsed < timestep) return;
//std::cout << "values size: " << values.size() << " isDone(): " << isDone() << std::endl;
if (elapsed > duration && !complete) {callback(); complete = true; return; }
elapsed += nonelapsed; // or should it be += timestep?
if (index == values.size() - 1) return;
nonelapsed = 0; // or should it -= timestep?
index++;
//*(T*)target = values[index];
write(values[index]);
}
template<typename T>
void LerpAnimation<T>::cancel() {
//*(T*)target = endvalue;
write(endvalue);
}
template<typename T>
void DiscreteAnimation<T>::cancel() {
//*(T*)target = values[values.size() - 1];
write(values[values.size() - 1]);
}
bool Animation::isDone() {
return elapsed + Animation::EPSILON >= duration;
}
namespace animation_template_implementations {
// instantiate to compile concrete templates
//LerpAnimation<sf::Vector2f> implement_vector2f;
auto implement_v2f_const = LerpAnimation<sf::Vector2<float>>(4.0, sf::Vector2<float>(), sf::Vector2f(1,1), [](){}, [](sf::Vector2f v){}, false);
auto implement_disc_i = DiscreteAnimation<int>(3.0, std::vector<int>{0},[](){},[](int){},false);
//LerpAnimation<sf::Vector2i> implement_vector2i;
//LerpAnimation<int> implment_int;
//LerpAnimation<std::string> implment_string;
//LerpAnimation<float> implement_float;
//DiscreteAnimation<int> implement_int_discrete;
}

50
src/Animation.h Normal file
View File

@ -0,0 +1,50 @@
#pragma once
#include "Common.h"
#include <functional>
class Animation
{
protected:
static constexpr float EPSILON = 0.05;
float duration, elapsed;
std::function<void()> callback;
bool loop;
bool complete=false;
public:
//Animation(float, T, T*, std::function<void()>, bool); // lerp
//Animation(float, std::vector<T>, T*, std::function<void()>, bool); // discrete
Animation(float, std::function<void()>, bool);
//Animation() {};
virtual void step(float) = 0;
virtual void cancel() = 0;
bool isDone();
};
template<typename T>
class LerpAnimation: public Animation
{
T startvalue, endvalue;
std::function<void(T)> write;
void lerp();
public:
~LerpAnimation() { cancel(); }
LerpAnimation(float, T, T, std::function<void()>, std::function<void(T)>, bool);
//LerpAnimation() {};
void step(float) override final;
void cancel() override final;
};
template<typename T>
class DiscreteAnimation: public Animation
{
std::vector<T> values;
std::function<void(T)> write;
float nonelapsed, timestep;
int index;
public:
DiscreteAnimation(float, std::vector<T>, std::function<void()>, std::function<void(T)>, bool);
DiscreteAnimation() {};
~DiscreteAnimation() { cancel(); }
void step(float) override final;
void cancel() override final;
};

24
src/Button.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "Button.h"
void Button::render(sf::RenderWindow & window)
{
window.draw(rect);
window.draw(caption);
}
Button::Button(int x, int y, int w, int h,
sf::Color _background, sf::Color _textcolor,
const char * _caption, sf::Font & font,
const char * _action)
{
rect.setPosition(sf::Vector2f(x, y));
rect.setSize(sf::Vector2f(w, h));
rect.setFillColor(_background);
caption.setFillColor(_textcolor);
caption.setPosition(sf::Vector2f(x, y));
caption.setString(_caption);
caption.setFont(font);
action = _action;
}

35
src/Button.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include "Common.h"
class Button
{
protected:
public:
// TODO / JankMode: setter & getter for these three fields
// were protected, but directly changing them should be...fine?
sf::RectangleShape rect;
sf::Text caption;
std::string action;
Button() {};
Button(int x, int y, int w, int h,
sf::Color _background, sf::Color _textcolor,
const char * _caption, sf::Font & font,
const char * _action);
void setPosition(sf::Vector2f v) { rect.setPosition(v); caption.setPosition(v); }
void setSize(sf::Vector2f & v) { rect.setSize(v); }
void setBackground(sf::Color c) { rect.setFillColor(c); }
void setCaption(std::string & s) { caption.setString(s); }
void setTextColor(sf::Color c) { caption.setFillColor(c); }
void render(sf::RenderWindow & window);
auto contains(sf::Vector2i p) { return rect.getGlobalBounds().contains(p.x, p.y); }
auto contains(sf::Vector2f rel, sf::Vector2i p) {
return rect.getGlobalBounds().contains(p.x - rel.x, p.y - rel.y);
}
auto getAction() { return action; }
private:
};

138
src/Components.h Normal file
View File

@ -0,0 +1,138 @@
#pragma once
#include "Common.h"
#include "IndexSprite.h"
#include "Grid.h"
//#include "Item.h"
#include "Python.h"
#include <list>
class CGrid
{
public:
bool visible;
int x, y;
IndexSprite indexsprite;
Grid* grid;
CGrid(Grid* _g, int _ti, int _si, int _x, int _y, bool _v)
: visible(_v), x(_x), y(_y), grid(_g), indexsprite(_ti, _si, _x, _y, 1.0) {}
};
class CInventory
{
public:
//std::list<std::shared_ptr<Item>>;
int x;
};
class CBehavior
{
public:
PyObject* object;
CBehavior(PyObject* p): object(p) {}
};
/*
class CCombatant
{
public:
int hp;
int maxhp;
}
class CCaster
{
public:
int mp;
int maxmp;
}
class CLevel
{
int constitution; // +HP, resist effects
int strength; // +damage, block/parry
int dexterity; // +speed, dodge
int intelligence; // +MP, spell resist
int wisdom; // +damage, deflect
int luck; // crit, loot
}
*/
/*
class CTransform
{
public:
Vec2 pos = { 0.0, 0.0 };
Vec2 velocity = { 0.0, 0.0 };
float angle = 0;
CTransform(const Vec2 & p, const Vec2 & v, float a)
: pos(p), velocity(v), angle(a) {}
};
*/
/*
class CShape
{
public:
sf::CircleShape circle;
CShape(float radius, int points, const sf::Color & fill, const sf::Color & outline, float thickness)
: circle(radius, points)
{
circle.setFillColor(fill);
circle.setOutlineColor(outline);
circle.setOutlineThickness(thickness);
circle.setOrigin(radius, radius);
}
};
class CCollision
{
public:
float radius = 0;
CCollision(float r)
: radius(r) {}
};
class CScore
{
public:
int score = 0;
CScore(int s)
: score(s) {}
};
class CLifespan
{
public:
int remaining = 0;
int total = 0;
CLifespan(int t)
: remaining(t), total(t) {}
};
class CInput
{
public:
bool up = false;
bool left = false;
bool right = false;
bool down = false;
bool fire = false;
CInput() {}
};
class CSteer
{
public:
sf::Vector2f position;
sf::Vector2f velocity;
float v_max;
float dv_max;
float theta_max;
float dtheta_max;
};
*/

25
src/Entity.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "Entity.h"
Entity::Entity(const size_t i, const std::string & t)
: m_id(i), m_tag(t) {}
bool Entity::isActive() const
{
return m_active;
}
const std::string & Entity::tag() const
{
return m_tag;
}
const size_t Entity::id() const
{
return m_id;
}
void Entity::destroy()
{
m_active = false;
}

35
src/Entity.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include "Common.h"
#include "Components.h"
class Entity
{
friend class EntityManager;
bool m_active = true;
size_t m_id = 0;
std::string m_tag = "default";
//constructor and destructor
Entity(const size_t id, const std::string & t);
public:
// component pointers
//std::shared_ptr<CTransform> cTransform;
//std::shared_ptr<CShape> cShape;
//std::shared_ptr<CCollision> cCollision;
//std::shared_ptr<CInput> cInput;
//std::shared_ptr<CScore> cScore;
//std::shared_ptr<CLifespan> cLifespan;
std::shared_ptr<CGrid> cGrid;
std::shared_ptr<CInventory> cInventory;
std::shared_ptr<CBehavior> cBehavior;
//private member access functions
bool isActive() const;
const std::string & tag() const;
const size_t id() const;
void destroy();
};

73
src/EntityManager.cpp Normal file
View File

@ -0,0 +1,73 @@
#include "EntityManager.h"
EntityManager::EntityManager()
:m_totalEntities(0) {}
void EntityManager::update()
{
//TODO: add entities from m_entitiesToAdd to all vector / tag map
removeDeadEntities(m_entities);
// C++17 way of iterating!
for (auto& [tag, entityVec] : m_entityMap)
{
removeDeadEntities(entityVec);
}
for (auto& e : m_entitiesToAdd)
{
m_entities.push_back(e);
m_entityMap[e->tag()].push_back(e);
}
//if (m_entitiesToAdd.size())
// m_entitiesToAdd.erase(m_entitiesToAdd.begin(), m_entitiesToAdd.end());
m_entitiesToAdd = EntityVec();
}
void EntityManager::removeDeadEntities(EntityVec & vec)
{
EntityVec survivors; // New vector
for (auto& e : m_entities){
if (e->isActive()) survivors.push_back(e); // populate new vector
else if (e->cGrid) { // erase vector from grid
for( auto it = e->cGrid->grid->entities.begin(); it != e->cGrid->grid->entities.end(); it++){
if( *it == e ){
e->cGrid->grid->entities.erase( it );
break;
}
}
}
}
//std::cout << "All entities: " << m_entities.size() << " Survivors: " << survivors.size() << std::endl;
m_entities = survivors; // point to new vector
for (auto& [tag, entityVec] : m_entityMap)
{
EntityVec tag_survivors; // New vector
for (auto& e : m_entityMap[tag])
{
if (e->isActive()) tag_survivors.push_back(e); // populate new vector
}
m_entityMap[tag] = tag_survivors; // point to new vector
//std::cout << tag << " entities: " << m_entityMap[tag].size() << " Survivors: " << tag_survivors.size() << std::endl;
}
}
std::shared_ptr<Entity> EntityManager::addEntity(const std::string & tag)
{
// create the entity shared pointer
auto entity = std::shared_ptr<Entity>(new Entity(m_totalEntities++, tag));
m_entitiesToAdd.push_back(entity);
return entity;
}
const EntityVec & EntityManager::getEntities()
{
return m_entities;
}
const EntityVec & EntityManager::getEntities(const std::string & tag)
{
return m_entityMap[tag];
}

25
src/EntityManager.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include "Common.h"
#include "Entity.h"
typedef std::vector<std::shared_ptr<Entity>> EntityVec;
typedef std::map<std::string, EntityVec> EntityMap;
class EntityManager
{
EntityVec m_entities;
EntityVec m_entitiesToAdd;
EntityMap m_entityMap;
size_t m_totalEntities;
void removeDeadEntities(EntityVec & vec);
public:
EntityManager();
void update();
std::shared_ptr<Entity> addEntity(const std::string & tag);
const EntityVec & getEntities();
const EntityVec & getEntities(const std::string & tag);
};

View File

@ -1,7 +1,9 @@
#include "GameEngine.h"
#include "MenuScene.h"
//#include "UITestScene.h"
#include "ActionCode.h"
#include "McRFPy_API.h"
#include "PyScene.h"
#include "PythonScene.h"
#include "UITestScene.h"
#include "Resources.h"
@ -9,44 +11,39 @@ GameEngine::GameEngine()
{
Resources::font.loadFromFile("./assets/JetbrainsMono.ttf");
Resources::game = this;
window_title = "McRogueFace - 7DRL 2024 Engine Demo";
window.create(sf::VideoMode(1024, 768), window_title, sf::Style::Titlebar | sf::Style::Close);
window_title = "McRogueFace - r/RoguelikeDev Tutorial Run";
window.create(sf::VideoMode(1024, 768), window_title);
visible = window.getDefaultView();
window.setFramerateLimit(30);
scene = "uitest";
//std::cout << "Constructing MenuScene" << std::endl;
scenes["menu"] = new MenuScene(this);
scenes["uitest"] = new UITestScene(this);
//std::cout << "Constructed MenuScene" <<std::endl;
//scenes["play"] = new UITestScene(this);
//api = new McRFPy_API(this);
McRFPy_API::game = this;
McRFPy_API::api_init();
McRFPy_API::executePyString("import mcrfpy");
McRFPy_API::executeScript("scripts/game.py");
//McRFPy_API::executePyString("from UIMenu import *");
//McRFPy_API::executePyString("from Grid import *");
//scenes["py"] = new PythonScene(this, "TestScene");
IndexSprite::game = this;
clock.restart();
runtime.restart();
}
Scene* GameEngine::currentScene() { return scenes[scene]; }
void GameEngine::changeScene(std::string s)
{
/*std::cout << "Current scene is now '" << s << "'\n";*/
if (scenes.find(s) != scenes.end())
scene = s;
else
std::cout << "Attempted to change to a scene that doesn't exist (`" << s << "`)" << std::endl;
}
void GameEngine::changeScene(std::string s) { std::cout << "Current scene is now '" << s << "'\n"; scene = s; }
void GameEngine::quit() { running = false; }
void GameEngine::setPause(bool p) { paused = p; }
sf::Font & GameEngine::getFont() { /*return font; */ return Resources::font; }
sf::RenderWindow & GameEngine::getWindow() { return window; }
void GameEngine::createScene(std::string s) { scenes[s] = new PyScene(this); }
void GameEngine::setWindowScale(float multiplier)
{
window.setSize(sf::Vector2u(1024 * multiplier, 768 * multiplier)); // 7DRL 2024: window scaling
//window.create(sf::VideoMode(1024 * multiplier, 768 * multiplier), window_title, sf::Style::Titlebar | sf::Style::Close);
}
void GameEngine::run()
{
float fps = 0.0;
@ -54,12 +51,11 @@ void GameEngine::run()
while (running)
{
currentScene()->update();
testTimers();
sUserInput();
if (!paused)
{
}
currentScene()->render();
currentScene()->sRender();
currentFrame++;
frameTime = clock.restart().asSeconds();
fps = 1 / frameTime;
@ -67,44 +63,6 @@ void GameEngine::run()
}
}
void GameEngine::manageTimer(std::string name, PyObject* target, int interval)
{
auto it = timers.find(name);
if (it != timers.end()) // overwrite existing
{
if (target == NULL || target == Py_None)
{
// Delete: Overwrite existing timer with one that calls None. This will be deleted in the next timer check
// 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;
}
}
if (target == NULL || target == Py_None)
{
std::cout << "Refusing to initialize timer to None. It's not an error, it's just pointless." << std::endl;
return;
}
timers[name] = std::make_shared<PyTimerCallable>(target, interval, runtime.getElapsedTime().asMilliseconds());
}
void GameEngine::testTimers()
{
int now = runtime.getElapsedTime().asMilliseconds();
auto it = timers.begin();
while (it != timers.end())
{
it->second->test(now);
if (it->second->isNone())
{
it = timers.erase(it);
}
else
it++;
}
}
void GameEngine::sUserInput()
{
sf::Event event;
@ -114,20 +72,12 @@ void GameEngine::sUserInput()
int actionCode = 0;
if (event.type == sf::Event::Closed) { running = false; continue; }
// TODO: add resize event to Scene to react; call it after constructor too, maybe
else if (event.type == sf::Event::Resized) {
continue; // 7DRL short circuit. Resizing manually disabled
/*
sf::FloatRect area(0.f, 0.f, event.size.width, event.size.height);
//sf::FloatRect area(0.f, 0.f, 1024.f, 768.f); // 7DRL 2024: attempt to set scale appropriately
//sf::FloatRect area(0.f, 0.f, event.size.width, event.size.width * 0.75);
visible = sf::View(area);
window.setView(visible);
//window.setSize(sf::Vector2u(event.size.width, event.size.width * 0.75)); // 7DRL 2024: window scaling
std::cout << "Visible area set to (0, 0, " << event.size.width << ", " << event.size.height <<")"<<std::endl;
//std::cout << "Visible area set to (0, 0, " << event.size.width << ", " << event.size.height <<")"<<std::endl;
actionType = "resize";
//window.setSize(sf::Vector2u(event.size.width, event.size.width * 0.75)); // 7DRL 2024: window scaling
*/
}
else if (event.type == sf::Event::KeyPressed || event.type == sf::Event::MouseButtonPressed || event.type == sf::Event::MouseWheelScrolled) actionType = "start";
@ -164,23 +114,6 @@ void GameEngine::sUserInput()
std::string name = currentScene()->action(actionCode);
currentScene()->doAction(name, actionType);
}
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* retval = PyObject_Call(currentScene()->key_callable, args, NULL);
if (!retval)
{
std::cout << "key_callable 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 << "key_callable returned a non-None value. It's not an error, it's just not being saved or used." << std::endl;
}
*/
}
else
{
//std::cout << "[GameEngine] Action not registered for input: " << actionCode << ": " << actionType << std::endl;

View File

@ -1,16 +1,17 @@
#pragma once
#include "Common.h"
#include "Entity.h"
#include "EntityManager.h"
#include "Scene.h"
#include "McRFPy_API.h"
#include "IndexTexture.h"
#include "Timer.h"
#include "PyCallable.h"
class GameEngine
{
sf::RenderWindow window;
sf::Font font;
std::string scene;
std::map<std::string, Scene*> scenes;
bool running = true;
bool paused = false;
@ -20,17 +21,10 @@ class GameEngine
float frameTime;
std::string window_title;
sf::Clock runtime;
//std::map<std::string, Timer> timers;
std::map<std::string, std::shared_ptr<PyTimerCallable>> timers;
void testTimers();
public:
std::string scene;
GameEngine();
Scene* currentScene();
void changeScene(std::string);
void createScene(std::string);
void quit();
void setPause(bool);
sf::Font & getFont();
@ -40,8 +34,6 @@ public:
int getFrame() { return currentFrame; }
float getFrameTime() { return frameTime; }
sf::View getView() { return visible; }
void manageTimer(std::string, PyObject*, int);
void setWindowScale(float);
// global textures for scripts to access
std::vector<IndexTexture> textures;

340
src/Grid.cpp Normal file
View File

@ -0,0 +1,340 @@
#include "Grid.h"
#include <cmath>
#include "Entity.h"
GridPoint::GridPoint():
color(0, 0, 0, 0), walkable(false), tilesprite(-1), transparent(false), visible(false), discovered(false), color_overlay(0,0,0,255), tile_overlay(-1), uisprite(-1)
{};
void Grid::setSprite(int ti)
{
int tx = ti % texture_width, ty = ti / texture_width;
sprite.setTextureRect(sf::IntRect(tx * grid_size, ty * grid_size, grid_size, grid_size));
}
Grid::Grid(int gx, int gy, int gs, int _x, int _y, int _w, int _h):
grid_size(gs),
grid_x(gx), grid_y(gy),
zoom(1.0f), center_x((gx/2) * gs), center_y((gy/2) * gs),
texture_width(12), texture_height(11), visible(false)
{
//grid_size = gs;
//zoom = 1.0f;
//grid_x = gx;
//grid_y = gy;
tcodmap = new TCODMap(gx, gy);
points.resize(gx*gy);
box.setSize(sf::Vector2f(_w, _h));
box.setPosition(sf::Vector2f(_x, _y));
box.setFillColor(sf::Color(0,0,0,0));
renderTexture.create(_w, _h);
texture.loadFromFile("./assets/kenney_tinydungeon.png");
texture.setSmooth(false);
sprite.setTexture(texture);
//output.setSize(box.getSize());
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());
// Show one texture at a time
sprite.setTexture(texture);
}
void Grid::refreshTCODmap() {
//int total = 0, walkable = 0, transparent = 0;
for (int x = 0; x < grid_x; x++) {
for (int y = 0; y < grid_y; y++) {
auto p = at(x, y);
//total++; if (p.walkable) walkable++; if (p.transparent) transparent++;
tcodmap->setProperties(x, y, p.transparent, p.walkable);
}
}
//std::cout << "Map refreshed: " << total << " squares, " << walkable << "walkable, " << transparent << " transparent" << std::endl;
}
void Grid::refreshTCODsight(int x, int y) {
tcodmap->computeFov(x,y, 0, true, FOV_PERMISSIVE_8);
for (int x = 0; x < grid_x; x++) {
for (int y = 0; y < grid_y; y++) {
auto& p = at(x, y);
if (p.visible && !tcodmap->isInFov(x, y)) {
p.discovered = true;
p.visible = false;
} else if (!p.visible && tcodmap->isInFov(x,y)) {
p.discovered = true;
p.visible = true;
}
}
}
}
bool Grid::inBounds(int x, int y) {
return (x >= 0 && y >= 0 && x < grid_x && y < grid_y);
}
void Grid::screenToGrid(int sx, int sy, int& gx, int& gy) {
float center_x_sq = center_x / grid_size;
float center_y_sq = center_y / grid_size;
float width_sq = box.getSize().x / (grid_size * zoom);
float height_sq = box.getSize().y / (grid_size * zoom);
float left_edge = center_x_sq - (width_sq / 2.0);
float right_edge = center_x_sq + (width_sq / 2.0);
float top_edge = center_y_sq - (height_sq / 2.0);
float bottom_edge = center_y_sq + (height_sq / 2.0);
float grid_px = zoom * grid_size;
//std::cout << "##############################" <<
// "\nscreen coord: (" << sx << ", " << sy << ")" << std::endl;
sx -= box.getPosition().x;
sy -= box.getPosition().y;
//std::cout << "box coord: (" << sx << ", " << sy << ")" << std::endl;
float mouse_x_sq = sx / grid_px;
float mouse_y_sq = sy / grid_px;
float ans_x = mouse_x_sq + left_edge;
float ans_y = mouse_y_sq + top_edge;
// compare integer method with this (mostly working) one
//int diff_realpixel_x = box.getSize().x / 2.0 - sx;
//int diff_realpixel_y = box.getSize().y / 2.0 - sy;
int left_spritepixels = center_x - (box.getSize().x / 2.0 / zoom);
int top_spritepixels = center_y - (box.getSize().y / 2.0 / zoom);
std::cout << "Float method got ans (" << ans_x << ", " << ans_y << ")"
<< std::endl << "Int method px (" << left_spritepixels + (sx/zoom) << ", " <<
top_spritepixels + (sy/zoom) << ")" << std::endl <<
"Int grid (" << (left_spritepixels + (sx/zoom) ) / grid_size <<
", " << (top_spritepixels + (sy/zoom)) / grid_size << ")" <<
std::endl;
// casting float turns -0.5 to 0; I want any negative coord to be OOB
if (ans_x < 0) ans_x = -1;
if (ans_y < 0) ans_y = -1;
gx = ans_x;
gy = ans_y;
/*
std::cout <<
"C: (" << center_x << ", " << center_y << ")" << std::endl <<
"W: " << width_sq << " H: " << height_sq << std::endl <<
"L: " << left_edge << " T: " << top_edge << std::endl <<
"R: " << right_edge << " B: " << bottom_edge << std::endl <<
"Grid Px: " << grid_px << "( zoom: " << zoom << ")" << std::endl <<
"answer: G(" << ans_x << ", " << ans_y << ")" << std::endl <<
"##############################" <<
std::endl;
*/
}
void Grid::renderPxToGrid(int sx, int sy, int& gx, int& gy) {
// just like screen px coversion, but no offset by grid's position
float center_x_sq = center_x / grid_size;
float center_y_sq = center_y / grid_size;
float width_sq = box.getSize().x / (grid_size * zoom);
float height_sq = box.getSize().y / (grid_size * zoom);
int width_px = box.getSize().x / 2.0;
int height_px = box.getSize().y / 2.0;
float left_edge = center_x_sq - (width_sq / 2.0);
float top_edge = center_y_sq - (height_sq / 2.0);
float grid_px = zoom * grid_size;
float sx_sq = sx / grid_px;
float sy_sq = sy / grid_px;
float ans_x = sx_sq + left_edge;
float ans_y = sy_sq + top_edge;
if (ans_x < 0) ans_x = -1;
if (ans_y < 0) ans_y = -1;
gx = ans_x;
gy = ans_y;
}
void Grid::integerGrid(float fx, float fy, int& gx, int& gy) {
if (fx < 0) fx -= 0.5;
if (fy < 0) fy -= 0.5;
gx = fx;
gy = fy;
}
void Grid::gridToRenderPx(int gx, int gy, int& sx, int& sy) {
// integer grid square (gx, gy) - what pixel (on rendertexture)
// should it's top left corner be at (the sprite's position)
// eff_gridsize = grid_size * zoom
// if center_x = 161, and grid_size is 16, that's 10 + 1/16 sprites
// center_x - (box.getSize().x / 2 / zoom) = left edge (in px)
// gx * eff_gridsize = pixel position without panning
// pixel_gx - left_edge = grid's render position
sx = (gx * grid_size * zoom) - (center_x - (box.getSize().x / 2.0 / zoom));
sy = (gy * grid_size * zoom) - (center_y - (box.getSize().y / 2.0 / zoom));
}
void Grid::render(sf::RenderWindow & window)
{
renderTexture.clear();
//renderTexture.draw(box);
// sprites that are visible according to zoom, center_x, center_y, and box width
float center_x_sq = center_x / grid_size;
float center_y_sq = center_y / grid_size;
float width_sq = box.getSize().x / (grid_size * zoom);
float height_sq = box.getSize().y / (grid_size * zoom);
float left_edge = center_x_sq - (width_sq / 2.0);
//float right_edge = center_x_sq + (width_sq / 2.0);
float top_edge = center_y_sq - (height_sq / 2.0);
//float bottom_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(grid_size * zoom, 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;
//for (float x = (left_edge >= 0 ? left_edge : 0);
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)
{
// Converting everything to integer pixels to avoid jitter
//auto pixel_pos = sf::Vector2f(
// (x - left_edge) * (zoom * grid_size),
// (y - top_edge) * (zoom * grid_size));
// This failed horribly:
//int gx, gy; integerGrid(x, y, gx, gy);
//int px_x, px_y; gridToRenderPx(gx, gy, px_x, px_y);
//auto pixel_pos = sf::Vector2f(px_x, px_y);
// this draws coherently, but the coordinates
// don't match up with the mouse cursor function
// jitter not eliminated
auto pixel_pos = sf::Vector2f(
(x*grid_size - left_spritepixels) * zoom,
(y*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);
}
}
}
for (auto e : entities) {
auto drawent = e->cGrid->indexsprite.drawable();
drawent.setScale(zoom, zoom);
auto pixel_pos = sf::Vector2f(
(drawent.getPosition().x*grid_size - left_spritepixels) * zoom,
(drawent.getPosition().y*grid_size - top_spritepixels) * zoom );
drawent.setPosition(pixel_pos);
renderTexture.draw(drawent);
}
// loop again and draw on top of entities
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*grid_size - left_spritepixels) * zoom,
(y*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();
window.draw(output);
}
GridPoint& Grid::at(int x, int y)
{
return points[y * grid_x + x];
}

56
src/Grid.h Normal file
View File

@ -0,0 +1,56 @@
#pragma once
#include "Common.h"
#include "libtcod.h"
//#include "Entity.h"
class Entity; // forward declare
class GridPoint
{
public:
// Layers: color, walkable, tilesprite, transparent, visible, discovered, overlay, uisprite
sf::Color color;
bool walkable;
int tilesprite;
bool transparent, visible, discovered;
sf::Color color_overlay;
int tile_overlay, uisprite;
GridPoint();
};
class Grid
{
private:
public:
Grid();
sf::RectangleShape box; // view on window
bool visible;
sf::Texture texture;
sf::Sprite sprite, output;
sf::RenderTexture renderTexture;
TCODMap* tcodmap;
void setSprite(int);
const int texture_width, texture_height;
auto contains(sf::Vector2i p) { return box.getGlobalBounds().contains(p.x, p.y); }
Grid(int gx, int gy, int gs, int _x, int _y, int _w, int _h);
int grid_x, grid_y; // rectangle map size (integer - sprites)
int grid_size; // pixel size of 1 sprite
float zoom;
int center_x, center_y; // center in 1.0x Pixels
std::vector<GridPoint> points; // grid visible contents
std::vector<std::shared_ptr<Entity>> entities;
void render(sf::RenderWindow&); // draw to screen
GridPoint& at(int, int);
bool inBounds(int, int);
void screenToGrid(int, int, int&, int&);
void renderPxToGrid(int, int, int&, int&);
void gridToRenderPx(int, int, int&, int&);
void integerGrid(float, float, int&, int&);
void refreshTCODmap();
void refreshTCODsight(int, int);
TCODDijkstra *dijkstra; //= new TCODDijkstra(myMap);
};

24
src/IndexSprite.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "IndexSprite.h"
#include "GameEngine.h"
//int texture_index, sprite_index, x, y;
GameEngine* IndexSprite::game;
sf::Sprite IndexSprite::drawable()
{
sf::Sprite s;
auto& tex = IndexSprite::game->textures[texture_index];
s.setTexture(tex.texture);
s.setScale(sf::Vector2f(scale, scale));
s.setPosition(sf::Vector2f(x, y));
//std::cout << "Drawable position: " << x << ", " << y << " -> " << s.getPosition().x << ", " << s.getPosition().y << std::endl;
s.setTextureRect(tex.spriteCoordinates(sprite_index));
return s;
}
IndexSprite::IndexSprite(int _ti, int _si, float _x, float _y, float _s):
texture_index(_ti), sprite_index(_si), x(_x), y(_y), scale(_s) {
//std::cout << "IndexSprite constructed with x, y " << _x << ", " << _y << std::endl;
//std::cout << " * Stored x, y " << x << ", " << y << std::endl;
}

13
src/IndexSprite.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include "Common.h"
class GameEngine; // forward declare
class IndexSprite {
public:
int texture_index, sprite_index;
float x, y;
float scale;
static GameEngine* game;
sf::Sprite drawable();
IndexSprite(int, int, float, float, float);
};

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,19 @@
#pragma once
#include "Common.h"
#include "Entity.h"
//#include "EntityManager.h"
//#include "Scene.h"
//#include "GameEngine.h" // can't - need forward declaration
//#include "ActionCode.h"
#include "Python.h"
#include "UIMenu.h"
#include "Grid.h"
#include "IndexSprite.h"
#include "EntityManager.h"
#include <list>
#include "PyFont.h"
#include "PyTexture.h"
// implementation required to link templates
#include "Animation.h"
class GameEngine; // forward declared (circular members)
@ -15,29 +24,63 @@ private:
texture_width = 12, texture_height = 11, // w & h sprite/frame count
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();
public:
static PyObject* mcrf_module;
static std::shared_ptr<PyFont> default_font;
static std::shared_ptr<PyTexture> default_texture;
//inline static sf::Sprite sprite;
//inline static sf::Texture texture;
//static void setSpriteTexture(int);
inline static sf::Sprite sprite;
inline static sf::Texture texture;
static void setSpriteTexture(int);
inline static GameEngine* game;
static void api_init();
static void api_shutdown();
// Python API functionality - use mcrfpy.* in scripts
//static PyObject* _drawSprite(PyObject*, PyObject*);
static PyObject* _drawSprite(PyObject*, PyObject*);
static void REPL_device(FILE * fp, const char *filename);
static void REPL();
// Jank mode engage: let the API hold data for Python to hack on
static std::map<std::string, UIMenu*> menus;
static EntityManager entities; // this is also kinda good, entities not on the current grid can still act (like monsters following you through doors??)
static std::map<std::string, Grid*> grids;
static std::list<Animation*> animations;
static std::vector<sf::SoundBuffer> soundbuffers;
static sf::Music music;
static sf::Sound sfx;
static std::shared_ptr<Entity> player;
static std::map<std::string, PyObject*> callbacks;
// Jank Python Method Exposures
static PyObject* _createMenu(PyObject*, PyObject*); // creates a new menu object in McRFPy_API::menus
static PyObject* _listMenus(PyObject*, PyObject*);
static PyObject* _modMenu(PyObject*, PyObject*);
static PyObject* _createCaption(PyObject*, PyObject*); // calls menu.add_caption
static PyObject* _createButton(PyObject*, PyObject*);
static PyObject* _createTexture(PyObject*, PyObject*);
static PyObject* _listTextures(PyObject*, PyObject*);
static PyObject* _createSprite(PyObject*, PyObject*);
// use _listMenus, probably will not implement
//static PyObject* _listCaptions(PyObject*, PyObject*);
//static PyObject* _listButtons(PyObject*, PyObject*);
static PyObject* _createEntity(PyObject*, PyObject*);
//static PyObject* _listEntities(PyObject*, PyObject*);
static PyObject* _createGrid(PyObject*, PyObject*);
static PyObject* _listGrids(PyObject*, PyObject*);
static PyObject* _modGrid(PyObject*, PyObject*);
static PyObject* _createAnimation(PyObject*, PyObject*);
static PyObject* _registerPyAction(PyObject*, PyObject*);
static PyObject* _registerInputAction(PyObject*, PyObject*);
@ -49,29 +92,71 @@ public:
static PyObject* _getMusicVolume(PyObject*, PyObject*);
static PyObject* _getSoundVolume(PyObject*, PyObject*);
// allow all player actions (items, menus, movement, combat)
static PyObject* _unlockPlayerInput(PyObject*, PyObject*);
// disallow player actions (animating enemy actions)
static PyObject* _lockPlayerInput(PyObject*, PyObject*);
// prompt C++/Grid Objects to callback with a target Entity or grid space
static PyObject* _requestGridTarget(PyObject*, PyObject*);
// string for labeling the map
static std::string active_grid;
static PyObject* _activeGrid(PyObject*, PyObject*);
static PyObject* _setActiveGrid(PyObject*, PyObject*);
// string for prompting input
static std::string input_mode;
static PyObject* _inputMode(PyObject*, PyObject*);
// turn cycle
static int turn_number;
static PyObject* _turnNumber(PyObject*, PyObject*);
static PyObject* _refreshFov(PyObject*, PyObject*);
static bool do_camfollow;
static void camFollow();
static PyObject* _camFollow(PyObject*, PyObject*);
static PyObject* _sceneUI(PyObject*, PyObject*);
// scene control
static PyObject* _setScene(PyObject*, PyObject*);
static PyObject* _currentScene(PyObject*, PyObject*);
static PyObject* _createScene(PyObject*, PyObject*);
static PyObject* _keypressScene(PyObject*, PyObject*);
// timer control
static PyObject* _setTimer(PyObject*, PyObject*);
static PyObject* _delTimer(PyObject*, PyObject*);
static PyObject* _exit(PyObject*, PyObject*);
static PyObject* _setScale(PyObject*, PyObject*);
// accept keyboard input from scene
static sf::Vector2i cursor_position;
static void player_input(int, int);
static void computerTurn();
static void playerTurn();
// Jank Functionality
static UIMenu* createMenu(int posx, int posy, int sizex, int sizey);
static void createCaption(std::string menukey, std::string text, int fontsize, sf::Color textcolor);
static void createButton(std::string menukey, int x, int y, int w, int h, sf::Color bgcolor, sf::Color textcolor, std::string caption, std::string action);
static void createSprite(std::string menukey, int ti, int si, float x, float y, float scale);
static int createTexture(std::string filename, int grid_size, int grid_width, int grid_height);
//static void playSound(const char * filename);
//static void playMusic(const char * filename);
static void doAction(std::string);
// McRFPy_API(GameEngine*);
// API functionality - use from C++ directly
//void spawnEntity(int tex_index, int grid_x, int grid_y, PyObject* script);
static void executeScript(std::string);
static void executePyString(std::string);
};
/*
static PyMethodDef mcrfpyMethods[] = {
{"drawSprite", McRFPy_API::_drawSprite, METH_VARARGS,
"Draw a sprite (index, x, y)"},
{NULL, NULL, 0, NULL}
};
static PyModuleDef mcrfpyModule = {
PyModuleDef_HEAD_INIT, "mcrfpy", NULL, -1, mcrfpyMethods,
NULL, NULL, NULL, NULL
};
// Module initializer fn, passed to PyImport_AppendInittab
PyObject* PyInit_mcrfpy()
{
return PyModule_Create(&mcrfpyModule);
}
*/

53
src/MenuScene.cpp Normal file
View File

@ -0,0 +1,53 @@
#include "MenuScene.h"
#include "ActionCode.h"
MenuScene::MenuScene(GameEngine* g) : Scene(g)
{
text.setFont(game->getFont());
text.setString("McRogueFace Engine - r/RoguelikeDev Tutorial 2023");
text.setCharacterSize(24);
//std::cout << "MenuScene Initialized. " << game << std::endl;
//std::cout << "Font: " << game->getFont().getInfo().family << std::endl;
text2.setFont(game->getFont());
text2.setString("Press 'Spacebar' to run demo");
text2.setCharacterSize(16);
text2.setPosition(0.0f, 50.0f);
text3.setFont(game->getFont());
text3.setString("use 'W' 'A' 'S' 'D' to move (even when blank; it's a bug)");
text3.setCharacterSize(16);
text3.setPosition(0.0f, 80.0f);
registerAction(ActionCode::KEY + sf::Keyboard::Space, "start_game");
registerAction(ActionCode::KEY + sf::Keyboard::Up, "up");
registerAction(ActionCode::KEY + sf::Keyboard::Down, "down");
}
void MenuScene::update()
{
//std::cout << "MenuScene update" << std::endl;
}
void MenuScene::doAction(std::string name, std::string type)
{
//std::cout << "MenuScene doAction: " << name << ", " << type << std::endl;
//if (name.compare("start_game") == 0 and type.compare("start") == 0)
if(ACTION("start_game", "start"))
game->changeScene("py");
/*
else if(ACTIONONCE("up"))
game->getWindow().setSize(sf::Vector2u(1280, 800));
else if(ACTIONONCE("down"))
game->getWindow().setSize(sf::Vector2u(1024, 768));
*/
}
void MenuScene::sRender()
{
game->getWindow().clear();
game->getWindow().draw(text);
game->getWindow().draw(text2);
game->getWindow().draw(text3);
game->getWindow().display();
}

View File

@ -4,14 +4,15 @@
#include "Scene.h"
#include "GameEngine.h"
class PyScene: public Scene
class MenuScene: public Scene
{
sf::Text text;
sf::Text text2;
sf::Text text3;
public:
PyScene(GameEngine*);
MenuScene(GameEngine*);
void update() override final;
void doAction(std::string, std::string) override final;
void render() override final;
void do_mouse_input(std::string, std::string);
void sRender() override final;
};

View File

@ -1,114 +0,0 @@
#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;
}
}

View File

@ -1,45 +0,0 @@
#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();
};

View File

@ -1,150 +0,0 @@
#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;
}

View File

@ -1,48 +0,0 @@
#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,
};
}

View File

@ -1,63 +0,0 @@
#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);
}

View File

@ -1,39 +0,0 @@
#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,74 +0,0 @@
#include "PyScene.h"
#include "ActionCode.h"
#include "Resources.h"
#include "PyCallable.h"
PyScene::PyScene(GameEngine* g) : Scene(g)
{
// mouse events
registerAction(ActionCode::MOUSEBUTTON + sf::Mouse::Left, "left");
registerAction(ActionCode::MOUSEBUTTON + sf::Mouse::Right, "right");
registerAction(ActionCode::MOUSEWHEEL + ActionCode::WHEEL_DEL, "wheel_up");
registerAction(ActionCode::MOUSEWHEEL + ActionCode::WHEEL_NEG + ActionCode::WHEEL_DEL, "wheel_down");
registerAction(ActionCode::KEY + sf::Keyboard::Grave, "debug_menu");
}
void PyScene::update()
{
}
void PyScene::do_mouse_input(std::string button, std::string type)
{
auto unscaledmousepos = sf::Mouse::getPosition(game->getWindow());
auto mousepos = game->getWindow().mapPixelToCoords(unscaledmousepos);
UIDrawable* target;
for (auto d: *ui_elements)
{
target = d->click_at(sf::Vector2f(mousepos));
if (target)
{
/*
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);
if (!retval)
{
std::cout << "click_callable 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 << "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);
}
}
}
void PyScene::doAction(std::string name, std::string type)
{
if (ACTIONPY) {
McRFPy_API::doAction(name.substr(0, name.size() - 3));
}
else if (name.compare("left") == 0 || name.compare("rclick") == 0 || name.compare("wheel_up") == 0 || name.compare("wheel_down") == 0) {
do_mouse_input(name, type);
}
else if ACTIONONCE("debug_menu") {
McRFPy_API::REPL();
}
}
void PyScene::render()
{
game->getWindow().clear();
auto vec = *ui_elements;
for (auto e: vec)
{
if (e)
e->render();
}
game->getWindow().display();
}

View File

@ -1,82 +0,0 @@
#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);
}

View File

@ -1,43 +0,0 @@
#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,
};
}

View File

@ -1,111 +0,0 @@
#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;
}

View File

@ -1,42 +0,0 @@
#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,
};
}

269
src/PythonScene.cpp Normal file
View File

@ -0,0 +1,269 @@
#include "PythonScene.h"
#include "ActionCode.h"
#include "McRFPy_API.h"
//#include "Animation.h"
PythonScene::PythonScene(GameEngine* g, std::string pymodule)
: Scene(g) {
// mouse events
registerAction(ActionCode::MOUSEBUTTON + sf::Mouse::Left, "click");
registerAction(ActionCode::MOUSEBUTTON + sf::Mouse::Right, "rclick");
registerAction(ActionCode::MOUSEWHEEL + ActionCode::WHEEL_DEL, "wheel_up");
registerAction(ActionCode::MOUSEWHEEL + ActionCode::WHEEL_NEG + ActionCode::WHEEL_DEL, "wheel_down");
// keyboard events
/*
registerAction(ActionCode::KEY + sf::Keyboard::Q, "upleft");
registerAction(ActionCode::KEY + sf::Keyboard::W, "up");
registerAction(ActionCode::KEY + sf::Keyboard::E, "upright");
registerAction(ActionCode::KEY + sf::Keyboard::A, "left");
registerAction(ActionCode::KEY + sf::Keyboard::S, "down");
registerAction(ActionCode::KEY + sf::Keyboard::D, "right");
registerAction(ActionCode::KEY + sf::Keyboard::Z, "downleft");
registerAction(ActionCode::KEY + sf::Keyboard::X, "wait");
registerAction(ActionCode::KEY + sf::Keyboard::C, "downright");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad7, "upleft");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad8, "up");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad9, "upright");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad4, "left");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad5, "wait");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad6, "right");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad1, "downleft");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad2, "down");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad3, "downright");
*/
// window resize
registerAction(0, "event");
dragging = false;
drag_grid = NULL;
// import pymodule and call start()
McRFPy_API::executePyString("import " + pymodule);
McRFPy_API::executePyString(pymodule + ".start()");
}
void PythonScene::animate() {
//std::cout << "Number of animations: " << McRFPy_API::animations.size() << std::endl;
auto frametime = game->getFrameTime();
auto it = McRFPy_API::animations.begin();
while (it != McRFPy_API::animations.end()) {
//std::cout << "Iterating" << std::endl;
(*it)->step(frametime);
//std::cout << "Step complete" << std::endl;
if ((*it)->isDone()) {
//std::cout << "Cleaning up Animation" << std::endl;
auto prev = it;
it++;
McRFPy_API::animations.erase(prev);
} else it++;
}
/* // workin on it
for (auto p : animations) {
if (p.first == "int") {
((Animation<int>)p.second).step(frametime);
} else if (p.first == "string") {
((Animation<std::string>)p.second).step(frametime);
} else if (p.first == "float") {
((Animation<float>)p.second).step(frametime);
} else if (p.first == "vector2f") {
((Animation<sf::Vector2f>)p.second).step(frametime);
} else if (p.first == "vector2i") {
((Animation<sf::Vector2i>)p.second).step(frametime);
} else if (p.first == "color") {
((Animation<int>)p.second).step(frametime); // TODO
} else {
std::cout << "Animation has label " << p.first << "; no type found" << std::endl;
}
}
auto it = animations.begin();
while (it != animations.end()) {
bool done = false;
if (p.first == "int") {
((Animation<int>)p.second).step(frametime);
} else if (p.first == "string") {
if ((Animation<std::string>)p.second).isDone()
delete (Animation<std::string>)p.second
} else if (p.first == "float") {
((Animation<float>)p.second).step(frametime);
} else if (p.first == "vector2f") {
((Animation<sf::Vector2f>)p.second).step(frametime);
} else if (p.first == "vector2i") {
((Animation<sf::Vector2i>)p.second).step(frametime);
} else if (p.first == "color") {
((Animation<int>)p.second).step(frametime); // TODO
if ((*it).second.isDone()) {
animations.erase(it++);
} else { it++; }
}
*/
}
void PythonScene::update() {
// turn cycle: If player's input made the state "computerturnwait", finish
// all animations and then let the NPCs act
if (McRFPy_API::animations.size() == 0 && McRFPy_API::input_mode.compare("computerturnwait") == 0) {
McRFPy_API::input_mode = "computerturn";
}
else if (McRFPy_API::animations.size() == 0 && McRFPy_API::input_mode.compare("computerturnrunning") == 0) {
McRFPy_API::input_mode = "playerturnstart";
}
McRFPy_API::entities.update();
// check if left click is still down & mouse has moved
// continue the drag motion
if (dragging && drag_grid) {
//std::cout << "Compute dragging" << std::endl;
auto mousepos = sf::Mouse::getPosition(game->getWindow());
auto dx = mouseprev.x - mousepos.x,
dy = mouseprev.y - mousepos.y;
if (dx != 0 || dy != 0) { McRFPy_API::do_camfollow = false; }
drag_grid->center_x += (dx / drag_grid->zoom);
drag_grid->center_y += (dy / drag_grid->zoom);
mouseprev = mousepos;
}
animate();
McRFPy_API::camFollow();
if (McRFPy_API::input_mode.compare(std::string("computerturn")) == 0) McRFPy_API::computerTurn();
if (McRFPy_API::input_mode.compare(std::string("playerturnstart")) == 0) McRFPy_API::playerTurn();
}
void PythonScene::doLClick(sf::Vector2i mousepos) {
// UI buttons get first chance
for (auto pair : McRFPy_API::menus) {
if (!pair.second->visible) continue;
for (auto b : pair.second->buttons) {
//std::cout << "Box: " << pair.second->box.getPosition().x << ", "
//<< pair.second->box.getPosition().y << "; Button:" << b.rect.getPosition().x <<
//", " << b.rect.getPosition().y << "; Mouse: " << mousepos.x << ", " <<
//mousepos.y << std::endl;
// JANK: provide the button a relative coordinate.
if (b.contains(pair.second->box.getPosition(), mousepos)) {
McRFPy_API::doAction(b.getAction());
return;
}
}
}
// left clicking a grid to select a square
for (auto pair : McRFPy_API::grids) {
if (!pair.second->visible) continue;
if (pair.second->contains(mousepos)) {
// grid was clicked
return;
}
}
}
void PythonScene::doRClick(sf::Vector2i mousepos) {
// just grids for right click
for (auto pair : McRFPy_API::grids) {
if (!pair.second->visible) continue;
if (pair.second->contains(mousepos)) {
// grid was clicked
return;
}
}
}
void PythonScene::doZoom(sf::Vector2i mousepos, int value) {
// just grids for right click
for (auto pair : McRFPy_API::grids) {
if (!pair.second->visible) continue;
if (pair.second->contains(mousepos)) {
// grid was zoomed
float new_zoom = pair.second->zoom + (value * 0.25);
if (new_zoom >= 0.5 && new_zoom <= 5.0) {
pair.second->zoom = new_zoom;
}
}
}
}
void PythonScene::doAction(std::string name, std::string type) {
auto mousepos = sf::Mouse::getPosition(game->getWindow());
//std::cout << "name: " << name << ", type: " << type << std::endl;
if (ACTIONPY) {
McRFPy_API::doAction(name.substr(0, name.size() - 3));
}
else if (ACTIONONCE("click")) {
// left click start
//std::cout << "LClick started at (" << mousepos.x << ", " << mousepos.y << ")" << std::endl;
dragstart = mousepos;
mouseprev = mousepos;
dragging = true;
// determine the grid that contains the cursor
for (auto pair : McRFPy_API::grids) {
if (!pair.second->visible) continue;
if (pair.second->contains(mousepos)) {
// grid was clicked
drag_grid = pair.second;
}
}
}
else if (ACTIONAFTER("click")) {
// left click end
//std::cout << "LClick ended at (" << mousepos.x << ", " << mousepos.y << ")" << std::endl;
// if click ended without starting a drag event, try lclick?
if (dragstart == mousepos) {
// mouse did not move, do click
//std::cout << "(did not move)" << std::endl;
doLClick(mousepos);
}
dragging = false;
drag_grid = NULL;
}
else if (ACTIONONCE("rclick")) {
// not going to test for right click drag - just rclick
doRClick(mousepos);
}
else if (ACTIONONCE("wheel_up")) {
// try zoom in
doZoom(mousepos, 1);
}
else if (ACTIONONCE("wheel_down")) {
// try zoom out
doZoom(mousepos, -1);
}
else if (ACTIONONCE("up")) { McRFPy_API::player_input(+0, -1); }
else if (ACTIONONCE("upright")) { McRFPy_API::player_input(+1, -1); }
else if (ACTIONONCE("right")) { McRFPy_API::player_input(+1, +0); }
else if (ACTIONONCE("downright")) { McRFPy_API::player_input(+1, +1); }
else if (ACTIONONCE("down")) { McRFPy_API::player_input(+0, +1); }
else if (ACTIONONCE("downleft")) { McRFPy_API::player_input(-1, +1); }
else if (ACTIONONCE("left")) { McRFPy_API::player_input(-1, +0); }
else if (ACTIONONCE("upleft")) { McRFPy_API::player_input(-1, -1); }
else if (ACTIONONCE("wait")) { McRFPy_API::player_input(+0, +0); }
}
bool PythonScene::registerActionInjected(int code, std::string name) {
std::cout << "Registering injected action (PythonScene): " << code << " (" << ActionCode::KEY + code << ")\n";
registerAction(ActionCode::KEY + code, name);
//return false;
return true;
}
bool PythonScene::unregisterActionInjected(int code, std::string name) {
return false;
}
void PythonScene::sRender() {
game->getWindow().clear();
for (auto pair: McRFPy_API::grids) {
if (!pair.second->visible) continue;
pair.second->render(game->getWindow());
}
for (auto pair: McRFPy_API::menus) {
if (!pair.second->visible) continue;
pair.second->render(game->getWindow());
}
game->getWindow().display();
}

29
src/PythonScene.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include "Common.h"
#include "Scene.h"
#include "GameEngine.h"
#include "Grid.h"
//#include "Animation.h"
//#include <list>
class PythonScene: public Scene
{
sf::Vector2i dragstart, mouseprev;
bool dragging;
Grid* drag_grid;
void doLClick(sf::Vector2i);
void doRClick(sf::Vector2i);
void doZoom(sf::Vector2i, int);
//std::list<Animation*> animations;
void animate();
std::map<std::string, bool> actionInjected;
public:
PythonScene(GameEngine*, std::string);
void update() override final;
void doAction(std::string, std::string) override final;
void sRender() override final;
bool registerActionInjected(int, std::string) override;
bool unregisterActionInjected(int, std::string) override;
};

View File

@ -1,8 +1,7 @@
#include "Resources.h"
//#include <list>
//#include "UI.h"
#include <list>
#include "UI.h"
// Resources class members memory allocation
sf::Font Resources::font;
GameEngine* Resources::game;
std::string Resources::caption_buffer;
GameEngine* Resources::game;

View File

@ -1,7 +1,7 @@
#pragma once
#include "Common.h"
//#include <list>
//#include "UI.h"
#include <list>
#include "UI.h"
class GameEngine; // forward declared
@ -10,5 +10,4 @@ class Resources
public:
static sf::Font font;
static GameEngine* game;
static std::string caption_buffer;
};

View File

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

View File

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

View File

@ -1,31 +0,0 @@
#include "Timer.h"
Timer::Timer(PyObject* _target, int _interval, int now)
: target(_target), interval(_interval), last_ran(now)
{}
Timer::Timer()
: target(Py_None), interval(0), last_ran(0)
{}
bool Timer::test(int now)
{
if (!target || target == Py_None) return false;
if (now > last_ran + interval)
{
last_ran = now;
PyObject* args = Py_BuildValue("(i)", now);
PyObject* retval = PyObject_Call(target, args, NULL);
if (!retval)
{
std::cout << "timer 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 << "timer returned a non-None value. It's not an error, it's just not being saved or used." << std::endl;
}
return true;
}
return false;
}

View File

@ -1,15 +0,0 @@
#pragma once
#include "Common.h"
#include "Python.h"
class GameEngine; // forward declare
class Timer
{
public:
PyObject* target;
int interval;
int last_ran;
Timer(); // for map to build
Timer(PyObject*, int, int);
bool test(int);
};

237
src/UI.cpp Normal file
View File

@ -0,0 +1,237 @@
#include "UI.h"
#include "Resources.h"
#include "GameEngine.h"
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>>>();
/*
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)
{
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);
}
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;
}
PyObject* DEFUNCT_py_instance(std::shared_ptr<UIDrawable> source)
{
// takes a UI drawable, calls its derived_type virtual function, and builds a Python object based on the return value.
using namespace mcrfpydef;
PyObject* newobj = NULL;
std::cout << "py_instance called\n";
switch (source->derived_type())
{
case PyObjectsEnum::UIFRAME:
{
std::cout << "UIFRAME case\n" << std::flush;
PyTypeObject* UIFrameType = &PyUIFrameType;
//std::cout << "tp_alloc\n" << std::flush;
//PyObject* _o = UIFrameType->tp_alloc(UIFrameType, 0);
//std::cout << "reinterpret_cast\n" << std::flush;
//auto o = reinterpret_cast<PyUICollectionObject*>(_o);
//PyUIFrameObject* o = (PyUIFrameObject*)PyObject_New(PyUIFrameObject, UIFrameType);
PyUIFrameObject* o = (PyUIFrameObject*)(UIFrameType->tp_alloc(UIFrameType, 0));
//PyUIFrameObject* o = PyObject_New(PyUIFrameObject, UIFrameType);
/*
// backtracking the problem: instantiate a PyColor of (255, 0, 0) for testing
PyTypeObject* colorType = &PyColorType;
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);
pyColorObj->data = std::make_shared<sf::Color>();
pyColorObj->data-> r = 255;
return (PyObject*)pyColorObj;
*/
std::cout << "pointer check: " << o << "\n" << std::flush;
if (o)
{
std::cout << "Casting data...\n" << std::flush;
auto p = std::static_pointer_cast<UIFrame>(source);
std::cout << "casted. Assigning...\n" << std::flush;
//o->data = std::make_shared<UIFrame>();
o->data = p;
//std::cout << "assigned.\n" << std::flush;
auto usource = o->data; //(UIFrame*)source.get();
std::cout << "Loaded data into object. " << usource->box.getPosition().x << " " << usource->box.getPosition().y << " " <<
usource->box.getSize().x << " " << usource->box.getSize().y << std::endl;
}
else
{
std::cout << "Allocation failed.\n" << std::flush;
}
newobj = (PyObject*)o;
break;
}
case PyObjectsEnum::UICAPTION:
{
std::cout << "UICAPTION case\n";
PyErr_SetString(PyExc_NotImplementedError, "UICaption class not implemented in Python yet.");
/* not yet implemented
PyUICaptionObject* o = (PyUICaptionObject*)PyUICaptionType.tp_alloc(&PyUICaptionType, 0);
if (o)
o->data = std::static_pointer_cast<UICaption>(source);
*/
break;
}
case PyObjectsEnum::UISPRITE:
{
std::cout << "UISPRITE case\n";
PyErr_SetString(PyExc_NotImplementedError, "UISprite class not implemented in Python yet.");
/*
PyUISpriteObject* o = (PyUISpriteObject*)PyUISpriteType.tp_alloc(&PyUISpriteType, 0);
if (o)
o->data = std::static_pointer_cast<UISprite>(source);
*/
break;
}
default:
return NULL;
break;
}
return newobj;
}

1436
src/UI.h

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,283 +0,0 @@
#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;
}

View File

@ -1,62 +0,0 @@
#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;
}
};
}

View File

@ -1,203 +0,0 @@
#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;
}

View File

@ -1,88 +0,0 @@
#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;
}
};
}

View File

@ -1,81 +0,0 @@
#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);
}

View File

@ -1,176 +0,0 @@
#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;
}
*/
}

View File

@ -1,177 +0,0 @@
#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");
}

View File

@ -1,75 +0,0 @@
#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,
};
}

View File

@ -1,265 +0,0 @@
#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;
}

View File

@ -1,77 +0,0 @@
#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;
}
};
}

View File

@ -1,639 +0,0 @@
#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;
}

View File

@ -1,184 +0,0 @@
#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;
}
};
}

View File

@ -1,158 +0,0 @@
#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");
}

View File

@ -1,92 +0,0 @@
#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,
};
}

83
src/UIMenu.cpp Normal file
View File

@ -0,0 +1,83 @@
#include "UIMenu.h"
#include "Common.h"
#include "Resources.h"
UIMenu::UIMenu(sf::Font & _font)
: font(_font)
{
//font = _font;
box.setSize(sf::Vector2f(300, 400));
box.setPosition(sf::Vector2f(300, 250));
box.setFillColor(sf::Color(0,0,255));
}
UIMenu::UIMenu()
: font(Resources::font)
{
box.setSize(sf::Vector2f(300, 400));
box.setPosition(sf::Vector2f(300, 250));
box.setFillColor(sf::Color(0,0,255));
}
void UIMenu::render(sf::RenderWindow & window)
{
window.draw(box);
for (auto& s: sprites) {
auto _s = s.drawable();
//std::cout << "Sprite has values " << s.x << ", " << s.y << std::endl;
//std::cout << "Drawable generated @ " << _s.getPosition().x << ", " << _s.getPosition().y << std::endl;
_s.move(box.getPosition());
//std::cout << "Moved by " << box.getPosition().x << ", " << box.getPosition().y << std::endl;
//std::cout << "Render UIMenu Sprite @ " << _s.getPosition().x << ", " << _s.getPosition().y << std::endl;
window.draw(_s);
}
for (auto& c : captions) {
//auto s = std::string(c.getString());
//std::cout << s << std::flush << std::endl;
c.move(box.getPosition());
window.draw(c);
c.move(-box.getPosition());
}
for (auto& b : buttons) {
//b.render(window);
b.setPosition(box.getPosition() + b.rect.getPosition());
//b.caption.setPosition(box.getPosition() + b.caption.getPosition());
b.render(window);
b.setPosition(b.rect.getPosition() - box.getPosition());
//b.caption.setPosition(b.caption.getPosition() - box.getPosition());
}
}
void UIMenu::refresh()
{
}
void UIMenu::add_caption(const char* text, int tsize, sf::Color color)
{
auto c = sf::Text();
//auto bpos = box.getPosition();
c.setFillColor(color);
c.setPosition(10, next_text);
next_text += 50;
c.setCharacterSize(tsize);
c.setString(text);
c.setFont(font);
captions.push_back(c);
}
void UIMenu::add_button(Button b)
{
b.setPosition(sf::Vector2f(box.getSize().x / 2.0f, next_button));
next_button += 50;
buttons.push_back(b);
}
void UIMenu::add_sprite(IndexSprite s)
{
//std::cout << "Adding sprite to UIMenu x,y " << s.x << ", " << s.y << std::endl;
sprites.push_back(s);
}

38
src/UIMenu.h Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include "Common.h"
#include "Button.h"
#include "IndexSprite.h"
class UIMenu
{
public:
//UIMenu() {};
sf::Font & font;
UIMenu(sf::Font & _font);
UIMenu();
std::vector<sf::Text> captions;
std::vector<Button> buttons;
std::vector<IndexSprite> sprites;
/* idea: */ //std::vector<UIDrawable> children; // on the UIBox class?
sf::RectangleShape box;
bool visible = false;
int next_text = 10;
int next_button = 10;
void render(sf::RenderWindow & window);
void refresh();
void add_caption(const char* text, int size, sf::Color color);
void add_button(Button b);
void add_sprite(IndexSprite s);
protected:
private:
};

View File

@ -1,215 +0,0 @@
#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;
}

View File

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

@ -4,10 +4,8 @@
UITestScene::UITestScene(GameEngine* g) : Scene(g)
{
registerAction(ActionCode::KEY + sf::Keyboard::Grave, "debug_menu");
text.setFont(game->getFont());
text.setString("UITest: surprised to be here? game.py raised an exception.");
text.setString("Test Scene for UI elements");
text.setCharacterSize(24);
@ -61,15 +59,15 @@ UITestScene::UITestScene(GameEngine* g) : Scene(g)
//ui_elements.push_back(&e1);
//ui_elements.push_back(&e2);
//t.loadFromFile("./assets/kenney_tinydungeon.png");
//t.setSmooth(false);
//auto* indextex = new IndexTexture(t, 16, 12, 11);
//Resources::game->textures.push_back(*indextex);
t.loadFromFile("./assets/kenney_tinydungeon.png");
t.setSmooth(false);
auto* indextex = new IndexTexture(t, 16, 12, 11);
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";
auto e3 = std::make_shared<UISprite>(ptex, 84, sf::Vector2f(10, 10), 4.0);
auto e3 = std::make_shared<UISprite>();
// Make UISprite more like IndexSprite: this is bad
//e3->x = 10; e3->y = 10;
@ -79,19 +77,19 @@ UITestScene::UITestScene(GameEngine* g) : Scene(g)
//e3->update();
// This goes to show how inconvenient the default constructor is. It should be removed
//e3->itex = &Resources::game->textures[0];
//e3->sprite.setTexture(e3->itex->texture);
//e3->sprite_index = 84;
//e3->sprite.setTextureRect(e3->itex->spriteCoordinates(e3->sprite_index));
//e3->setPosition(10, 10);
//e3->setScale(4.0f);
e3->itex = &Resources::game->textures[0];
e3->sprite.setTexture(e3->itex->texture);
e3->sprite_index = 84;
e3->sprite.setTextureRect(e3->itex->spriteCoordinates(e3->sprite_index));
e3->setPosition(10, 10);
e3->setScale(4.0f);
e1aa->children->push_back(e3);
auto e4 = std::make_shared<UISprite>(
ptex, //indextex, //&Resources::game->textures[0],
indextex, //&Resources::game->textures[0],
85, sf::Vector2f(90, 10), 4.0);
e1aa->children->push_back(e4);
//std::cout << "UISprite built: " << e4->sprite.getPosition().x << " " << e4->sprite.getPosition().y << " " << e4->sprite.getScale().x << " " <<
@ -103,33 +101,6 @@ UITestScene::UITestScene(GameEngine* g) : Scene(g)
if (ui)
std::cout << "pointer to ui_elements now shows size=" << ui->size() << std::endl;
*/
// UIGrid test: (in grid cells) ( in screen pixels )
// constructor args: w h texture x y w h
auto e5 = std::make_shared<UIGrid>(4, 4, ptex, sf::Vector2f(550, 150), sf::Vector2f(200, 200));
e5->zoom=2.0;
e5->points[0].color = sf::Color(255, 0, 0);
e5->points[1].tilesprite = 1;
e5->points[5].color = sf::Color(0, 255, 0);
e5->points[6].tilesprite = 2;
e5->points[10].color = sf::Color(0, 0, 255);
e5->points[11].tilesprite = 3;
e5->points[15].color = sf::Color(255, 255, 255);
ui_elements->push_back(e5);
//UIEntity test:
// asdf
// 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.
e5a->grid = e5;
//auto e5as = UISprite(indextex, 85, sf::Vector2f(0, 0), 1.0);
//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);
e5->entities->push_back(e5a);
}
void UITestScene::update()
@ -143,9 +114,6 @@ void UITestScene::doAction(std::string name, std::string type)
//if (name.compare("start_game") == 0 and type.compare("start") == 0)
if(ACTION("start_game", "start"))
game->changeScene("py");
else if ACTIONONCE("debug_menu") {
McRFPy_API::REPL();
}
/*
else if(ACTIONONCE("up"))
game->getWindow().setSize(sf::Vector2u(1280, 800));
@ -154,7 +122,7 @@ void UITestScene::doAction(std::string name, std::string type)
*/
}
void UITestScene::render()
void UITestScene::sRender()
{
game->getWindow().clear();
game->getWindow().draw(text);
@ -177,5 +145,5 @@ void UITestScene::render()
game->getWindow().display();
//McRFPy_API::REPL();
McRFPy_API::REPL();
}

View File

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

50
src/scripts/Grid.py Normal file
View File

@ -0,0 +1,50 @@
class GridPoint:
def __init__(self, color, walkable, tilesprite, transparent, visible, discovered, color_overlay, tile_overlay, uisprite):
self.color = color
self.walkable = walkable
self.tilesprite = tilesprite
self.transparent = transparent
self.visible = visible
self.discovered = discovered
self.color_overlay = color_overlay
self.tile_overlay = tile_overlay
self.uisprite = uisprite
def __repr__(self):
return f"<GridPoint {self.color}, {self.tilesprite}/{self.uisprite} {'W' if self.walkable else '-'}{'T' if self.transparent else '-'}{'V' if self.visible else '-'}{'D' if self.discovered else '-'} {self.color_overlay}/{self.tile_overlay}>"
class Grid:
def __init__(self, title, gx, gy, gs, x, y, w, h, visible=False):
self.title = title
self.grid_x = gx
self.grid_y = gy
self.grid_size = gs
self.x = x
self.y = y
self.w = w
self.h = h
self.visible = visible
self.points = []
self.entities = []
def at(self, x, y):
if not (x > 0 and y > 0 and x < self.grid_x and y < self.grid_y): return None
return self.points[y * self.grid_y + x]
def __repr__(self):
return f"<Grid {self.grid_x}x{self.grid_y}, grid_size={self.grid_size}, (({self.x},{self.y}), ({self.w}, {self.h})), visible={self.visible}>"
# CGrid(Grid* _g, int _ti, int _si, int _x, int _y, bool _v)
class Entity:
def __init__(self, parent, tex_index, sprite_index, x, y, visible=True):
self.parent = parent
self.tex_index = tex_index
self.sprite_index = sprite_index
self.x = x
self.y = y
self.visible = visible
def __repr__(self):
return f"<Entity on grid {repr(self.parent)}@({self.x},{self.y}), TI={self.tex_index}, SI={self.sprite_index}, visible={self.visible}>"

79
src/scripts/MusicScene.py Normal file
View File

@ -0,0 +1,79 @@
import mcrfpy
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED, GREEN, BLUE = (255, 0, 0), (0, 255, 0), (0, 0, 255)
DARKRED, DARKGREEN, DARKBLUE = (192, 0, 0), (0, 192, 0), (0, 0, 192)
class MusicScene:
def __init__(self, ui_name = "demobox1", grid_name = "demogrid"):
# Texture & Sound Loading
print("Load textures")
mcrfpy.createTexture("./assets/test_portraits.png", 32, 8, 8) #0 - portraits
mcrfpy.createTexture("./assets/alives_other.png", 16, 64, 64) #1 - TinyWorld NPCs
mcrfpy.createTexture("./assets/alives_other.png", 32, 32, 32) #2 - TinyWorld NPCs - 2x2 sprite
# {"createSoundBuffer", McRFPy_API::_createSoundBuffer, METH_VARARGS, "(filename)"},
#{"loadMusic", McRFPy_API::_loadMusic, METH_VARARGS, "(filename)"},
#{"setMusicVolume", McRFPy_API::_setMusicVolume, METH_VARARGS, "(int)"},
#{"setSoundVolume", McRFPy_API::_setSoundVolume, METH_VARARGS, "(int)"},
#{"playSound", McRFPy_API::_playSound, METH_VARARGS, "(int)"},
#{"getMusicVolume", McRFPy_API::_getMusicVolume, METH_VARARGS, ""},
#{"getSoundVolume", McRFPy_API::_getSoundVolume, METH_VARARGS, ""},
mcrfpy.loadMusic("./assets/ultima.ogg")
mcrfpy.createSoundBuffer("./assets/boom.wav")
self.ui_name = ui_name
self.grid_name = grid_name
print("Create UI")
# Create dialog UI
mcrfpy.createMenu(ui_name, 20, 540, 500, 200)
mcrfpy.createCaption(ui_name, f"Music Volume: {mcrfpy.getMusicVolume()}", 24, RED)
mcrfpy.createCaption(ui_name, f"SFX Volume: {mcrfpy.getSoundVolume()}", 24, RED)
#mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKBLUE, (0, 0, 0), "clicky", "testaction")
mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKRED, (0, 0, 0), "Music+", "mvol+")
mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKGREEN, (0, 0, 0), "Music-", "mvol-")
mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKBLUE, GREEN, "SFX+", "svol+")
mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKBLUE, RED, "SFX-", "svol-")
mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKRED, (0, 0, 0), "REPL", "startrepl")
mcrfpy.createSprite(ui_name, 1, 0, 20, 40, 3.0)
print("Create UI 2")
entitymenu = "entitytestmenu"
mcrfpy.createMenu(entitymenu, 840, 20, 20, 500)
mcrfpy.createButton(entitymenu, 0, 10, 150, 40, DARKBLUE, BLACK, "PlayM", "playm")
mcrfpy.createButton(entitymenu, 0, 60, 150, 40, DARKBLUE, BLACK, "StopM", "stopm")
mcrfpy.createButton(entitymenu, 0, 110, 150, 40, DARKBLUE, BLACK, "SFX", "boom")
print("Make UIs visible")
self.menus = mcrfpy.listMenus()
self.menus[0].visible = True
self.menus[1].w = 170
self.menus[1].visible = True
mcrfpy.modMenu(self.menus[0])
mcrfpy.modMenu(self.menus[1])
self.mvol = mcrfpy.getMusicVolume()
self.svol = mcrfpy.getSoundVolume()
mcrfpy.registerPyAction("mvol+", lambda: self.setmvol(self.mvol+10))
mcrfpy.registerPyAction("mvol-", lambda: self.setmvol(self.mvol-10))
mcrfpy.registerPyAction("svol+", lambda: self.setsvol(self.svol+10))
mcrfpy.registerPyAction("svol-", lambda: self.setsvol(self.svol-10))
mcrfpy.registerPyAction("playm", lambda: None)
mcrfpy.registerPyAction("stopm", lambda: None)
mcrfpy.registerPyAction("boom", lambda: mcrfpy.playSound(0))
def setmvol(self, v):
mcrfpy.setMusicVolume(int(v))
self.menus[0].captions[0].text = f"Music Volume: {mcrfpy.getMusicVolume():.1f}"
mcrfpy.modMenu(self.menus[0])
self.mvol = mcrfpy.getMusicVolume()
def setsvol(self, v):
mcrfpy.setSoundVolume(int(v))
self.menus[0].captions[1].text = f"Sound Volume: {mcrfpy.getSoundVolume():.1f}"
mcrfpy.modMenu(self.menus[0])
self.svol = mcrfpy.getSoundVolume()
scene = None
def start():
global scene
scene = MusicScene()

575
src/scripts/TestScene.py Normal file
View File

@ -0,0 +1,575 @@
import UIMenu
import Grid
import mcrfpy
from random import randint, choice
from pprint import pprint
#print("TestScene imported")
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED, GREEN, BLUE = (255, 0, 0), (0, 255, 0), (0, 0, 255)
DARKRED, DARKGREEN, DARKBLUE = (192, 0, 0), (0, 192, 0), (0, 0, 192)
animations_in_progress = 0
# don't load grid over and over, use the global scene
scene = None
class TestEntity:
def __init__(self, grid, label, tex_index, basesprite, x, y, texture_width=64, walk_frames=5, attack_frames=6, do_fov=False):
self.grid = grid
self.basesprite = basesprite
self.texture_width = texture_width
self.walk_frames = walk_frames
self.attack_frames = attack_frames
self.x = x
self.y = y
self.facing_direction = 0
self.do_fov = do_fov
self.label = label
self.inventory = []
#print(f"Calling C++ with: {repr((self.grid, label, tex_index, self.basesprite, x, y, self))}")
grids = mcrfpy.listGrids()
for g in grids:
if g.title == self.grid:
self.entity_index = len(g.entities)
mcrfpy.createEntity(self.grid, label, tex_index, self.basesprite, x, y, self)
def ai_act(self):
return # no AI motion
#if self.label == "player": return
self.move(randint(-1, 1), randint(-1, 1))
scene.actors += 1
def player_act(self):
#print("I'M INTERVENING")
mcrfpy.unlockPlayerInput()
scene.updatehints()
def die(self):
#self.x = -2
#self.y = -2
self.move(-1000,-1000)
self.animate(0,animove=(-1000,-1000))
self.x = -1000
self.y = -1000
self.label = "dead"
def interact(self, initiator, callback):
print(f"Interacted with {self.label}. ", end='')
if self.label == 'item':
print("'taking' item.")
callback()
self.die()
else:
print("blocking movement.")
def move(self, dx, dy, force=False):
# select animation direction
# prefer left or right for diagonals.
#grids = mcrfpy.listGrids()
for g in scene.grids:
if g.title == self.grid:
if not force and g.at(self.x + dx, self.y + dy) is None or not g.at(self.x + dx, self.y + dy).walkable:
#print("Blocked at target location.")
return
if not force: # entities can be stepped on when force=True (like collecting items!)
for entity in scene.tes:
if (entity.x, entity.y) == (self.x + dx, self.y + dy):
print(f"Blocked by entity {entity.label} at ({entity.x}, {entity.y})")
return entity.interact(self, lambda: self.move(dx, dy, force=True))
if self.label == "player":
mcrfpy.lockPlayerInput()
scene.updatehints()
if (dx == 0 and dy == 0):
direction = self.facing_direction # TODO, jump straight to computer turn
elif (dx):
direction = 2 if dx == +1 else 3
else:
direction = 0 if dy == +1 else 1
self.animate(direction, move=True, animove=(self.x + dx, self.y+dy))
self.facing_direction = direction
if (self.do_fov): mcrfpy.refreshFov()
def animate(self, direction, attacking=False, move=False, animove=None):
start_sprite = self.basesprite + (self.texture_width * (direction + (4 if attacking else 0)))
animation_frames = [start_sprite + i for i in range((self.attack_frames if attacking else self.walk_frames))]
mcrfpy.createAnimation(
0.15, # duration, seconds
self.grid, # parent: a UIMenu or Grid key
"entity", # target type: 'menu', 'grid', 'caption', 'button', 'sprite', or 'entity'
self.entity_index, # target id: integer index for menu or grid objs; None for grid/menu
"sprite", # field: 'position', 'size', 'bgcolor', 'textcolor', or 'sprite'
self.animation_done, #callback: callable once animation is complete
False, #loop: repeat indefinitely
animation_frames # values: iterable of frames for 'sprite', lerp target for others
)
#global animations_in_progress
#animations_in_progress += 1
if move:
pos = [self.x, self.y]
if (direction == 0): pos[1] += 1
elif (direction == 1): pos[1] -= 1
elif (direction == 2): pos[0] += 1
elif (direction == 3): pos[0] -= 1
if not animove:
self.x, self.y = pos
animove = pos
else:
pos = animove
self.x, self.y = animove
#scene.move_entity(self.grid, self.entity_index, pos)
#for g in mcrfpy.listGrids():
for g in scene.grids:
if g.title == self.grid:
g.entities[self.entity_index].x = pos[0]
g.entities[self.entity_index].y = pos[1]
mcrfpy.modGrid(g, True)
if animove:
mcrfpy.createAnimation(
0.25, # duration, seconds
self.grid, # parent: a UIMenu or Grid key
"entity", # target type: 'menu', 'grid', 'caption', 'button', 'sprite', or 'entity'
self.entity_index, # target id: integer index for menu or grid objs; None for grid/menu
"position", # field: 'position', 'size', 'bgcolor', 'textcolor', or 'sprite'
self.animation_done, #callback: callable once animation is complete
False, #loop: repeat indefinitely
animove # values: iterable of frames for 'sprite', lerp target for others
)
#animations_in_progress += 1
def animation_done(self):
#global animations_in_progress
#animations_in_progress -= 1
scene.actors -= 1
#print(f"{self} done animating - {scene.actors} remaining")
if scene.actors <= 0:
scene.actors = 0
mcrfpy.unlockPlayerInput()
scene.updatehints()
class TestItemEntity(TestEntity):
def __init__(self, grid, label, tex_index, basesprite, x, y, texture_width=64, walk_frames=5, attack_frames=6, do_fov=False, item="Nothing"):
super().__init__(grid, label, tex_index, basesprite, x, y, texture_width, walk_frames, attack_frames, do_fov)
self.item = item
def interact(self, initiator, callback):
if self.label == 'dead': return super().interact(initiator, callback)
print(f"Interacted with {self.label}, an item. Adding {self.item} to {initiator.label}'s inventory")
initiator.inventory.append(self.item)
callback()
scene.itemguis()
self.die()
class TestDoorEntity(TestEntity):
def __init__(self, grid, label, tex_index, basesprite, x, y, texture_width=64, walk_frames=5, attack_frames=6, do_fov=False, key="Nothing"):
super().__init__(grid, label, tex_index, basesprite, x, y, texture_width, walk_frames, attack_frames, do_fov)
self.key = key
def interact(self, initiator, callback):
if self.label == 'dead': return super().interact(initiator, callback)
print(f"Interacted with {self.label}, a Door. ", end='')
if self.key in initiator.inventory:
initiator.inventory.remove(self.key)
print("Taking key & passing.")
callback()
scene.itemguis()
self.die()
else:
print("The door is locked.")
class TestScene:
def __init__(self, ui_name = "demobox1", grid_name = "demogrid"):
# Texture & Sound Loading
self.actors = 0
#print("Load textures")
mcrfpy.createTexture("./assets/test_portraits.png", 32, 8, 8) #0 - portraits
mcrfpy.createTexture("./assets/alives_other.png", 16, 64, 64) #1 - TinyWorld NPCs
mcrfpy.createTexture("./assets/alives_other.png", 32, 32, 32) #2 - TinyWorld NPCs - 2x2 sprite
mcrfpy.createTexture("./assets/custom_player.png", 16, 5, 13) #3 - player
mcrfpy.createTexture("./assets/Sprite-0001.png", 80, 10, 10) #4 - LGJ2023 keycard + other icons
mcrfpy.createTexture("./assets/tiny_keycards.png", 16, 8, 8) #5 - tiny keycards (ground items)
self.ui_name = ui_name
self.grid_name = grid_name
# Menu index = 0
#print("Create UI")
# Create dialog UI
mcrfpy.createMenu(ui_name, 20, 540, 500, 200)
#mcrfpy.createCaption(ui_name, "Hello There", 18, BLACK)
mcrfpy.createCaption(ui_name, "", 24, RED)
#mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKBLUE, (0, 0, 0), "clicky", "testaction")
mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKRED, (0, 0, 0), "REPL", "startrepl")
##mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKGREEN, (0, 0, 0), "map gen", "gridgen")
#mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKGREEN, (0, 0, 0), "mapL", "gridgen2")
#mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKBLUE, (192, 0, 0), "a_menu", "animtest")
#mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKRED, GREEN, "a_spr", "animtest2")
#mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKBLUE, GREEN, "Next sp", "nextsp")
#mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKBLUE, RED, "Prev sp", "prevsp")
#mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKBLUE, DARKGREEN, "+16 sp", "skipsp")
mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKGREEN, (0, 0, 0), "Next", "nextsp")
mcrfpy.createButton(ui_name, 250, 0, 130, 40, DARKBLUE, (0, 0, 0), "Prev", "prevsp")
mcrfpy.createSprite(ui_name, 4, 1, 10, 10, 2.0)
# Menu index = 1
#print("Create UI 2")
entitymenu = "entitytestmenu"
mcrfpy.createMenu(entitymenu, 840, 20, 20, 500)
mcrfpy.createButton(entitymenu, 0, 10, 150, 40, DARKBLUE, BLACK, "Up", "test_up")
mcrfpy.createButton(entitymenu, 0, 60, 150, 40, DARKBLUE, BLACK, "Down", "test_down")
mcrfpy.createButton(entitymenu, 0, 110, 150, 40, DARKBLUE, BLACK, "Left", "test_left")
mcrfpy.createButton(entitymenu, 0, 160, 150, 40, DARKBLUE, BLACK, "Right", "test_right")
mcrfpy.createButton(entitymenu, 0, 210, 150, 40, DARKBLUE, BLACK, "Attack", "test_attack")
mcrfpy.createButton(entitymenu, 0, 210, 150, 40, DARKBLUE, RED, "TE", "testent")
# Menu index = 2
mcrfpy.createMenu( "gridtitlemenu", 0, -10, 0, 0)
mcrfpy.createCaption("gridtitlemenu", "<grid name>", 18, WHITE)
#mcrfpy.createCaption("gridtitlemenu", "<camstate>", 16, WHITE)
# Menu index = 3
mcrfpy.createMenu( "hintsmenu", 0, 505, 0, 0)
mcrfpy.createCaption("hintsmenu", "<hintline>", 16, WHITE)
# Menu index = 4
# menu names must be created in alphabetical order (?!) - thanks, C++ hash map
mcrfpy.createMenu( "i", 600, 20, 0, 0)
#mcrfpy.createMenu( "camstatemenu", 600, 20, 0, 0)
mcrfpy.createCaption("i", "<camstate>", 16, WHITE)
# Menu index = 5
mcrfpy.createMenu( "j", 600, 500, 0, 0)
mcrfpy.createButton( "j", 0, 0, 80, 40, DARKBLUE, WHITE, "Recenter", "activatecamfollow")
# Menu index = 6, 7, 8, 9, 10: keycard sprites
mcrfpy.createMenu("k", 540, 540, 80, 80) #6
mcrfpy.createSprite("k", 4, 0, 10, 10, 1.0)
mcrfpy.createMenu("l", 540 + (80 * 1), 540, 80, 80) #7
mcrfpy.createSprite("l", 4, 1, 10, 10, 1.0)
mcrfpy.createMenu("m", 540 + (80 * 2), 540, 80, 80) #8
mcrfpy.createSprite("m", 4, 2, 10, 10, 1.0)
mcrfpy.createMenu("n", 540 + (80 * 3), 540, 80, 80) #9
mcrfpy.createSprite("n", 4, 3, 10, 10, 1.0)
mcrfpy.createMenu("o", 540 + (80 * 4), 540, 80, 80) #10
mcrfpy.createSprite("o", 4, 4, 10, 10, 1.0)
mcrfpy.createMenu("p", 20, 20, 40, 40) #11
#mcrfpy.createButton("p", 0, 0, 130, 40, DARKGREEN, (0, 0, 0), "Register", "keyregistration")
mcrfpy.createButton("p", 0, 0, 130, 40, DARKGREEN, (0, 0, 0), "Register", "startrepl")
mcrfpy.registerPyAction("keyregistration", keyregistration)
#print("Make UIs visible")
self.menus = mcrfpy.listMenus()
self.menus[0].visible = True
self.menus[1].w = 170
self.menus[1].visible = True
self.menus[2].visible = True
for mn in range(2, 6):
self.menus[mn].bgcolor = BLACK
self.menus[mn].visible = True
mcrfpy.modMenu(self.menus[mn])
for mn in range(6, 11):
self.menus[mn].bgcolor = BLACK
self.menus[mn].visible = False
mcrfpy.modMenu(self.menus[mn])
self.menus[11].visible=True
mcrfpy.modMenu(self.menus[11])
#self.menus[2].bgcolor = BLACK
#self.menus[3].visible = True
#self.menus[3].bgcolor = BLACK
#self.menus[4].visible = True
#self.menus[4].bgcolor = BLACK
#self.menus[5].visible = True
#mcrfpy.modMenu(self.menus[0])
#mcrfpy.modMenu(self.menus[1])
#mcrfpy.modMenu(self.menus[2])
#mcrfpy.modMenu(self.menus[3])
#mcrfpy.modMenu(self.menus[4])
#mcrfpy.modMenu(self.menus[5])
#pprint(mcrfpy.listMenus())
#print(f"UI 1 gave back this sprite: {self.menus[0].sprites}")
#print("Create grid")
# create grid (title, gx, gy, gs, x, y, w, h)
mcrfpy.createGrid(grid_name, 100, 100, 16, 20, 20, 800, 500)
self.grids = mcrfpy.listGrids()
#print(self.grids)
#print("Create entities")
#mcrfpy.createEntity("demogrid", "dragon", 2, 545, 10, 10, lambda: None)
#mcrfpy.createEntity("demogrid", "tinyenemy", 1, 1538, 3, 6, lambda: None)
#print("Create fancy entity")
self.player = TestEntity("demogrid", "player", 3, 20, 17, 3, 5, walk_frames=4, attack_frames=5, do_fov=True)
self.tes = [
TestEntity("demogrid", "classtest", 1, 1538, 5, 7, 64, walk_frames=5, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 1545, 7, 9, 64, walk_frames=5, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 1552, 9, 11, 64, walk_frames=5, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 1566, 11, 13, 64, walk_frames=4, attack_frames=6),
#TestEntity("demogrid", "item", 1, 1573, 13, 15, 64, walk_frames=4, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 1583, 15, 17, 64, walk_frames=4, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 130, 9, 7, 64, walk_frames=5, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 136, 11, 9, 64, walk_frames=5, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 143, 13, 11, 64, walk_frames=5, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 158, 15, 13, 64, walk_frames=5, attack_frames=6),
TestEntity("demogrid", "classtest", 1, 165, 17, 15, 64, walk_frames=5, attack_frames=6),
self.player,
TestItemEntity("demogrid", "GreenKeyCard", 5, 0, 19, 3, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, item="Green Keycard"),
TestItemEntity("demogrid", "BlueKeyCard", 5, 1, 21, 3, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, item="Blue Keycard"),
TestItemEntity("demogrid", "RedKeyCard", 5, 2, 23, 3, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, item="Red Keycard"),
TestItemEntity("demogrid", "OrangeKeyCard", 5, 3, 25, 3, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, item="Orange Keycard"),
TestItemEntity("demogrid", "DevilKeyCard", 5, 4, 27, 3, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, item="Devil Keycard"),
TestDoorEntity("demogrid", "GreenKeyDoor", 5, 8, 19, 5, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, key="Green Keycard"),
TestDoorEntity("demogrid", "BlueKeyDoor", 5, 9, 21, 5, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, key="Blue Keycard"),
TestDoorEntity("demogrid", "RedKeyDoor", 5, 10, 23, 5, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, key="Red Keycard"),
TestDoorEntity("demogrid", "OrangeKeyDoor", 5, 11, 25, 5, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, key="Orange Keycard"),
TestDoorEntity("demogrid", "DevilKeyDoor", 5, 12, 27, 5, texture_width=64,
walk_frames=5, attack_frames=6, do_fov=False, key="Devil Keycard")
]
self.grids = mcrfpy.listGrids()
self.entity_direction = 0
mcrfpy.registerPyAction("test_down", lambda: [te.animate(0, False, True) for te in self.tes])
mcrfpy.registerPyAction("test_up", lambda: [te.animate(1, False, True) for te in self.tes])
mcrfpy.registerPyAction("test_right", lambda: [te.animate(2, False, True) for te in self.tes])
mcrfpy.registerPyAction("test_left", lambda: [te.animate(3, False, True) for te in self.tes])
mcrfpy.registerPyAction("test_attack", lambda: [te.animate(0, True) for te in self.tes])
mcrfpy.registerPyAction("testent", lambda: [te.animate(2, True) for te in self.tes])
mcrfpy.registerPyAction("activatecamfollow", lambda: mcrfpy.camFollow(True))
# Button behavior
self.clicks = 0
self.sprite_index = 0
#mcrfpy.registerPyAction("testaction", self.click)
mcrfpy.registerPyAction("gridgen", self.gridgen)
#mcrfpy.registerPyAction("gridgen2", lambda: self.gridgen())
#mcrfpy.registerPyAction("animtest", lambda: self.createAnimation())
#mcrfpy.registerPyAction("animtest2", lambda: self.createAnimation2())
mcrfpy.registerPyAction("nextsp", lambda: self.changesprite(1))
mcrfpy.registerPyAction("prevsp", lambda: self.changesprite(-1))
mcrfpy.registerPyAction("skipsp", lambda: self.changesprite(16))
mcrfpy.unlockPlayerInput()
mcrfpy.setActiveGrid("demogrid")
self.gridgen()
self.updatehints()
mcrfpy.camFollow(True)
def itemguis(self):
print(self.player.inventory)
items = ["Green Keycard", "Blue Keycard", "Red Keycard", "Orange Keycard", "Devil Keycard"]
for mn in range(6, 11):
self.menus[mn].visible = items[mn-6] in self.player.inventory
mcrfpy.modMenu(self.menus[mn])
def animate_entity(self, direction, attacking=False):
if direction is None:
direction = self.entity_direction
else:
self.entity_direction = direction
dragon_sprite = 545 + (32 * (direction + (4 if attacking else 0)))
dragon_animation = [dragon_sprite + i for i in range((5 if attacking else 4))]
mcrfpy.createAnimation(
1.0, # duration, seconds
"demogrid", # parent: a UIMenu or Grid key
"entity", # target type: 'menu', 'grid', 'caption', 'button', 'sprite', or 'entity'
0, # target id: integer index for menu or grid objs; None for grid/menu
"sprite", # field: 'position', 'size', 'bgcolor', 'textcolor', or 'sprite'
lambda: self.animation_done("demobox1"), #callback: callable once animation is complete
False, #loop: repeat indefinitely
dragon_animation # values: iterable of frames for 'sprite', lerp target for others
)
orc_sprite = 1538 + (64 * (direction + (4 if attacking else 0)))
orc_animation = [orc_sprite + i for i in range((5 if attacking else 4))]
mcrfpy.createAnimation(
1.0, # duration, seconds
"demogrid", # parent: a UIMenu or Grid key
"entity", # target type: 'menu', 'grid', 'caption', 'button', 'sprite', or 'entity'
1, # target id: integer index for menu or grid objs; None for grid/menu
"sprite", # field: 'position', 'size', 'bgcolor', 'textcolor', or 'sprite'
lambda: self.animation_done("demobox1"), #callback: callable once animation is complete
False, #loop: repeat indefinitely
orc_animation # values: iterable of frames for 'sprite', lerp target for others
)
#def move_entity(self, targetgrid, entity_index, position):
# for i, g in enumerate(self.grids):
# if g.title == targetgrid:
# g.entities[entity_index].x = position[0]
# g.entities[entity_index].y = position[1]
# #pts = g.points
# g.visible = True
# mcrfpy.modGrid(g)
# self.grids = mcrfpy.listGrids()
# #self.grids[i].points = pts
# return
def changesprite(self, n):
self.sprite_index += n
self.menus[0].captions[0].text = f"Sprite #{self.sprite_index}"
self.menus[0].sprites[0].sprite_index = self.sprite_index
mcrfpy.modMenu(self.menus[0])
def click(self):
self.clicks += 1
self.menus[0].captions[0].text = f"Clicks: {self.clicks}"
self.menus[0].sprites[0].sprite_index = randint(0, 3)
mcrfpy.modMenu(self.menus[0])
def updatehints(self):
self.menus[2].captions[0].text=mcrfpy.activeGrid()
mcrfpy.modMenu(self.menus[2])
self.menus[3].captions[0].text=mcrfpy.inputMode()
mcrfpy.modMenu(self.menus[3])
#self.menus[4].captions[0].text=f"follow: {mcrfpy.camFollow()}"
self.menus[4].captions[0].text="following" if mcrfpy.camFollow() else "free"
mcrfpy.modMenu(self.menus[4])
self.menus[5].visible = not mcrfpy.camFollow()
mcrfpy.modMenu(self.menus[5])
def gridgen(self):
#print(f"[Python] modifying {len(self.grids[0].points)} grid points")
for p in self.grids[0].points:
#p.color = (randint(0, 255), randint(64, 192), 0)
p.color = (0,0,0)
p.walkable = False
p.transparent = False
room_centers = [(randint(0, self.grids[0].grid_x-1), randint(0, self.grids[0].grid_y-1)) for i in range(20)] + [(17, 3), (20,10) + (20,5)]
#room_centers.append((3, 5))
for r in room_centers:
# random hallway
target = choice(room_centers)
length1 = abs(target[0] - r[0])
xbase = min(target[0], r[0])
for x in range(length1):
gpoint = self.grids[0].at(x, r[1])
if gpoint is None: continue
gpoint.walkable = True
gpoint.transparent = True
gpoint.color = (192, 192, 192)
length2 = abs(target[1] - r[1])
ybase = min(target[1], r[1])
for y in range(length2):
gpoint = self.grids[0].at(r[0], y)
if gpoint is None: continue
gpoint.walkable = True
gpoint.transparent = True
gpoint.color = (192, 192, 192)
for r in room_centers:
#print(r)
room_color = (randint(16, 24)*8, randint(16, 24)*8, randint(16, 24)*8)
#self.grids[0].at(r[0], r[1]).walkable = True
#self.grids[0].at(r[0], r[1]).color = room_color
halfx, halfy = randint(2, 11), randint(2,11)
for p_x in range(r[0] - halfx, r[0] + halfx):
for p_y in range(r[1] - halfy, r[1] + halfy):
gpoint = self.grids[0].at(p_x, p_y)
if gpoint is None: continue
gpoint.walkable = True
gpoint.transparent = True
gpoint.color = room_color
#print()
#print("[Python] Modifying:")
self.grids[0].at(10, 10).color = (255, 255, 255)
self.grids[0].at(10, 10).walkable = False
self.grids[0].visible = True
mcrfpy.modGrid(self.grids[0])
#self.grids = mcrfpy.listGrids()
#print(f"Sent grid: {repr(self.grids[0])}")
#print(f"Received grid: {repr(mcrfpy.listGrids()[0])}")
def animation_done(self, s):
print(f"The `{s}` animation completed.")
#self.menus = mcrfpy.listMenus()
# if (!PyArg_ParseTuple(args, "fsssiOOO", &duration, &parent, &target_type, &target_id, &field, &callback, &loop_obj, &values_obj)) return NULL;
def createAnimation(self):
print(self.menus)
self.menus = mcrfpy.listMenus()
self.menus[0].w = 500
self.menus[0].h = 200
print(self.menus)
mcrfpy.modMenu(self.menus[0])
print(self.menus)
mcrfpy.createAnimation(
3.0, # duration, seconds
"demobox1", # parent: a UIMenu or Grid key
"menu", # target type: 'menu', 'grid', 'caption', 'button', 'sprite', or 'entity'
0, # target id: integer index for menu or grid objs; None for grid/menu
"size", # field: 'position', 'size', 'bgcolor', 'textcolor', or 'sprite'
lambda: self.animation_done("demobox1"), #callback: callable once animation is complete
False, #loop: repeat indefinitely
[150, 100] # values: iterable of frames for 'sprite', lerp target for others
)
def createAnimation2(self):
mcrfpy.createAnimation(
5,
"demobox1",
"sprite",
0,
"sprite",
lambda: self.animation_done("sprite change"),
False,
[0, 1, 2, 1, 2, 0]
)
def keytest():
print("Key tested.")
def keyregistration():
print("Registering 'keytest'")
mcrfpy.registerPyAction("keytest", keytest)
print("Registering input")
print(mcrfpy.registerInputAction(15, "keytest")) # 15 = P
mcrfpy.registerPyAction("player_move_up", lambda: scene.player.move(0, -1))
mcrfpy.registerPyAction("player_move_left", lambda: scene.player.move(-1, 0))
mcrfpy.registerPyAction("player_move_down", lambda: scene.player.move(0, 1))
mcrfpy.registerPyAction("player_move_right", lambda: scene.player.move(1, 0))
mcrfpy.registerInputAction(ord('w') - ord('a'), "player_move_up")
mcrfpy.registerInputAction(ord('a') - ord('a'), "player_move_left")
mcrfpy.registerInputAction(ord('s') - ord('a'), "player_move_down")
mcrfpy.registerInputAction(ord('d') - ord('a'), "player_move_right")
def start():
global scene
#print("TestScene.start called")
scene = TestScene()
mcrfpy.refreshFov()
scene.updatehints()

48
src/scripts/UIMenu.py Normal file
View File

@ -0,0 +1,48 @@
class Caption:
def __init__(self, text, textsize, color):
self.text = text
self.textsize = textsize
self.color = color
def __repr__(self):
return f"<Caption text={self.text}, textsize={self.textsize}, color={self.color}>"
class Button:
def __init__(self, x, y, w, h, bgcolor, textcolor, text, actioncode):
self.x = x
self.y = y
self.w = w
self.h = h
self.bgcolor = bgcolor
self.textcolor = textcolor
self.text = text
self.actioncode = actioncode
def __repr__(self):
return f"<Button ({self.x}, {self.y}, {self.w}, {self.h}), bgcolor={self.bgcolor}, textcolor={self.textcolor}, actioncode={self.actioncode}>"
class Sprite:
def __init__(self, tex_index, sprite_index, x, y):
self.tex_index = tex_index
self.sprite_index = sprite_index
self.x = x
self.y = y
def __repr__(self):
return f"<Sprite tex_index={self.tex_index}, self.sprite_index={self.sprite_index}, x={self.x}, y={self.y}>"
class UIMenu:
def __init__(self, title, x, y, w, h, bgcolor, visible=False):
self.title = title
self.x = x
self.y = y
self.w = w
self.h = h
self.bgcolor = bgcolor
self.visible = visible
self.captions = []
self.buttons = []
self.sprites = []
def __repr__(self):
return f"<UIMenu title={repr(self.title)}, x={self.x}, y={self.y}, w={self.w}, h={self.h}, bgcolor={self.bgcolor}, visible={self.visible}, {len(self.captions)} captions, {len(self.buttons)} buttons, {len(self.sprites)} sprites>"

View File

@ -1,300 +0,0 @@
import mcrfpy
mcrfpy.createScene("play")
ui = mcrfpy.sceneUI("play")
t = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16) # 12, 11)
font = mcrfpy.Font("assets/JetbrainsMono.ttf")
frame_color = (64, 64, 128)
grid = mcrfpy.Grid(20, 15, t, 10, 10, 800, 595)
grid.zoom = 2.0
entity_frame = mcrfpy.Frame(815, 10, 194, 595, fill_color = frame_color)
inventory_frame = mcrfpy.Frame(10, 610, 800, 143, fill_color = frame_color)
stats_frame = mcrfpy.Frame(815, 610, 194, 143, fill_color = frame_color)
begin_btn = mcrfpy.Frame(350,250,100,100, fill_color = (255,0,0))
begin_btn.children.append(mcrfpy.Caption(5, 5, "Begin", font))
def cos_keys(key, state):
if key == 'M' and state == 'start':
mapgen()
elif state == "end": return
elif key == "W":
player.move("N")
elif key == "A":
player.move("W")
elif key == "S":
player.move("S")
elif key == "D":
player.move("E")
def cos_init(*args):
if args[3] != "start": return
mcrfpy.keypressScene(cos_keys)
ui.remove(4)
begin_btn.click = cos_init
[ui.append(e) for e in (grid, entity_frame, inventory_frame, stats_frame, begin_btn)]
import random
def rcolor():
return tuple([random.randint(0, 255) for i in range(3)]) # TODO list won't work with GridPoint.color, so had to cast to tuple
x_max, y_max = grid.grid_size
for x in range(x_max):
for y in range(y_max):
grid.at((x,y)).color = rcolor()
from math import pi, cos, sin
def mapgen(room_size_max = 7, room_size_min = 3, room_count = 4):
# reset map
for x in range(x_max):
for y in range(y_max):
grid.at((x, y)).walkable = False
grid.at((x, y)).transparent= False
grid.at((x,y)).tilesprite = random.choices([40, 28], weights=[.8, .2])[0]
global cos_entities
for e in cos_entities:
e.e.position = (999,999) # TODO
e.die()
cos_entities = []
#Dungeon generation
centers = []
attempts = 0
while len(centers) < room_count:
# Leaving this attempt here for later comparison. These rooms sucked.
# overlapping, uninteresting hallways, crowded into the corners sometimes, etc.
attempts += 1
if attempts > room_count * 15: break
# room_left = random.randint(1, x_max)
# room_top = random.randint(1, y_max)
# Take 2 - circular distribution of rooms
angle_mid = (len(centers) / room_count) * 2 * pi + 0.785
angle = random.uniform(angle_mid - 0.25, angle_mid + 0.25)
radius = random.uniform(3, 14)
room_left = int(radius * cos(angle)) + int(x_max/2)
if room_left <= 1: room_left = 1
if room_left > x_max - 1: room_left = x_max - 2
room_top = int(radius * sin(angle)) + int(y_max/2)
if room_top <= 1: room_top = 1
if room_top > y_max - 1: room_top = y_max - 2
room_w = random.randint(room_size_min, room_size_max)
if room_w + room_left >= x_max: room_w = x_max - room_left - 2
room_h = random.randint(room_size_min, room_size_max)
if room_h + room_top >= y_max: room_h = y_max - room_top - 2
#print(room_left, room_top, room_left + room_w, room_top + room_h)
if any( # centers contained in this randomly generated room
[c[0] >= room_left and c[0] <= room_left + room_w and c[1] >= room_top and c[1] <= room_top + room_h for c in centers]
):
continue # re-randomize the room position
centers.append(
(int(room_left + (room_w/2)), int(room_top + (room_h/2)))
)
for x in range(room_w):
for y in range(room_h):
grid.at((room_left+x, room_top+y)).walkable=True
grid.at((room_left+x, room_top+y)).transparent=True
grid.at((room_left+x, room_top+y)).tilesprite = random.choice([48, 49, 50, 51, 52, 53])
# generate a boulder
if (room_w > 2 and room_h > 2):
room_boulder_x, room_boulder_y = random.randint(room_left+1, room_left+room_w-1), random.randint(room_top+1, room_top+room_h-1)
cos_entities.append(BoulderEntity(room_boulder_x, room_boulder_y))
print(f"{room_count} rooms generated after {attempts} attempts.")
#print(centers)
# hallways
pairs = []
for c1 in centers:
for c2 in centers:
if c1 == c2: continue
if (c2, c1) in pairs or (c1, c2) in pairs: continue
left = min(c1[0], c2[0])
right = max(c1[0], c2[0])
top = min(c1[1], c2[1])
bottom = max(c1[1], c2[1])
corners = [(left, top), (left, bottom), (right, top), (right, bottom)]
corners.remove(c1)
corners.remove(c2)
random.shuffle(corners)
target, other = corners
for x in range(target[0], other[0], -1 if target[0] > other[0] else 1):
was_walkable = grid.at((x, target[1])).walkable
grid.at((x, target[1])).walkable=True
grid.at((x, target[1])).transparent=True
if not was_walkable:
grid.at((x, target[1])).tilesprite = random.choices([0, 12, 24], weights=[.6, .3, .1])[0]
for y in range(target[1], other[1], -1 if target[1] > other[1] else 1):
was_walkable = grid.at((target[0], y)).walkable
grid.at((target[0], y)).walkable=True
grid.at((target[0], y)).transparent=True
if not was_walkable:
grid.at((target[0], y)).tilesprite = random.choices([0, 12, 24], weights=[0.4, 0.3, 0.3])[0]
pairs.append((c1, c2))
# spawn exit and button
spawn_points = []
for x in range(x_max):
for y in range(y_max):
if grid.at((x, y)).walkable:
spawn_points.append((x, y))
random.shuffle(spawn_points)
door_spawn, button_spawn = spawn_points[:2]
cos_entities.append(ExitEntity(*door_spawn, *button_spawn))
# respawn player
global player
if player:
player.position = (999,999) # TODO - die() is broken and I don't know why
player = PlayerEntity()
#for x in range(x_max):
# for y in range(y_max):
# if grid.at((x, y)).walkable:
# #grid.at((x,y)).tilesprite = random.choice([48, 49, 50, 51, 52, 53])
# pass
# else:
# #grid.at((x,y)).tilesprite = random.choices([40, 28], weights=[.8, .2])[0]
#131 - last sprite
#123, 124 - brown, grey rats
#121 - ghost
#114, 115, 116 - green, red, blue potion
#102 - shield
#98 - low armor guy, #97 - high armor guy
#89 - chest, #91 - empty chest
#84 - wizard
#82 - barrel
#66 - boulder
#64, 65 - graves
#48 - 53: ground (not going to figure out how they fit together tonight)
#42 - button-looking ground
#40 - basic solid wall
#36, 37, 38 - wall (left, middle, right)
#28 solid wall but with a grate
#21 - wide open door, 33 medium open, 45 closed door
#0 - basic dirt
class MyEntity:
def __init__(self, x=0, y=0, sprite_num=86):
self.e = mcrfpy.Entity(x, y, t, sprite_num)
grid.entities.append(self.e)
def die(self):
for i in range(len(grid.entities)):
e = grid.entities[i]
if e == self.e:
grid.entities.remove(i)
break
def bump(self, other, dx, dy):
raise NotImplementedError
def try_move(self, dx, dy):
tx, ty = int(self.e.position[0] + dx), int(self.e.position[1] + dy)
for e in cos_entities:
if e.e.position == (tx, ty):
#print(f"bumping {e}")
return e.bump(self, dx, dy)
if tx < 0 or tx >= x_max:
#print("out of bounds horizontally")
return False
if ty < 0 or ty >= y_max:
#print("out of bounds vertically")
return False
if grid.at((tx, ty)).walkable == True:
#print("Motion!")
self.e.position = (tx, ty)
return True
else:
#print("Bonk")
return False
def _relative_move(self, dx, dy):
tx, ty = int(self.e.position[0] + dx), int(self.e.position[1] + dy)
self.e.position = (tx, ty)
def move(self, direction):
if direction == "N":
self.try_move(0, -1)
elif direction == "E":
self.try_move(1, 0)
elif direction == "S":
self.try_move(0, 1)
elif direction == "W":
self.try_move(-1, 0)
cos_entities = []
class PlayerEntity(MyEntity):
def __init__(self):
# find spawn point
spawn_points = []
for x in range(x_max):
for y in range(y_max):
if grid.at((x, y)).walkable:
spawn_points.append((x, y))
random.shuffle(spawn_points)
for spawn in spawn_points:
for e in cos_entities:
if e.e.position == spawn: break
else:
break
#print(f"spawn at {spawn}")
super().__init__(spawn[0], spawn[1], sprite_num=84)
class BoulderEntity(MyEntity):
def __init__(self, x, y):
super().__init__(x, y, 66)
def bump(self, other, dx, dy):
if type(other) == BoulderEntity:
#print("Boulders can't push boulders")
return False
tx, ty = int(self.e.position[0] + dx), int(self.e.position[1] + dy)
# Is the boulder blocked the same direction as the bumper? If not, let's both move
old_pos = int(self.e.position[0]), int(self.e.position[1])
if self.try_move(dx, dy):
other.e.position = old_pos
return True
class ButtonEntity(MyEntity):
def __init__(self, x, y, exit):
super().__init__(x, y, 42)
self.exit = exit
def bump(self, other, dx, dy):
if type(other) == BoulderEntity:
self.exit.unlock()
other._relative_move(dx, dy)
return True
class ExitEntity(MyEntity):
def __init__(self, x, y, bx, by):
super().__init__(x, y, 45)
self.my_button = ButtonEntity(bx, by, self)
self.unlocked = False
global cos_entities
cos_entities.append(self.my_button)
def unlock(self):
self.e.sprite_number = 21
self.unlocked = True
def lock(self):
self.e.sprite_number = 45
self.unlocked = True
def bump(self, other, dx, dy):
if type(other) == BoulderEntity:
return False
if self.unlocked:
other._relative_move(dx, dy)
player = None

View File

@ -0,0 +1,10 @@
print("[Python] Attempting import")
import scriptable
print(f"[Python] calling fibonacci(8): {scriptable.fibonacci(8)}")
print(f"[Python] calling fibonacci(15): {scriptable.fibonacci(15)}")
import venv
print("[Python] Importing library installed with pip")
import numpy

View File

@ -1,60 +0,0 @@
import mcrfpy
font = mcrfpy.Font("assets/JetbrainsMono.ttf")
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
print("[game.py] Default texture:")
print(mcrfpy.default_texture)
print(type(mcrfpy.default_texture))
# build test widgets
mcrfpy.createScene("pytest")
mcrfpy.setScene("pytest")
ui = mcrfpy.sceneUI("pytest")
# Frame
f = mcrfpy.Frame(25, 19, 462, 346, fill_color=(255, 92, 92))
print("Frame alive")
# fill (LinkedColor / Color): f.fill_color
# outline (LinkedColor / Color): f.outline_color
# pos (LinkedVector / Vector): f.pos
# size (LinkedVector / Vector): f.size
# Caption
print("Caption attempt w/ fill_color:")
#c = mcrfpy.Caption(512+25, 19, "Hi.", font)
#c = mcrfpy.Caption(512+25, 19, "Hi.", font, fill_color=(255, 128, 128))
c = mcrfpy.Caption(512+25, 19, "Hi.", font, fill_color=mcrfpy.Color(255, 128, 128), outline_color=(128, 255, 128))
print("Caption alive")
# fill (LinkedColor / Color): c.fill_color
#color_val = c.fill_color
print(c.fill_color)
#print("Set a fill color")
#c.fill_color = (255, 255, 255)
print("Lol, did it segfault?")
# outline (LinkedColor / Color): c.outline_color
# font (Font): c.font
# pos (LinkedVector / Vector): c.pos
# Sprite
s = mcrfpy.Sprite(25, 384+19, texture, 86, 9.0)
# pos (LinkedVector / Vector): s.pos
# texture (Texture): s.texture
# Grid
g = mcrfpy.Grid(10, 10, texture, 512+25, 384+19, 462, 346)
# texture (Texture): g.texture
# pos (LinkedVector / Vector): g.pos
# size (LinkedVector / Vector): g.size
for _x in range(10):
for _y in range(10):
g.at((_x, _y)).color = (255 - _x*25, 255 - _y*25, 255)
g.zoom = 2.0
[ui.append(d) for d in (f, c, s, g)]
print("built!")
# tests

View File

@ -1,221 +0,0 @@
#print("Hello mcrogueface")
import mcrfpy
import cos_play
# Universal stuff
font = mcrfpy.Font("assets/JetbrainsMono.ttf")
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16) #12, 11)
texture_cold = mcrfpy.Texture("assets/kenney_ice.png", 16, 16) #12, 11)
texture_hot = mcrfpy.Texture("assets/kenney_lava.png", 16, 16) #12, 11)
# Test stuff
mcrfpy.createScene("boom")
mcrfpy.setScene("boom")
ui = mcrfpy.sceneUI("boom")
box = mcrfpy.Frame(40, 60, 200, 300, fill_color=(255,128,0), outline=4.0, outline_color=(64,64,255,96))
ui.append(box)
#caption = mcrfpy.Caption(10, 10, "Clicky", font, (255, 255, 255, 255), (0, 0, 0, 255))
#box.click = lambda x, y, btn, type: print("Hello callback: ", x, y, btn, type)
#box.children.append(caption)
test_sprite_number = 86
sprite = mcrfpy.Sprite(20, 60, texture, test_sprite_number, 4.0)
spritecap = mcrfpy.Caption(5, 5, "60", font)
def click_sprite(x, y, btn, action):
global test_sprite_number
if action != "start": return
if btn in ("left", "wheel_up"):
test_sprite_number -= 1
elif btn in ("right", "wheel_down"):
test_sprite_number += 1
sprite.sprite_number = test_sprite_number # TODO - inconsistent naming for __init__, __repr__ and getsetter: sprite_number vs sprite_index
spritecap.text = test_sprite_number
sprite.click = click_sprite # TODO - sprites don't seem to correct for screen position or scale when clicking
box.children.append(sprite)
box.children.append(spritecap)
box.click = click_sprite
f_a = mcrfpy.Frame(250, 60, 80, 80, fill_color=(255, 92, 92))
f_a_txt = mcrfpy.Caption(5, 5, "0", font)
f_b = mcrfpy.Frame(340, 60, 80, 80, fill_color=(92, 255, 92))
f_b_txt = mcrfpy.Caption(5, 5, "0", font)
f_c = mcrfpy.Frame(430, 60, 80, 80, fill_color=(92, 92, 255))
f_c_txt = mcrfpy.Caption(5, 5, "0", font)
ui.append(f_a)
f_a.children.append(f_a_txt)
ui.append(f_b)
f_b.children.append(f_b_txt)
ui.append(f_c)
f_c.children.append(f_c_txt)
import sys
def ding(*args):
f_a_txt.text = str(sys.getrefcount(ding)) + " refs"
f_b_txt.text = sys.getrefcount(dong)
f_c_txt.text = sys.getrefcount(stress_test)
def dong(*args):
f_a_txt.text = str(sys.getrefcount(ding)) + " refs"
f_b_txt.text = sys.getrefcount(dong)
f_c_txt.text = sys.getrefcount(stress_test)
running = False
timers = []
def add_ding():
global timers
n = len(timers)
mcrfpy.setTimer(f"timer{n}", ding, 100)
print("+1 ding:", timers)
def add_dong():
global timers
n = len(timers)
mcrfpy.setTimer(f"timer{n}", dong, 100)
print("+1 dong:", timers)
def remove_random():
global timers
target = random.choice(timers)
print("-1 timer:", target)
print("remove from list")
timers.remove(target)
print("delTimer")
mcrfpy.delTimer(target)
print("done")
import random
import time
def stress_test(*args):
global running
global timers
if not running:
print("stress test initial")
running = True
timers.append("recurse")
add_ding()
add_dong()
mcrfpy.setTimer("recurse", stress_test, 1000)
mcrfpy.setTimer("terminate", lambda *args: mcrfpy.delTimer("recurse"), 30000)
ding(); dong()
else:
#print("stress test random activity")
#random.choice([
# add_ding,
# add_dong,
# remove_random
# ])()
#print(timers)
print("Segfaultin' time")
mcrfpy.delTimer("recurse")
print("Does this still work?")
time.sleep(0.5)
print("How about now?")
stress_test()
# Loading Screen
mcrfpy.createScene("loading")
ui = mcrfpy.sceneUI("loading")
#mcrfpy.setScene("loading")
logo_texture = mcrfpy.Texture("assets/temp_logo.png", 1024, 1024)#1, 1)
logo_sprite = mcrfpy.Sprite(50, 50, logo_texture, 0, 0.5)
ui.append(logo_sprite)
logo_sprite.click = lambda *args: mcrfpy.setScene("menu")
logo_caption = mcrfpy.Caption(70, 600, "Click to Proceed", font, (255, 0, 0, 255), (0, 0, 0, 255))
#logo_caption.fill_color =(255, 0, 0, 255)
ui.append(logo_caption)
# menu screen
mcrfpy.createScene("menu")
for e in [
mcrfpy.Caption(10, 10, "Crypt of Sokoban", font, (255, 255, 255), (0, 0, 0)),
mcrfpy.Caption(20, 55, "a McRogueFace demo project", font, (192, 192, 192), (0, 0, 0)),
mcrfpy.Frame(15, 70, 150, 60, fill_color=(64, 64, 128)),
mcrfpy.Frame(15, 145, 150, 60, fill_color=(64, 64, 128)),
mcrfpy.Frame(15, 220, 150, 60, fill_color=(64, 64, 128)),
mcrfpy.Frame(15, 295, 150, 60, fill_color=(64, 64, 128)),
#mcrfpy.Frame(900, 10, 100, 100, fill_color=(255, 0, 0)),
]:
mcrfpy.sceneUI("menu").append(e)
def click_once(fn):
def wraps(*args, **kwargs):
#print(args)
action = args[3]
if action != "start": return
return fn(*args, **kwargs)
return wraps
@click_once
def asdf(x, y, btn, action):
print(f"clicky @({x},{y}) {action}->{btn}")
@click_once
def clicked_exit(*args):
mcrfpy.exit()
menu_btns = [
("Boom", lambda *args: 1 / 0),
("Exit", clicked_exit),
("About", lambda *args: mcrfpy.setScene("about")),
("Settings", lambda *args: mcrfpy.setScene("settings")),
("Start", lambda *args: mcrfpy.setScene("play"))
]
for i in range(len(mcrfpy.sceneUI("menu"))):
e = mcrfpy.sceneUI("menu")[i] # TODO - fix iterator
#print(e, type(e))
if type(e) is not mcrfpy.Frame: continue
label, fn = menu_btns.pop()
#print(label)
e.children.append(mcrfpy.Caption(5, 5, label, font, (192, 192, 255), (0,0,0)))
e.click = fn
# settings screen
mcrfpy.createScene("settings")
window_scaling = 1.0
scale_caption = mcrfpy.Caption(180, 70, "1.0x", font, (255, 255, 255), (0, 0, 0))
#scale_caption.fill_color = (255, 255, 255) # TODO - mcrfpy.Caption.__init__ is not setting colors
for e in [
mcrfpy.Caption(10, 10, "Settings", font, (255, 255, 255), (0, 0, 0)),
mcrfpy.Frame(15, 70, 150, 60, fill_color=(64, 64, 128)), # +
mcrfpy.Frame(300, 70, 150, 60, fill_color=(64, 64, 128)), # -
mcrfpy.Frame(15, 295, 150, 60, fill_color=(64, 64, 128)),
scale_caption,
]:
mcrfpy.sceneUI("settings").append(e)
@click_once
def game_scale(x, y, btn, action, delta):
global window_scaling
print(f"WIP - scale the window from {window_scaling:.1f} to {window_scaling+delta:.1f}")
window_scaling += delta
scale_caption.text = f"{window_scaling:.1f}x"
mcrfpy.setScale(window_scaling)
#mcrfpy.setScale(2)
settings_btns = [
("back", lambda *args: mcrfpy.setScene("menu")),
("-", lambda x, y, btn, action: game_scale(x, y, btn, action, -0.1)),
("+", lambda x, y, btn, action: game_scale(x, y, btn, action, +0.1))
]
for i in range(len(mcrfpy.sceneUI("settings"))):
e = mcrfpy.sceneUI("settings")[i] # TODO - fix iterator
#print(e, type(e))
if type(e) is not mcrfpy.Frame: continue
label, fn = settings_btns.pop()
#print(label, fn)
e.children.append(mcrfpy.Caption(5, 5, label, font, (192, 192, 255), (0,0,0)))
e.click = fn

37
src/scripts/test_ui.py Normal file
View File

@ -0,0 +1,37 @@
import mcrfpy
mcrfpy.createTexture("./assets/test_portraits.png", 32, 8, 8)
from random import choice, randint
box_colors = [
(0, 0, 192),
(0, 192, 0),
(192, 0, 0),
(192, 192, 0),
(0, 192, 192),
(192, 0, 192)
]
text_colors = [
(0, 0, 255),
(0, 255, 0),
(255, 0, 0),
(255, 255, 0),
(0, 255, 255),
(255, 0, 255)
]
test_x = 500
test_y = 10
for i in range(40):
ui_name = f"test{i}"
mcrfpy.createMenu(ui_name, test_x, test_y, 400, 200)
mcrfpy.createCaption(ui_name, "Hello There", 18, choice(text_colors))
mcrfpy.createButton(ui_name, 250, 20, 100, 50, choice(box_colors), (0, 0, 0), "asdf", "testaction")
mcrfpy.createSprite(ui_name, 0, randint(0, 3), 650, 60, 5.0)
test_x -= 50
test_y += 50
if (test_x <= 50):
test_x = 500
#print(test_x)

View File

@ -1,168 +0,0 @@
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]

View File

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