Compare commits

...

59 Commits

Author SHA1 Message Date
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
70 changed files with 5641 additions and 304 deletions

4
.gitignore vendored
View File

@ -3,3 +3,7 @@
bin
.msvc-intermediate/
*.bak*
PCbuild
.vs
obj

101
JANKFILE.md Normal file
View File

@ -0,0 +1,101 @@
```
* python API overhaul
* At the very least, a function to turn a color tuple to sf::Color would be good.
All the sf::Color objects going into / out of Python need to support transparency (4th value of Alpha) - Python would tolerate it, C++ needs to allow it (parse "iii|i", default alpha of 0 for fully nontransparent)
* The entire method of reading properties to re-ingest Grids/Menus is stupid.
C++ classes should be exposed more directly, or have clean methods to accept the changes from Python code.
* return actual exceptions instead of std::cout and NULL
* smart pointers (`std::shared_ptr<PyObject>`) to C++-ify Python object passing? Can this be made to work with reference counting?
* Raise Exceptions - see *janknote* in the Animation API method
* C++ hygene: Make data members private, call getter/setter methods
* std::shared_ptr, not bare pointers. The grid/entity/component connections suffer from this
* SFML: "widget" hierarchy. Keep all sizes/positions in the RectangleShape objects. Make Grid and UIMenu subclasses of the same base, and keep a single map of drawable widgets.
* UIMenu should become a "frame", and draw any objects of the "Widget" class relatively to its own positions
* "Widget" as an abstract class should set the template for going to/from Python; don't copy to and from Python, the C++ object *is* the Python object.
* ==Buttons don't know their parent...?== So there's arithmetic happening in the event loop to determine it's actual positions. Fix this with the widgets-on-widgets system (so we can go deeper, and just ask the widgets if they or their children were clicked)
* Keep aspect ratio correct (and hopefully unbork any mouse issues) when maximizing / full screening the game
```
# r/RoguelikeDev Does the Complete Roguelike Tutorial - July 2023
## Planning
Event ends roughly 26 August (last post will be 22 August)
* Add and remove keystroke registration from Python scripts
* Error checking: raise Python exceptions instead of null reference segfault in C++
* Proper exception handling: figure out the "any code at REPL shows the unhandled exception" bug thingy
* Extra clarity: display more status info about what Python stuff is being done
- load all files in directory at first
- list modules / classes found
- list Scenes that were found
- Read all Python modules, then call objects & methods at C++-managed events (Engine provided decorators, perhaps??)
* PythonScene version of MenuScene
- instantiate PythonScenes
* Switch Scenes from Python (edge of board / stairs / teleportation feature)
* Update the visible map from Python (fix "map blank until you move" bug)
- update "camera state" and "Recenter camera" buttons' API info as well without a whole Turn
* C++/TCOD Entity pathfinding without calling Python every Turn
* Replace jank .py files that define engine-required objects with C++ definitions inside of `mcrfpy` module
This actually a pretty big list, but there's about 7 weeks left, so it's just over one item per week.
I have no bad feelings if these items leak over to EngJam or beyond.
## Notes 12 July
Some changes to make in McRFPy_API.cpp:
* create a parallel to _registerPyAction which will register a keystroke to a Python label in the current scene.
## Notes 13 July
- 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.
- then remove `McRFPy_API::player_input`
This behavior can be more flexibly implemented in Python using keyboard registration. There's some deduplication as well, since I have a Python implementation of collision detection anyway (for items, doors, and NPCs).
Also, upgraded to C++20 (g++ `c++2a`), mostly because I want to use map::contains.
More ideas:
* Need to make "pan" and "zoom" optional on each grid (for minimap type use cases)
* clicks are handled by C++ and only used on buttons. Grids could have a Python click function (with pixel & grid coords available)
* pixel-on-menu Python click function should be possible too
* Need to call a Python update function when C++ events cause camera following to change: UI is only updating after player input
Continue with:
* implement checks in PythonScene::registerActionInjected - Save injected actions, don't let regular actions be overwritten, return success
* Remove PythonScene key definitions and McRFPy_API::player_input
* re-implement walking via keyboard input in Python
* Find a good spot for camera following to update Python immediately
* Find a good spot for grid updates to redraw TCOD line of sight immediately
## Notes 16 July
Main problem that came up today: all Python code is executed at the moment the GameEngine instantiates the PythonScene, which is actually when the game starts up. The active scene at that point is the MenuScene, so the Python code registers events against that scene (which rejects injected event binding and has no API functionality).
Workaround: There's a clickable button that performs the input registration. This is good for working out the behavior, but doesn't really allow Python scripts to properly control and set up their own environment.
The module name is passed to the PythonScene constructor, and the `start()` method is called to set up class objects. Can I add more methods that are called on this module to swap scenes?
## Notes 17 July
The player entity is moving around via Python now!
Unfortunately, the "actiononce" macro I use in C++ is not the behavior passed on to the Python key events. Key release events are being passed to Python just the way that keypresses are.
I'll need to expose more of the input event properties down to Python. I also don't like how keycodes are being passed from python to C++, which currently limits user input to key strokes. Mouse buttons and mouse wheel should be possible too (just as they are under the SFML event binding).

View File

@ -1,3 +1,9 @@
# About McRogueFace Engine
This software is distributed on Github and other git repos for review & discussion purposes. This engine was made in preparation and during the 2023 7 Day Roguelike game jam. It's not production ready. I don't want to support this buggy version of the software - do not create derivative works, modify, or redistribute this git repo. Not that you'd actually want to: take a close look at the code and you'll quickly find reasons not to depend on the codebase in its present state.
I expect to release the engine under BSD, MIT, or similar license after overhauling my technical debt incurred during the game jam. see JANKFILE.md for outstanding issues.
# Software Licenses
## Dependencies included in this repo

View File

@ -7,7 +7,7 @@ An experimental prototype game engine built for my own use in 7DRL 2023.
## Tenets:
* C++ first, Python close behind.
* Entity-Component system based on David Churchill's Memorial University COP4300 course lectures available on Youtube.
* Entity-Component system based on David Churchill's Memorial University COMP4300 course lectures available on Youtube.
* Graphics, particles and shaders provided by SFML.
* Pathfinding, noise generation, and other Roguelike goodness provided by TCOD.
@ -18,12 +18,13 @@ I did the r/RoguelikeDev TCOD tutorial in Python. I loved it, but I did not want
## To-do
* ✅ Initial Commit
* Integrate scene, action, entity, component system from COMP4300 engine
* Windows / Visual Studio project
* Draw Sprites
* Play Sounds
* Draw UI, spawn entity from Python code
* Integrate scene, action, entity, component system from COMP4300 engine
* Windows / Visual Studio project
* Draw Sprites
* Play Sounds
* Draw UI, spawn entity from Python code
* ❌ Python AI for entities (NPCs on set paths, enemies towards player)
* ❌ Walking / Collision; "Boards" (stairs / doors / walk off edge of screen)
* ✅ Walking / Collision
* ❌ "Boards" (stairs / doors / walk off edge of screen)
* ❌ Cutscenes - interrupt normal controls, text scroll, character portraits
* ❌ Mouse integration - tooltips, zoom, click to select targets, cursors

BIN
assets/Sprite-0001.ase Normal file

Binary file not shown.

BIN
assets/Sprite-0001.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
assets/alives_other.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

BIN
assets/boom.wav Normal file

Binary file not shown.

BIN
assets/custom_player.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
assets/gamescale_decor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
assets/terrain.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
assets/terrain_alpha.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
assets/test_portraits.ase Normal file

Binary file not shown.

BIN
assets/test_portraits.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -2,6 +2,8 @@
#rm -R bin/linux
mkdir -p bin/linux/lib
mkdir -p obj
rm obj/*
# copy shared objects, squish "linux" subdirectory in bin/linux/lib
#cp -R lib/linux/* bin/linux/lib
@ -15,13 +17,49 @@ cp -R src/scripts bin/linux/scripts
# work from output directory and change every g++ path to relative D:<
cd bin/linux
# prepare object files of engine classes
abort_compile()
{
echo "Compilation failed on $fn.cpp"
exit 1
}
# Precompile engine classes. Get errors in their file, not where they're included
for fn in $(ls ../../src/*.cpp -1 | cut -d/ -f4 | cut -d. -f1)
do
# Skip combined_poc.cpp, it has a duplicate main
if [ "$fn" = "combined_poc" ]; then continue; fi
echo "Compile $fn.cpp"
g++ \
-I../../deps_linux \
-I../../deps_linux/Python-3.11.1 \
-I../../platform/linux \
--std=c++2a \
-c ../../src/$fn.cpp \
-o ../../obj/$fn.o \
-lm \
-ldl \
-lutil \
-lpthread \
-lpython3.11 \
-lsfml-graphics \
-lsfml-window \
-lsfml-system \
-lsfml-audio \
-ltcod \
|| abort_compile $fn
done
# Final executable
# --std= : c++2a vs c++17
g++ \
--std=c++17 \
--std=c++2a \
-I../../deps_linux \
-I../../deps_linux/Python-3.11.1 \
-I../../platform/linux \
../../src/combined_poc.cpp \
-o poc \
../../obj/*.o \
-o mcrogueface \
-Wl,-rpath lib \
-L../../deps_linux \
-lm \
@ -32,5 +70,6 @@ g++ \
-lsfml-graphics \
-lsfml-window \
-lsfml-system \
-ltcod
-lsfml-audio \
-ltcod \

4
debug_linux.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
cp src/scripts/*.py bin/linux/scripts/
cd bin/linux
gdb ./mcrogueface

View File

@ -0,0 +1,710 @@
#ifndef Py_CONFIG_H
#define Py_CONFIG_H
/* pyconfig.h. NOT Generated automatically by configure.
This is a manually maintained version used for the Watcom,
Borland and Microsoft Visual C++ compilers. It is a
standard part of the Python distribution.
WINDOWS DEFINES:
The code specific to Windows should be wrapped around one of
the following #defines
MS_WIN64 - Code specific to the MS Win64 API
MS_WIN32 - Code specific to the MS Win32 (and Win64) API (obsolete, this covers all supported APIs)
MS_WINDOWS - Code specific to Windows, but all versions.
Py_ENABLE_SHARED - Code if the Python core is built as a DLL.
Also note that neither "_M_IX86" or "_MSC_VER" should be used for
any purpose other than "Windows Intel x86 specific" and "Microsoft
compiler specific". Therefore, these should be very rare.
NOTE: The following symbols are deprecated:
NT, USE_DL_EXPORT, USE_DL_IMPORT, DL_EXPORT, DL_IMPORT
MS_CORE_DLL.
WIN32 is still required for the locale module.
*/
/* Deprecated USE_DL_EXPORT macro - please use Py_BUILD_CORE */
#ifdef USE_DL_EXPORT
# define Py_BUILD_CORE
#endif /* USE_DL_EXPORT */
/* Visual Studio 2005 introduces deprecation warnings for
"insecure" and POSIX functions. The insecure functions should
be replaced by *_s versions (according to Microsoft); the
POSIX functions by _* versions (which, according to Microsoft,
would be ISO C conforming). Neither renaming is feasible, so
we just silence the warnings. */
#ifndef _CRT_SECURE_NO_DEPRECATE
#define _CRT_SECURE_NO_DEPRECATE 1
#endif
#ifndef _CRT_NONSTDC_NO_DEPRECATE
#define _CRT_NONSTDC_NO_DEPRECATE 1
#endif
#define HAVE_IO_H
#define HAVE_SYS_UTIME_H
#define HAVE_TEMPNAM
#define HAVE_TMPFILE
#define HAVE_TMPNAM
#define HAVE_CLOCK
#define HAVE_STRERROR
#include <io.h>
#define HAVE_STRFTIME
#define DONT_HAVE_SIG_ALARM
#define DONT_HAVE_SIG_PAUSE
#define LONG_BIT 32
#define WORD_BIT 32
#define MS_WIN32 /* only support win32 and greater. */
#define MS_WINDOWS
#define NT_THREADS
#define WITH_THREAD
#ifndef NETSCAPE_PI
#define USE_SOCKET
#endif
/* Compiler specific defines */
/* ------------------------------------------------------------------------*/
/* Microsoft C defines _MSC_VER */
#ifdef _MSC_VER
/* We want COMPILER to expand to a string containing _MSC_VER's *value*.
* This is horridly tricky, because the stringization operator only works
* on macro arguments, and doesn't evaluate macros passed *as* arguments.
* Attempts simpler than the following appear doomed to produce "_MSC_VER"
* literally in the string.
*/
#define _Py_PASTE_VERSION(SUFFIX) \
("[MSC v." _Py_STRINGIZE(_MSC_VER) " " SUFFIX "]")
/* e.g., this produces, after compile-time string catenation,
* ("[MSC v.1200 32 bit (Intel)]")
*
* _Py_STRINGIZE(_MSC_VER) expands to
* _Py_STRINGIZE1((_MSC_VER)) expands to
* _Py_STRINGIZE2(_MSC_VER) but as this call is the result of token-pasting
* it's scanned again for macros and so further expands to (under MSVC 6)
* _Py_STRINGIZE2(1200) which then expands to
* "1200"
*/
#define _Py_STRINGIZE(X) _Py_STRINGIZE1((X))
#define _Py_STRINGIZE1(X) _Py_STRINGIZE2 ## X
#define _Py_STRINGIZE2(X) #X
/* MSVC defines _WINxx to differentiate the windows platform types
Note that for compatibility reasons _WIN32 is defined on Win32
*and* on Win64. For the same reasons, in Python, MS_WIN32 is
defined on Win32 *and* Win64. Win32 only code must therefore be
guarded as follows:
#if defined(MS_WIN32) && !defined(MS_WIN64)
*/
#ifdef _WIN64
#define MS_WIN64
#endif
/* set the COMPILER and support tier
*
* win_amd64 MSVC (x86_64-pc-windows-msvc): 1
* win32 MSVC (i686-pc-windows-msvc): 1
* win_arm64 MSVC (aarch64-pc-windows-msvc): 3
* other archs and ICC: 0
*/
#ifdef MS_WIN64
#if defined(_M_X64) || defined(_M_AMD64)
#if defined(__INTEL_COMPILER)
#define COMPILER ("[ICC v." _Py_STRINGIZE(__INTEL_COMPILER) " 64 bit (amd64) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]")
#define PY_SUPPORT_TIER 0
#else
#define COMPILER _Py_PASTE_VERSION("64 bit (AMD64)")
#define PY_SUPPORT_TIER 1
#endif /* __INTEL_COMPILER */
#define PYD_PLATFORM_TAG "win_amd64"
#elif defined(_M_ARM64)
#define COMPILER _Py_PASTE_VERSION("64 bit (ARM64)")
#define PY_SUPPORT_TIER 3
#define PYD_PLATFORM_TAG "win_arm64"
#else
#define COMPILER _Py_PASTE_VERSION("64 bit (Unknown)")
#define PY_SUPPORT_TIER 0
#endif
#endif /* MS_WIN64 */
/* set the version macros for the windows headers */
/* Python 3.9+ requires Windows 8 or greater */
#define Py_WINVER 0x0602 /* _WIN32_WINNT_WIN8 */
#define Py_NTDDI NTDDI_WIN8
/* We only set these values when building Python - we don't want to force
these values on extensions, as that will affect the prototypes and
structures exposed in the Windows headers. Even when building Python, we
allow a single source file to override this - they may need access to
structures etc so it can optionally use new Windows features if it
determines at runtime they are available.
*/
#if defined(Py_BUILD_CORE) || defined(Py_BUILD_CORE_BUILTIN) || defined(Py_BUILD_CORE_MODULE)
#ifndef NTDDI_VERSION
#define NTDDI_VERSION Py_NTDDI
#endif
#ifndef WINVER
#define WINVER Py_WINVER
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT Py_WINVER
#endif
#endif
/* _W64 is not defined for VC6 or eVC4 */
#ifndef _W64
#define _W64
#endif
/* Define like size_t, omitting the "unsigned" */
#ifdef MS_WIN64
typedef __int64 Py_ssize_t;
# define PY_SSIZE_T_MAX LLONG_MAX
#else
typedef _W64 int Py_ssize_t;
# define PY_SSIZE_T_MAX INT_MAX
#endif
#define HAVE_PY_SSIZE_T 1
#if defined(MS_WIN32) && !defined(MS_WIN64)
#if defined(_M_IX86)
#if defined(__INTEL_COMPILER)
#define COMPILER ("[ICC v." _Py_STRINGIZE(__INTEL_COMPILER) " 32 bit (Intel) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]")
#define PY_SUPPORT_TIER 0
#else
#define COMPILER _Py_PASTE_VERSION("32 bit (Intel)")
#define PY_SUPPORT_TIER 1
#endif /* __INTEL_COMPILER */
#define PYD_PLATFORM_TAG "win32"
#elif defined(_M_ARM)
#define COMPILER _Py_PASTE_VERSION("32 bit (ARM)")
#define PYD_PLATFORM_TAG "win_arm32"
#define PY_SUPPORT_TIER 0
#else
#define COMPILER _Py_PASTE_VERSION("32 bit (Unknown)")
#define PY_SUPPORT_TIER 0
#endif
#endif /* MS_WIN32 && !MS_WIN64 */
typedef int pid_t;
/* define some ANSI types that are not defined in earlier Win headers */
#if _MSC_VER >= 1200
/* This file only exists in VC 6.0 or higher */
#include <basetsd.h>
#endif
#endif /* _MSC_VER */
/* ------------------------------------------------------------------------*/
/* egcs/gnu-win32 defines __GNUC__ and _WIN32 */
#if defined(__GNUC__) && defined(_WIN32)
/* XXX These defines are likely incomplete, but should be easy to fix.
They should be complete enough to build extension modules. */
/* Suggested by Rene Liebscher <R.Liebscher@gmx.de> to avoid a GCC 2.91.*
bug that requires structure imports. More recent versions of the
compiler don't exhibit this bug.
*/
#if (__GNUC__==2) && (__GNUC_MINOR__<=91)
#warning "Please use an up-to-date version of gcc! (>2.91 recommended)"
#endif
#define COMPILER "[gcc]"
#define PY_LONG_LONG long long
#define PY_LLONG_MIN LLONG_MIN
#define PY_LLONG_MAX LLONG_MAX
#define PY_ULLONG_MAX ULLONG_MAX
#endif /* GNUC */
/* ------------------------------------------------------------------------*/
/* lcc-win32 defines __LCC__ */
#if defined(__LCC__)
/* XXX These defines are likely incomplete, but should be easy to fix.
They should be complete enough to build extension modules. */
#define COMPILER "[lcc-win32]"
typedef int pid_t;
/* __declspec() is supported here too - do nothing to get the defaults */
#endif /* LCC */
/* ------------------------------------------------------------------------*/
/* End of compilers - finish up */
#ifndef NO_STDIO_H
# include <stdio.h>
#endif
/* 64 bit ints are usually spelt __int64 unless compiler has overridden */
#ifndef PY_LONG_LONG
# define PY_LONG_LONG __int64
# define PY_LLONG_MAX _I64_MAX
# define PY_LLONG_MIN _I64_MIN
# define PY_ULLONG_MAX _UI64_MAX
#endif
/* For Windows the Python core is in a DLL by default. Test
Py_NO_ENABLE_SHARED to find out. Also support MS_NO_COREDLL for b/w compat */
#if !defined(MS_NO_COREDLL) && !defined(Py_NO_ENABLE_SHARED)
# define Py_ENABLE_SHARED 1 /* standard symbol for shared library */
# define MS_COREDLL /* deprecated old symbol */
#endif /* !MS_NO_COREDLL && ... */
/* All windows compilers that use this header support __declspec */
#define HAVE_DECLSPEC_DLL
/* For an MSVC DLL, we can nominate the .lib files used by extensions */
#ifdef MS_COREDLL
# if !defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_BUILTIN)
/* not building the core - must be an ext */
# if defined(_MSC_VER)
/* So MSVC users need not specify the .lib
file in their Makefile (other compilers are
generally taken care of by distutils.) */
# if defined(_DEBUG)
# pragma comment(lib,"python311_d.lib")
# elif defined(Py_LIMITED_API)
# pragma comment(lib,"python3.lib")
# else
# pragma comment(lib,"python311.lib")
# endif /* _DEBUG */
# endif /* _MSC_VER */
# endif /* Py_BUILD_CORE */
#endif /* MS_COREDLL */
#if defined(MS_WIN64)
/* maintain "win32" sys.platform for backward compatibility of Python code,
the Win64 API should be close enough to the Win32 API to make this
preferable */
# define PLATFORM "win32"
# define SIZEOF_VOID_P 8
# define SIZEOF_TIME_T 8
# define SIZEOF_OFF_T 4
# define SIZEOF_FPOS_T 8
# define SIZEOF_HKEY 8
# define SIZEOF_SIZE_T 8
# define ALIGNOF_SIZE_T 8
/* configure.ac defines HAVE_LARGEFILE_SUPPORT iff
sizeof(off_t) > sizeof(long), and sizeof(long long) >= sizeof(off_t).
On Win64 the second condition is not true, but if fpos_t replaces off_t
then this is true. The uses of HAVE_LARGEFILE_SUPPORT imply that Win64
should define this. */
# define HAVE_LARGEFILE_SUPPORT
#elif defined(MS_WIN32)
# define PLATFORM "win32"
# define HAVE_LARGEFILE_SUPPORT
# define SIZEOF_VOID_P 4
# define SIZEOF_OFF_T 4
# define SIZEOF_FPOS_T 8
# define SIZEOF_HKEY 4
# define SIZEOF_SIZE_T 4
# define ALIGNOF_SIZE_T 4
/* MS VS2005 changes time_t to a 64-bit type on all platforms */
# if defined(_MSC_VER) && _MSC_VER >= 1400
# define SIZEOF_TIME_T 8
# else
# define SIZEOF_TIME_T 4
# endif
#endif
#ifdef _DEBUG
# define Py_DEBUG
#endif
#ifdef MS_WIN32
#define SIZEOF_SHORT 2
#define SIZEOF_INT 4
#define SIZEOF_LONG 4
#define ALIGNOF_LONG 4
#define SIZEOF_LONG_LONG 8
#define SIZEOF_DOUBLE 8
#define SIZEOF_FLOAT 4
/* VC 7.1 has them and VC 6.0 does not. VC 6.0 has a version number of 1200.
Microsoft eMbedded Visual C++ 4.0 has a version number of 1201 and doesn't
define these.
If some compiler does not provide them, modify the #if appropriately. */
#if defined(_MSC_VER)
#if _MSC_VER > 1300
#define HAVE_UINTPTR_T 1
#define HAVE_INTPTR_T 1
#else
/* VC6, VS 2002 and eVC4 don't support the C99 LL suffix for 64-bit integer literals */
#define Py_LL(x) x##I64
#endif /* _MSC_VER > 1300 */
#endif /* _MSC_VER */
#endif
/* define signed and unsigned exact-width 32-bit and 64-bit types, used in the
implementation of Python integers. */
#define PY_UINT32_T uint32_t
#define PY_UINT64_T uint64_t
#define PY_INT32_T int32_t
#define PY_INT64_T int64_t
/* Fairly standard from here! */
/* Define if on AIX 3.
System headers sometimes define this.
We just want to avoid a redefinition error message. */
#ifndef _ALL_SOURCE
/* #undef _ALL_SOURCE */
#endif
/* Define to empty if the keyword does not work. */
/* #define const */
/* Define to 1 if you have the <conio.h> header file. */
#define HAVE_CONIO_H 1
/* Define to 1 if you have the <direct.h> header file. */
#define HAVE_DIRECT_H 1
/* Define to 1 if you have the declaration of `tzname', and to 0 if you don't.
*/
#define HAVE_DECL_TZNAME 1
/* Define if you have dirent.h. */
/* #define DIRENT 1 */
/* Define to the type of elements in the array set by `getgroups'.
Usually this is either `int' or `gid_t'. */
/* #undef GETGROUPS_T */
/* Define to `int' if <sys/types.h> doesn't define. */
/* #undef gid_t */
/* Define if your struct tm has tm_zone. */
/* #undef HAVE_TM_ZONE */
/* Define if you don't have tm_zone but do have the external array
tzname. */
#define HAVE_TZNAME
/* Define to `int' if <sys/types.h> doesn't define. */
/* #undef mode_t */
/* Define if you don't have dirent.h, but have ndir.h. */
/* #undef NDIR */
/* Define to `long' if <sys/types.h> doesn't define. */
/* #undef off_t */
/* Define to `int' if <sys/types.h> doesn't define. */
/* #undef pid_t */
/* Define if the system does not provide POSIX.1 features except
with this defined. */
/* #undef _POSIX_1_SOURCE */
/* Define if you need to in order for stat and other things to work. */
/* #undef _POSIX_SOURCE */
/* Define as the return type of signal handlers (int or void). */
#define RETSIGTYPE void
/* Define to `unsigned' if <sys/types.h> doesn't define. */
/* #undef size_t */
/* Define if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define if you don't have dirent.h, but have sys/dir.h. */
/* #undef SYSDIR */
/* Define if you don't have dirent.h, but have sys/ndir.h. */
/* #undef SYSNDIR */
/* Define if you can safely include both <sys/time.h> and <time.h>. */
/* #undef TIME_WITH_SYS_TIME */
/* Define if your <sys/time.h> declares struct tm. */
/* #define TM_IN_SYS_TIME 1 */
/* Define to `int' if <sys/types.h> doesn't define. */
/* #undef uid_t */
/* Define if the closedir function returns void instead of int. */
/* #undef VOID_CLOSEDIR */
/* Define if getpgrp() must be called as getpgrp(0)
and (consequently) setpgrp() as setpgrp(0, 0). */
/* #undef GETPGRP_HAVE_ARGS */
/* Define this if your time.h defines altzone */
/* #define HAVE_ALTZONE */
/* Define if you have the putenv function. */
#define HAVE_PUTENV
/* Define if your compiler supports function prototypes */
#define HAVE_PROTOTYPES
/* Define if you can safely include both <sys/select.h> and <sys/time.h>
(which you can't on SCO ODT 3.0). */
/* #undef SYS_SELECT_WITH_SYS_TIME */
/* Define if you want build the _decimal module using a coroutine-local rather
than a thread-local context */
#define WITH_DECIMAL_CONTEXTVAR 1
/* Define if you want documentation strings in extension modules */
#define WITH_DOC_STRINGS 1
/* Define if you want to compile in rudimentary thread support */
/* #undef WITH_THREAD */
/* Define if you want to use the GNU readline library */
/* #define WITH_READLINE 1 */
/* Use Python's own small-block memory-allocator. */
#define WITH_PYMALLOC 1
/* Define if you want to compile in object freelists optimization */
#define WITH_FREELISTS 1
/* Define if you have clock. */
/* #define HAVE_CLOCK */
/* Define when any dynamic module loading is enabled */
#define HAVE_DYNAMIC_LOADING
/* Define if you have ftime. */
#define HAVE_FTIME
/* Define if you have getpeername. */
#define HAVE_GETPEERNAME
/* Define if you have getpgrp. */
/* #undef HAVE_GETPGRP */
/* Define if you have getpid. */
#define HAVE_GETPID
/* Define if you have gettimeofday. */
/* #undef HAVE_GETTIMEOFDAY */
/* Define if you have getwd. */
/* #undef HAVE_GETWD */
/* Define if you have lstat. */
/* #undef HAVE_LSTAT */
/* Define if you have the mktime function. */
#define HAVE_MKTIME
/* Define if you have nice. */
/* #undef HAVE_NICE */
/* Define if you have readlink. */
/* #undef HAVE_READLINK */
/* Define if you have setpgid. */
/* #undef HAVE_SETPGID */
/* Define if you have setpgrp. */
/* #undef HAVE_SETPGRP */
/* Define if you have setsid. */
/* #undef HAVE_SETSID */
/* Define if you have setvbuf. */
#define HAVE_SETVBUF
/* Define if you have siginterrupt. */
/* #undef HAVE_SIGINTERRUPT */
/* Define to 1 if you have the `shutdown' function. */
#define HAVE_SHUTDOWN 1
/* Define if you have symlink. */
/* #undef HAVE_SYMLINK */
/* Define if you have tcgetpgrp. */
/* #undef HAVE_TCGETPGRP */
/* Define if you have tcsetpgrp. */
/* #undef HAVE_TCSETPGRP */
/* Define if you have times. */
/* #undef HAVE_TIMES */
/* Define to 1 if you have the `umask' function. */
#define HAVE_UMASK 1
/* Define if you have uname. */
/* #undef HAVE_UNAME */
/* Define if you have waitpid. */
/* #undef HAVE_WAITPID */
/* Define to 1 if you have the `wcsftime' function. */
#if defined(_MSC_VER) && _MSC_VER >= 1310
#define HAVE_WCSFTIME 1
#endif
/* Define to 1 if you have the `wcscoll' function. */
#define HAVE_WCSCOLL 1
/* Define to 1 if you have the `wcsxfrm' function. */
#define HAVE_WCSXFRM 1
/* Define if the zlib library has inflateCopy */
#define HAVE_ZLIB_COPY 1
/* Define if you have the <dlfcn.h> header file. */
/* #undef HAVE_DLFCN_H */
/* Define to 1 if you have the <errno.h> header file. */
#define HAVE_ERRNO_H 1
/* Define if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
/* Define to 1 if you have the <process.h> header file. */
#define HAVE_PROCESS_H 1
/* Define to 1 if you have the <signal.h> header file. */
#define HAVE_SIGNAL_H 1
/* Define if you have the <stdarg.h> prototypes. */
#define HAVE_STDARG_PROTOTYPES
/* Define if you have the <stddef.h> header file. */
#define HAVE_STDDEF_H 1
/* Define if you have the <sys/audioio.h> header file. */
/* #undef HAVE_SYS_AUDIOIO_H */
/* Define if you have the <sys/param.h> header file. */
/* #define HAVE_SYS_PARAM_H 1 */
/* Define if you have the <sys/select.h> header file. */
/* #define HAVE_SYS_SELECT_H 1 */
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define if you have the <sys/time.h> header file. */
/* #define HAVE_SYS_TIME_H 1 */
/* Define if you have the <sys/times.h> header file. */
/* #define HAVE_SYS_TIMES_H 1 */
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define if you have the <sys/un.h> header file. */
/* #define HAVE_SYS_UN_H 1 */
/* Define if you have the <sys/utime.h> header file. */
/* #define HAVE_SYS_UTIME_H 1 */
/* Define if you have the <sys/utsname.h> header file. */
/* #define HAVE_SYS_UTSNAME_H 1 */
/* Define if you have the <unistd.h> header file. */
/* #define HAVE_UNISTD_H 1 */
/* Define if you have the <utime.h> header file. */
/* #define HAVE_UTIME_H 1 */
/* Define if the compiler provides a wchar.h header file. */
#define HAVE_WCHAR_H 1
/* The size of `wchar_t', as computed by sizeof. */
#define SIZEOF_WCHAR_T 2
/* The size of `_Bool', as computed by sizeof. */
#define SIZEOF__BOOL 1
/* The size of `pid_t', as computed by sizeof. */
#define SIZEOF_PID_T SIZEOF_INT
/* Define if you have the dl library (-ldl). */
/* #undef HAVE_LIBDL */
/* Define if you have the mpc library (-lmpc). */
/* #undef HAVE_LIBMPC */
/* Define if you have the nsl library (-lnsl). */
#define HAVE_LIBNSL 1
/* Define if you have the seq library (-lseq). */
/* #undef HAVE_LIBSEQ */
/* Define if you have the socket library (-lsocket). */
#define HAVE_LIBSOCKET 1
/* Define if you have the sun library (-lsun). */
/* #undef HAVE_LIBSUN */
/* Define if you have the termcap library (-ltermcap). */
/* #undef HAVE_LIBTERMCAP */
/* Define if you have the termlib library (-ltermlib). */
/* #undef HAVE_LIBTERMLIB */
/* Define if you have the thread library (-lthread). */
/* #undef HAVE_LIBTHREAD */
/* WinSock does not use a bitmask in select, and uses
socket handles greater than FD_SETSIZE */
#define Py_SOCKET_FD_CAN_BE_GE_FD_SETSIZE
/* Define if C doubles are 64-bit IEEE 754 binary format, stored with the
least significant byte first */
#define DOUBLE_IS_LITTLE_ENDIAN_IEEE754 1
/* Define to 1 if you have the `erf' function. */
#define HAVE_ERF 1
/* Define to 1 if you have the `erfc' function. */
#define HAVE_ERFC 1
// netdb.h functions (provided by winsock.h)
#define HAVE_GETHOSTNAME 1
#define HAVE_GETHOSTBYADDR 1
#define HAVE_GETHOSTBYNAME 1
#define HAVE_GETPROTOBYNAME 1
#define HAVE_GETSERVBYNAME 1
#define HAVE_GETSERVBYPORT 1
// sys/socket.h functions (provided by winsock.h)
#define HAVE_INET_PTON 1
#define HAVE_INET_NTOA 1
#define HAVE_ACCEPT 1
#define HAVE_BIND 1
#define HAVE_CONNECT 1
#define HAVE_GETSOCKNAME 1
#define HAVE_LISTEN 1
#define HAVE_RECVFROM 1
#define HAVE_SENDTO 1
#define HAVE_SETSOCKOPT 1
#define HAVE_SOCKET 1
/* Define to 1 if you have the `dup' function. */
#define HAVE_DUP 1
/* framework name */
#define _PYTHONFRAMEWORK ""
/* Define if libssl has X509_VERIFY_PARAM_set1_host and related function */
#define HAVE_X509_VERIFY_PARAM_SET1_HOST 1
#endif /* !Py_CONFIG_H */

View File

@ -15,10 +15,26 @@ std::wstring executable_path()
}
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

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.32126.315
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mcrogueface_msvc", "mcrogueface_msvc.vcxproj", "{C00F9FB3-0806-41E8-9A10-29A325022EE1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C00F9FB3-0806-41E8-9A10-29A325022EE1}.Debug|x64.ActiveCfg = Debug|x64
{C00F9FB3-0806-41E8-9A10-29A325022EE1}.Debug|x64.Build.0 = Debug|x64
{C00F9FB3-0806-41E8-9A10-29A325022EE1}.Debug|x86.ActiveCfg = Debug|Win32
{C00F9FB3-0806-41E8-9A10-29A325022EE1}.Debug|x86.Build.0 = Debug|Win32
{C00F9FB3-0806-41E8-9A10-29A325022EE1}.Release|x64.ActiveCfg = Release|x64
{C00F9FB3-0806-41E8-9A10-29A325022EE1}.Release|x64.Build.0 = Release|x64
{C00F9FB3-0806-41E8-9A10-29A325022EE1}.Release|x86.ActiveCfg = Release|Win32
{C00F9FB3-0806-41E8-9A10-29A325022EE1}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1EE0CDF9-5331-47E9-828B-7AAA768F56EC}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,182 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\src\Animation.cpp" />
<ClCompile Include="..\..\..\src\Button.cpp" />
<ClCompile Include="..\..\..\src\Entity.cpp" />
<ClCompile Include="..\..\..\src\EntityManager.cpp" />
<ClCompile Include="..\..\..\src\GameEngine.cpp" />
<ClCompile Include="..\..\..\src\Grid.cpp" />
<ClCompile Include="..\..\..\src\IndexSprite.cpp" />
<ClCompile Include="..\..\..\src\IndexTexture.cpp" />
<ClCompile Include="..\..\..\src\main.cpp" />
<ClCompile Include="..\..\..\src\McRFPy_API.cpp" />
<ClCompile Include="..\..\..\src\MenuScene.cpp" />
<ClCompile Include="..\..\..\src\PythonScene.cpp" />
<ClCompile Include="..\..\..\src\Scene.cpp" />
<ClCompile Include="..\..\..\src\UIMenu.cpp" />
<ClCompile Include="..\..\..\src\UITestScene.cpp" />
<ClCompile Include="..\..\..\src\VectorShape.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\src\ActionCode.h" />
<ClInclude Include="..\..\..\src\Common.h" />
<ClInclude Include="..\..\..\src\Components.h" />
<ClInclude Include="..\platform.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{c00f9fb3-0806-41e8-9a10-29a325022ee1}</ProjectGuid>
<RootNamespace>mcroguefacemsvc</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)..\bin\windows\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\.msvc-intermediate\$(Configuration)\</IntDir>
<TargetName>combined_poc</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)..\..\..\bin\windows\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\.msvc-intermediate\$(Configuration)\</IntDir>
<TargetName>mcrogueface</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>C:\Users\jpm68\Share\McRogueFace\deps\SFML-2.5.1\include;C:\Users\jpm68\Share\McRogueFace\deps\posix-stub;C:\Users\jpm68\Share\McRogueFace\deps\Python-3.11.1\Include;C:\Users\jpm68\Share\McRogueFace\deps\Python-3.11.1\PC;C:\Users\jpm68\Share\McRogueFace\deps\libtcod-1.23.1-x86_64-msvc\include;C:\Users\jpm68\Share\McRogueFace\platform\windows;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>C:\Users\jpm68\Share\McRogueFace\deps\SFML-2.5.1\include;C:\Users\jpm68\Share\McRogueFace\deps\posix-stub;C:\Users\jpm68\Share\McRogueFace\deps\Python-3.11.1\Include;C:\Users\jpm68\Share\McRogueFace\deps\Python-3.11.1\PC;C:\Users\jpm68\Share\McRogueFace\deps\libtcod-1.23.1-x86_64-msvc\include;C:\Users\jpm68\Share\McRogueFace\platform\windows;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>C:\Users\jpm68\Share\McRogueFace\deps\SFML-2.5.1\include;C:\Users\jpm68\Share\McRogueFace\deps\posix-stub;C:\Users\jpm68\Share\McRogueFace\deps\Python-3.11.1\Include;C:\Users\jpm68\Share\McRogueFace\deps\Python-3.11.1\PC;C:\Users\jpm68\Share\McRogueFace\deps\libtcod-1.23.1-x86_64-msvc\include;C:\Users\jpm68\Share\McRogueFace\platform\windows;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>C:\Users\jpm68\Share\McRogueFace\deps_windows\posix-stub;C:\Users\jpm68\Share\McRogueFace\platform\windows;C:\Users\jpm68\Share\McRogueFace\deps_windows\SFML-2.5.1\include;C:\Users\jpm68\Share\McRogueFace\deps_windows\Python-3.11.1\Include;C:\Users\jpm68\Share\McRogueFace\deps_windows\Python-3.11.1\PC;C:\Users\jpm68\Share\McRogueFace\deps_windows\libtcod-1.23.1-x86_64-msvc\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>C:\Users\jpm68\Share\McRogueFace\deps_windows\Python-3.11.1\PCbuild\amd64;C:\Users\jpm68\Share\McRogueFace\deps_windows\libtcod-1.23.1-x86_64-msvc;C:\Users\jpm68\Share\McRogueFace\deps_windows\SFML-2.5.1\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>libtcod.lib;sfml-audio.lib;sfml-graphics.lib;sfml-window.lib;sfml-system.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\src\main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\Button.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\Entity.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\EntityManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\GameEngine.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\MenuScene.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\Scene.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\UIMenu.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\UITestScene.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\VectorShape.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\McRFPy_API.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\PythonScene.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\IndexSprite.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\IndexTexture.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\Grid.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\Animation.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\src\ActionCode.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\Common.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\Components.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\platform.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

View File

@ -12,10 +12,28 @@ std::wstring executable_path()
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

@ -0,0 +1,28 @@
Required project settings to build in Visual Studio 19:
C/C++ > General
Additional Include Directories
\McRogueFace\platform\windows
\McRogueFace\deps_windows\libtcod-1.23.1-x86_64-msvc\include
\McRogueFace\deps_windows\Python-3.11.1\PC
\McRogueFace\deps_windows\Python-3.11.1\Include
\McRogueFace\deps_windows\posix-stub
\McRogueFace\deps_windows\SFML-2.5.1\include
C/C++ > Preprocessor
Preprocessor Definitions
_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS
Linker > General
Additional Library Directories
\McRogueFace\deps_windows\SFML-2.5.1\lib
\McRogueFace\deps_windows\libtcod-1.23.1-x86_64-msvc
\McRogueFace\deps_windows\Python-3.11.1\PCbuild\amd64
Linker > Input
Additional Dependencies
libtcod.lib
sfml-audio.lib
sfml-graphics.lib
sfml-window.lib
sfml-system.lib

View File

@ -1,3 +1,4 @@
#!/bin/bash
cp src/scripts/*.py bin/linux/scripts/
cd bin/linux
./poc
./mcrogueface

34
src/ActionCode.h Normal file
View File

@ -0,0 +1,34 @@
#include <SFML/Window/Keyboard.hpp>
class ActionCode
{
public:
enum CodeType { Key = 0, Mousebutton, Mousewheel };
const static int KEY = 4096;
const static int MOUSEBUTTON = 8192;
const static int MOUSEWHEEL = 16384;
const static int WHEEL_NUM = 4;
const static int WHEEL_NEG = 2;
const static int WHEEL_DEL = 1;
static int keycode(sf::Keyboard::Key& k) { return KEY + (int)k; }
static int keycode(sf::Mouse::Button& b) { return MOUSEBUTTON + (int)b; }
//static int keycode(sf::Mouse::Wheel& w, float d) { return MOUSEWHEEL + (((int)w)<<12) + int(d*16) + 512; }
static int keycode(sf::Mouse::Wheel& w, float d) {
int neg = 0;
if (d < 0) { neg = 1; }
return MOUSEWHEEL + (w * WHEEL_NUM) + (neg * WHEEL_NEG) + 1;
}
static int key(int a) { return a & KEY; }
static int mouseButton(int a) { return a & MOUSEBUTTON; }
static bool isMouseWheel(int a) { return a & MOUSEWHEEL; }
//static int wheel(int a) { return a || MOUSEWHEEL >> 12; }
static int wheel(int a) { return (a & WHEEL_NUM) / WHEEL_NUM; }
//static float delta(int a) { return (a || MOUSEWHEEL || 2047) / 16.0f - 512; }
static int delta(int a) {
int factor = 1;
if (a & WHEEL_NEG) factor = -1;
return (a & WHEEL_DEL) * factor;
}
};

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:
};

16
src/Common.h Normal file
View File

@ -0,0 +1,16 @@
# pragma once
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <vector>
#include <iostream>
#include <memory>
#include <fstream>
#include <sstream>
#include <algorithm>
// wstring<->string conversion
#include <locale>
#include <codecvt>
#include <filesystem>

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);
};

117
src/GameEngine.cpp Normal file
View File

@ -0,0 +1,117 @@
#include "GameEngine.h"
#include "MenuScene.h"
//#include "UITestScene.h"
#include "ActionCode.h"
#include "McRFPy_API.h"
#include "PythonScene.h"
#include "UITestScene.h"
#include "Resources.h"
GameEngine::GameEngine()
{
Resources::font.loadFromFile("./assets/JetbrainsMono.ttf");
Resources::game = this;
window.create(sf::VideoMode(1024, 768), "McRogueFace - r/RoguelikeDev Tutorial Run");
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::executePyString("from UIMenu import *");
//McRFPy_API::executePyString("from Grid import *");
//scenes["py"] = new PythonScene(this, "TestScene");
IndexSprite::game = this;
clock.restart();
}
Scene* GameEngine::currentScene() { return scenes[scene]; }
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::run()
{
clock.restart();
while (running)
{
currentScene()->update();
sUserInput();
if (!paused)
{
}
currentScene()->sRender();
currentFrame++;
frameTime = clock.restart().asSeconds();
}
}
void GameEngine::sUserInput()
{
sf::Event event;
while (window.pollEvent(event))
{
std::string actionType;
int actionCode = 0;
if (event.type == sf::Event::Closed) { running = false; continue; }
else if (event.type == sf::Event::Resized) {
sf::FloatRect area(0.f, 0.f, event.size.width, event.size.height);
visible = sf::View(area);
window.setView(visible);
//std::cout << "Visible area set to (0, 0, " << event.size.width << ", " << event.size.height <<")"<<std::endl;
actionType = "resize";
}
else if (event.type == sf::Event::KeyPressed || event.type == sf::Event::MouseButtonPressed || event.type == sf::Event::MouseWheelScrolled) actionType = "start";
else if (event.type == sf::Event::KeyReleased || event.type == sf::Event::MouseButtonReleased) actionType = "end";
if (event.type == sf::Event::MouseButtonPressed || event.type == sf::Event::MouseButtonReleased)
actionCode = ActionCode::keycode(event.mouseButton.button);
else if (event.type == sf::Event::KeyPressed || event.type == sf::Event::KeyReleased)
actionCode = ActionCode::keycode(event.key.code);
else if (event.type == sf::Event::MouseWheelScrolled)
{
// //sf::Mouse::Wheel w = event.MouseWheelScrollEvent.wheel;
if (event.mouseWheelScroll.wheel == sf::Mouse::VerticalWheel)
{
int delta = 1;
if (event.mouseWheelScroll.delta < 0) delta = -1;
actionCode = ActionCode::keycode(event.mouseWheelScroll.wheel, delta );
/*
std::cout << "[GameEngine] Generated MouseWheel code w(" << (int)event.mouseWheelScroll.wheel << ") d(" << event.mouseWheelScroll.delta << ") D(" << delta << ") = " << actionCode << std::endl;
std::cout << " test decode: isMouseWheel=" << ActionCode::isMouseWheel(actionCode) << ", wheel=" << ActionCode::wheel(actionCode) << ", delta=" << ActionCode::delta(actionCode) << std::endl;
std::cout << " math test: actionCode && WHEEL_NEG -> " << (actionCode && ActionCode::WHEEL_NEG) << "; actionCode && WHEEL_DEL -> " << (actionCode && ActionCode::WHEEL_DEL) << ";" << std::endl;
*/
}
// float d = event.MouseWheelScrollEvent.delta;
// actionCode = ActionCode::keycode(0, d);
}
else
continue;
//std::cout << "Event produced action code " << actionCode << ": " << actionType << std::endl;
if (currentScene()->hasAction(actionCode))
{
std::string name = currentScene()->action(actionCode);
currentScene()->doAction(name, actionType);
}
else
{
//std::cout << "[GameEngine] Action not registered for input: " << actionCode << ": " << actionType << std::endl;
}
}
}

44
src/GameEngine.h Normal file
View File

@ -0,0 +1,44 @@
#pragma once
#include "Common.h"
#include "Entity.h"
#include "EntityManager.h"
#include "Scene.h"
#include "McRFPy_API.h"
#include "IndexTexture.h"
class GameEngine
{
sf::RenderWindow window;
sf::Font font;
std::string scene;
std::map<std::string, Scene*> scenes;
bool running = true;
bool paused = false;
int currentFrame = 0;
sf::View visible;
sf::Clock clock;
float frameTime;
public:
GameEngine();
Scene* currentScene();
void changeScene(std::string);
void quit();
void setPause(bool);
sf::Font & getFont();
sf::RenderWindow & getWindow();
void run();
void sUserInput();
int getFrame() { return currentFrame; }
float getFrameTime() { return frameTime; }
sf::View getView() { return visible; }
// global textures for scripts to access
std::vector<IndexTexture> textures;
// global audio storage
std::vector<sf::SoundBuffer> sfxbuffers;
sf::Music music;
sf::Sound sfx;
};

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);
};

11
src/IndexTexture.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "IndexTexture.h"
sf::IntRect IndexTexture::spriteCoordinates(int index) {
int tx = index % grid_width, ty = index / grid_width;
return sf::IntRect(tx * grid_size, ty * grid_size, grid_size, grid_size);
}
IndexTexture::IndexTexture (sf::Texture t, int gs, int gw, int gh):
grid_size(gs), grid_width(gw), grid_height(gh) {
texture = t;
}

12
src/IndexTexture.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include "Common.h"
class GameEngine; // forward declare
class IndexTexture {
public:
sf::Texture texture;
int grid_size, grid_width, grid_height;
static GameEngine* game;
sf::IntRect spriteCoordinates(int);
IndexTexture(sf::Texture, int, int, int);
};

1216
src/McRFPy_API.cpp Normal file

File diff suppressed because it is too large Load Diff

160
src/McRFPy_API.h Normal file
View File

@ -0,0 +1,160 @@
#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>
// implementation required to link templates
#include "Animation.h"
class GameEngine; // forward declared (circular members)
class McRFPy_API
{
private:
static const int texture_size = 16, // w & h (pixels) of one sprite in the tex
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:
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 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*);
static PyObject* _createSoundBuffer(PyObject*, PyObject*);
static PyObject* _loadMusic(PyObject*, PyObject*);
static PyObject* _setMusicVolume(PyObject*, PyObject*);
static PyObject* _setSoundVolume(PyObject*, PyObject*);
static PyObject* _playSound(PyObject*, PyObject*);
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*);
// 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();
}

18
src/MenuScene.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include "Common.h"
#include "Scene.h"
#include "GameEngine.h"
class MenuScene: public Scene
{
sf::Text text;
sf::Text text2;
sf::Text text3;
public:
MenuScene(GameEngine*);
void update() override final;
void doAction(std::string, std::string) override final;
void sRender() override final;
};

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;
};

6
src/Resources.cpp Normal file
View File

@ -0,0 +1,6 @@
#include "Resources.h"
// Resources class members memory allocation
sf::Font Resources::font;
GameEngine* Resources::game;

10
src/Resources.h Normal file
View File

@ -0,0 +1,10 @@
#include "Common.h"
class GameEngine; // forward declared
class Resources
{
public:
static sf::Font font;
static GameEngine* game;
};

36
src/Scene.cpp Normal file
View File

@ -0,0 +1,36 @@
#include "Scene.h"
//Scene::Scene() { game = 0; std::cout << "WARN: default Scene constructor called. (game = " << game << ")" << std::endl;};
Scene::Scene(GameEngine* g) { game = g; }
void Scene::registerAction(int code, std::string name)
{
actions[code] = name;
actionState[name] = false;
}
bool Scene::hasAction(std::string name)
{
for (auto& item : actions)
if (item.second == name) return true;
return false;
}
bool Scene::hasAction(int code)
{
return (actions.find(code) != actions.end());
}
std::string Scene::action(int code)
{
return actions[code];
}
bool Scene::registerActionInjected(int code, std::string name)
{
std::cout << "Inject registered action - default implementation\n";
return false;
}
bool Scene::unregisterActionInjected(int code, std::string name)
{
return false;
}

40
src/Scene.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
// macros for scene input
#define ACTION(X, Y) (name.compare(X) == 0 && type.compare(Y) == 0)
#define ACTIONONCE(X) ((name.compare(X) == 0 && type.compare("start") == 0 && !actionState[name]))
#define ACTIONAFTER(X) ((name.compare(X) == 0 && type.compare("end") == 0))
#define ACTIONPY ((name.size() > 3 && name.compare(name.size() - 3, 3, "_py") == 0))
#include "Common.h"
//#include "GameEngine.h"
class GameEngine; // forward declare
class Scene
{
protected:
bool hasEnded = false;
bool paused = false;
std::map<int, std::string> actions;
std::map<std::string, bool> actionState;
GameEngine* game;
void simulate(int);
void registerAction(int, std::string);
public:
//Scene();
Scene(GameEngine*);
virtual void update() = 0;
virtual void sRender() = 0;
virtual void doAction(std::string, std::string) = 0;
bool hasAction(std::string);
bool hasAction(int);
std::string action(int);
virtual bool registerActionInjected(int, std::string);
virtual bool unregisterActionInjected(int, std::string);
};

35
src/UI.cpp Normal file
View File

@ -0,0 +1,35 @@
#include "UI.h"
#include "Resources.h"
#include "GameEngine.h"
void UIDrawable::render()
{
//std::cout << "Rendering base UIDrawable\n";
render(sf::Vector2f());
}
void UIFrame::render(sf::Vector2f offset)
{
//std::cout << "Rendering UIFrame w/ offset\n";
box.move(offset);
Resources::game->getWindow().draw(box);
box.move(-offset);
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);
}
void UISprite::render(sf::Vector2f offset)
{
sprite.move(offset);
Resources::game->getWindow().draw(sprite);
sprite.move(-offset);
}

236
src/UI.h Normal file
View File

@ -0,0 +1,236 @@
#include "Common.h"
#include "Python.h"
#include "structmember.h"
class UIDrawable
{
public:
UIDrawable* parent;
void render();
virtual void render(sf::Vector2f) = 0;
//virtual sf::Rect<int> aabb(); // not sure I care about this yet
//virtual sf::Vector2i position();
bool handle_event(/* ??? click, scroll, keystroke*/);
std::string action;
};
class UIFrame: public UIDrawable
{
public:
sf::RectangleShape box;
std::vector<UIDrawable*> children;
void render(sf::Vector2f) override final;
};
class UICaption: public UIDrawable
{
public:
sf::Text text;
void render(sf::Vector2f) override final;
};
class UISprite: public UIDrawable
{
public:
void render(sf::Vector2f) override final;
int texture_index, sprite_index;
float scale;
sf::Sprite sprite;
};
namespace mcrfpydef {
static PyObject* PyUIFrame_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
std::cout << "New called\n";
UIFrame* self;
self = (UIFrame*)type->tp_alloc(type, 0);
if (self != nullptr)
{
}
return (PyObject*)self;
}
static int PyUIFrame_init(UIFrame* self, PyObject* args, PyObject* kwds)
{
std::cout << "Init called\n";
static const char* keywords[] = { "x", "y", nullptr }; // TODO: keywords and other python objects to configure box (sf::RectangleShape)
float x = 0.0f, y = 0.0f;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ff", const_cast<char**>(keywords), &x, &y))
{
return -1;
}
//self->x = x;
//self->y = y;
return 0;
}
static PyObject* PyUIFrame_repr(UIFrame* self)
{
std::ostringstream ss;
ss << "<UIFrame ()>";
std::string repr_str = ss.str();
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
}
static PyObject* PyUIFrame_setSize(UIFrame* self, PyObject* args)
{
float w, h;
if (!PyArg_ParseTuple(args, "ff", &w, &h)) return (PyObject*)-1;
self->box.setSize(sf::Vector2f(w, h));
Py_INCREF(Py_None);
//return PyFloat_FromDouble(mag);
return Py_None;
}
static PyMethodDef PyUIFrame_methods[] = {
{"set_size", (PyCFunction)PyUIFrame_setSize, METH_VARARGS,
"Set the width and height of the UIFrame's visible box"},
{NULL, NULL, 0, NULL}
};
/*
static PyMemberDef PyUIFrame_members[] = {
{"box", T_OBJECT, offsetof(UIFrame, box), 0},
{NULL}
};
*/
static PyTypeObject PyUIFrameType = {
//PyVarObject_HEAD_INIT(NULL, 0)
//.tp_base = NULL,
.tp_name = "mcrfpy.UIFrame",
.tp_basicsize = sizeof(UIFrame),
.tp_itemsize = 0,
.tp_dealloc = [](PyObject* obj) { Py_TYPE(obj)->tp_free(obj); },
//.tp_repr = (reprfunc)PyUIFrame_repr,
//.tp_hash = NULL,
//.tp_iter
//.tp_iternext
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = PyDoc_STR("Custom UIFrame object"),
//.tp_methods = PyUIFrame_methods,
//.tp_members = PyUIFrame_members,
.tp_init = (initproc)PyUIFrame_init,
.tp_new = PyUIFrame_new, //PyType_GenericNew ?
};
// module
/*
static PyModuleDef ui_module = {
PyModuleDef_HEAD_INIT,
.m_name = "ui",
.m_size = -1,
};
PyMODINIT_FUNC PyInit_my_module(void) {
PyObject* module = PyModule_Create(&my_module);
if (module == NULL) {
return NULL;
}
if (PyType_Ready(&MyType_Type) < 0) {
return NULL;
}
Py_INCREF(&MyType_Type);
PyModule_AddObject(module, "UIFrame", (PyObject*)&MyType_Type);
return module;
}
*/
// Point class example
class Point
{
public:
float x, y;
float magnitude() {
return std::sqrt(x*x + y*y);
}
};
static PyObject* PyPoint_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
Point* self;
self = (Point*)type->tp_alloc(type, 0);
if (self != nullptr)
{
self->x = 0.0f;
self->y = 0.0f;
}
return (PyObject*)self;
}
// Method to initialize the Point object
static int PyPoint_init(Point* self, PyObject* args, PyObject* kwds)
{
static const char* keywords[] = { "x", "y", nullptr };
float x = 0.0f, y = 0.0f;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ff", const_cast<char**>(keywords), &x, &y))
{
return -1;
}
self->x = x;
self->y = y;
return 0;
}
// Method to calculate the magnitude of the Point
static PyObject* PyPoint_magnitude(Point* self)
{
float mag = self->magnitude();
return PyFloat_FromDouble(mag);
}
static PyMethodDef PyPoint_methods[] = {
{"magnitude", (PyCFunction)PyPoint_magnitude, METH_NOARGS,
"Vector length, or distance from origin."},
{NULL, NULL, 0, NULL}
};
static PyMemberDef PyPoint_members[] = {
{"x", T_FLOAT, offsetof(Point, x), 0},
{"y", T_FLOAT, offsetof(Point, y), 0},
{NULL}
};
static PyTypeObject PyPointType = {
.tp_name = "mcrfpy.Point",
.tp_basicsize = sizeof(Point),
//.tp_itemsize = 0,
//.tp_dealloc = NULL,
//.tp_repr = NULL,
//.tp_hash = NULL,
//.tp_iter
//.tp_iternext
.tp_flags = Py_TPFLAGS_DEFAULT,
//.tp_doc = PyDoc_STR("Custom point object. (x, y)"),
//.tp_methods = PyPoint_methods,
//.tp_members = PyPoint_members,
//.tp_init = (initproc)PyPoint_init,
//.tp_new = PyPoint_new, //PyType_GenericNew ?
};
/*
static PyModuleDef PyPointModule = {
PyModuleDef_HEAD_INIT,
.m_name = "point",
.m_doc = "Custom point module.",
.m_size = -1,
//.m_methods = PyPoint_methods,
};
*/
}

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:
};

83
src/UITestScene.cpp Normal file
View File

@ -0,0 +1,83 @@
#include "UITestScene.h"
#include "ActionCode.h"
UITestScene::UITestScene(GameEngine* g) : Scene(g)
{
text.setFont(game->getFont());
text.setString("Test Scene for UI elements");
text.setCharacterSize(24);
//registerAction(ActionCode::KEY + sf::Keyboard::Space, "start_game");
//registerAction(ActionCode::KEY + sf::Keyboard::Up, "up");
//registerAction(ActionCode::KEY + sf::Keyboard::Down, "down");
// Create a UI element or three?
e1 = UIFrame();
e1.box.setPosition(100, 150);
e1.box.setSize(sf::Vector2f(400,400));
e1.box.setFillColor(sf::Color(255, 0, 0));
e1a = UIFrame();
e1a.box.setPosition(50, 50);
e1a.box.setSize(sf::Vector2f(200,200));
e1a.box.setFillColor(sf::Color(0, 255, 0));
e1.children.push_back(&e1a);
e1aa = UIFrame();
e1aa.box.setPosition(5, 5);
e1aa.box.setSize(sf::Vector2f(100,100));
e1aa.box.setFillColor(sf::Color(0, 0, 255));
e1a.children.push_back(&e1aa);
e2 = UICaption();
e2.text.setFont(game->getFont());
e2.text.setString("Hello World.");
//e2.text.setColor(sf::Color(255, 255, 255));
e2.text.setPosition(50, 250);
ui_elements.push_back(&e1);
ui_elements.push_back(&e2);
}
void UITestScene::update()
{
//std::cout << "MenuScene update" << std::endl;
}
void UITestScene::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 UITestScene::sRender()
{
game->getWindow().clear();
game->getWindow().draw(text);
// draw all UI elements
for (auto e: ui_elements)
{
//std::cout << "Rendering element\n";
e->render();
}
//e1.render(sf::Vector2f(0, 0));
//e1.render(sf::Vector2f(-100, -100));
game->getWindow().display();
McRFPy_API::REPL();
}

21
src/UITestScene.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "Common.h"
#include "Scene.h"
#include "GameEngine.h"
#include <list>
#include "UI.h"
class UITestScene: public Scene
{
sf::Text text;
UIFrame e1, e1a, e1aa;
UICaption e2;
std::vector<UIDrawable*> ui_elements;
public:
UITestScene(GameEngine*);
void update() override final;
void doAction(std::string, std::string) override final;
void sRender() override final;
};

View File

@ -1,292 +0,0 @@
// Python script engine includes
#include <Python.h>
#include <iostream>
#include <stdlib.h>
#include <vector>
// wstring<->string conversion
#include <locale>
#include <codecvt>
// SFML
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
sf::RenderWindow window;
sf::Font font;
sf::Text text;
// TCOD
#include <libtcod.hpp>
#include "platform.h"
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);
}
bool fexists(std::string filename)
{
return std::filesystem::exists(filename);
}
// init_python - configure interpreter details here
PyStatus init_python(const char *program_name)
{
std::cout << "called init_python" << std::endl;
PyStatus status;
//**preconfig to establish locale**
PyPreConfig preconfig;
PyPreConfig_InitIsolatedConfig(&preconfig);
preconfig.utf8_mode = 1;
status = Py_PreInitialize(&preconfig);
if (PyStatus_Exception(status)) {
Py_ExitStatusException(status);
}
PyConfig config;
PyConfig_InitIsolatedConfig(&config);
config.dev_mode = 1;
PyConfig_SetBytesString(&config, &config.home,
narrow_string(executable_path() + L"/Python311").c_str());
std::cout << "config.home: "; std::wcout << config.home << std::endl;
/* Set the program name before reading the configuration
(decode byte string from the locale encoding).
Implicitly preinitialize Python. */
// windows can't name the Python interpreter...?
status = PyConfig_SetBytesString(&config, &config.program_name,
program_name);
// under Windows, the search paths are correct; under Linux, they need manual insertion
#if __PLATFORM_SET_PYTHON_SEARCH_PATHS == 1
config.module_search_paths_set = 1;
// search paths for python libs/modules/scripts
const wchar_t* str_arr[] = {
L"/scripts",
L"/Python311/lib.linux-x86_64-3.11",
L"/Python311",
L"/Python311/Lib",
L"/venv/lib/python3.11/site-packages"
};
for(auto s : str_arr) {
status = PyWideStringList_Append(&config.module_search_paths, (executable_path() + s).c_str());
std::wcout << "`" << s
<< "` transformed to `" << (executable_path() + s).c_str()
<< "` and got status error (`" << PyStatus_IsError(status) << "`)" << std::endl;
if (PyStatus_Exception(status)) {
std::wcout << "Exception handling " << s << std::endl << std::flush;
break;
}
}
#endif
status = Py_InitializeFromConfig(&config);
std::cout << "Python Initialized" << std::endl;
done:
//PyConfig_Clear(&config);
//free(python_home_ptr);
return status;
}
// C++ example functionality
int recurse_fib(int i)
{
if (i <= 1) return 1;
return recurse_fib(i-1) + recurse_fib(i-2);
}
// Create a python module to expose C++ functionality
static PyObject* scriptable_fibonacci(PyObject *self, PyObject *args)
{
int x;
// get input (single integer) from args
if (!PyArg_ParseTuple(args, "i", &x)) return NULL;
return PyLong_FromLong(recurse_fib(x));
}
static PyMethodDef scriptableMethods[] = {
{"fibonacci", scriptable_fibonacci, METH_VARARGS,
"Fibonacci sequence by index"},
{NULL, NULL, 0, NULL}
};
static PyModuleDef scriptableModule = {
PyModuleDef_HEAD_INIT, "scriptable", NULL, -1, scriptableMethods,
NULL, NULL, NULL, NULL
};
static PyObject* PyInit_scriptable(void)
{
return PyModule_Create(&scriptableModule);
}
int main(int argc, char ** argv)
{
std::cout << "Output." << std::endl;
std::wcout << "Current executable path: " << executable_path()
<< "\nCurrent working directory: " << working_path() << std::endl;
std::cout << "[C++] Initializing Python\n";
//setenv("PYTHONHOME", "./Python-3.11.1", 0);
//Express this program as a module before pyinit
PyImport_AppendInittab("scriptable", &PyInit_scriptable);
//Initialize the python instance
//Py_SetPythonHome is deprecated
//Py_SetPythonHome(L"./lib/python-dist");
std::cout << "Output. (2)" << std::endl;
PyStatus status = init_python(argv[0]);
std::cout << "Output. (3)" << std::endl;
std::cout << "***\n[C++] Executing some Python\n***\n";
int result = PyRun_SimpleString("import sys,datetime\n"
"print('test\\n', datetime.__file__)\n");
std::cout << "\n***\n[C++] Execution Complete\nResult = " << result << std::endl;
std::cout << "On to other modules..." << std::endl;
//Py_Initialize();
std::string asset_path = narrow_string(executable_path()) + "/assets";
std::string script_path = narrow_string(executable_path()) + "/scripts";
// SFML demo setup
//font.loadFromFile("./assets/JetbrainsMono.ttf");
font.loadFromFile(asset_path + "/JetbrainsMono.ttf");
window.create(sf::VideoMode(640, 480), "Python/SFML/TCOD test");
// TCOD demo setup
//Run a simple string
PyRun_SimpleString("from time import time,ctime\n"
"print('Today is',ctime(time()))\n");
std::cout << "[C++] Executing engine_user.py\n";
//FILE* PScriptFile = fopen("./scripts/engine_user.py", "r");
FILE* PScriptFile = fopen((script_path + "/engine_user.py").c_str(), "r");
if(PScriptFile) {
PyRun_SimpleFile(PScriptFile, "engine_user.py");
fclose(PScriptFile);
}
/*
std::cout << "[C++] Executing Python string\n";
PyRun_SimpleString("import sys;print(sys.version)");
//Run a simple file
std::cout << "[C++] executing the contents of test.py\n";
FILE* PScriptFile = fopen("test.py", "r");
if(PScriptFile){
PyRun_SimpleFile(PScriptFile, "test.py");
fclose(PScriptFile);
}
std::cout << "[C++] importing script.script_function and executing from C++. Aquiring GIL...\n";
//Run a python function
PyObject *pName, *pModule, *pFunc, *pArgs, *pValue;
pName = PyUnicode_FromString((char*)"script");
// acquire GIL
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
pModule = PyImport_Import(pName);
pFunc = PyObject_GetAttrString(pModule, (char*)"script_function");
pArgs = PyTuple_Pack(1, PyUnicode_FromString((char*)"Embedded Python"));
pValue = PyObject_CallObject(pFunc, pArgs);
std::cout << "[C++] Result:\n";
auto result = _PyUnicode_AsString(pValue);
std::cout << result << std::endl;
// release GIL
PyGILState_Release(gstate);
*/
// TCOD functionality - gather noise
TCODNoise noise_{2, TCOD_NOISE_SIMPLEX};
float hurst_ = TCOD_NOISE_DEFAULT_HURST;
float lacunarity_ = TCOD_NOISE_DEFAULT_LACUNARITY;
noise_ = TCODNoise(2, hurst_, lacunarity_);
float display_noise[64][48];
int generated = 0;
float n_min = 0, n_max = 0;
for (int x = 0; x < 64; x++) {
for (int y = 0; y < 48; y++) {
float f[2] = {float(x * 10 + 5) / 100.0f, float(y * 10 + 5) / 100.0f};
display_noise[x][y] = float(noise_.get(f, TCOD_NOISE_SIMPLEX));
//display_noise[x][y] = float(rand()) / RAND_MAX;
generated++;
if (display_noise[x][y] > n_max) n_max = display_noise[x][y];
else if (display_noise[x][y] < n_min) n_min = display_noise[x][y];
}
}
std::cout << "Generated " << generated << " points of noise: "
<< n_min << " - " << n_max << std::endl;
// SFML/graphical run
window.setFramerateLimit(30);
text.setFont(font);
text.setString("asdf");
text.setCharacterSize(16);
bool running = true;
while (running) {
// render
window.clear();
// draw boxes of noise
for (int x = 0; x < 64; x++) {
for (int y = 0; y < 48; y++) {
//int x1 = x * 10, x2 = (x+1) * 10, y1 = y * 10, y2 = (y+1) * 10;
sf::RectangleShape r;
r.setPosition(sf::Vector2f(x * 10, y * 10));
r.setSize(sf::Vector2f(10, 10));
r.setOutlineThickness(0);
r.setFillColor(sf::Color(display_noise[x][y] * 255, 0, 0));
window.draw(r);
}
}
window.draw(text);
window.display();
// user input
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) { running = false; continue; }
}
}
//Close the python instance
Py_Finalize();
std::cout << "[C++] Exiting normally.\n";
return 0;
}

8
src/main.cpp Normal file
View File

@ -0,0 +1,8 @@
#include <SFML/Graphics.hpp>
#include "GameEngine.h"
int main()
{
GameEngine g;
g.run();
}

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>"

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)

77
symbols_linux.sh Executable file
View File

@ -0,0 +1,77 @@
#!/bin/bash
#rm -R bin/linux
mkdir -p bin/linux/lib
mkdir -p obj
rm obj/*
# copy shared objects, squish "linux" subdirectory in bin/linux/lib
#cp -R lib/linux/* bin/linux/lib
# copy assets directory (font, sprites, etc)
cp -R assets bin/linux
# copy Python code
cp -R src/scripts bin/linux/scripts
# work from output directory and change every g++ path to relative D:<
cd bin/linux
# prepare object files of engine classes
abort_compile()
{
echo "Compilation failed on $fn.cpp"
exit 1
}
# Precompile engine classes. Get errors in their file, not where they're included
for fn in $(ls ../../src/*.cpp -1 | cut -d/ -f4 | cut -d. -f1)
do
# Skip combined_poc.cpp, it has a duplicate main
if [ "$fn" = "combined_poc" ]; then continue; fi
echo "Compile $fn.cpp"
g++ \
-g \
-I../../deps_linux \
-I../../deps_linux/Python-3.11.1 \
-I../../platform/linux \
--std=c++2a \
-c ../../src/$fn.cpp \
-o ../../obj/$fn.o \
-lm \
-ldl \
-lutil \
-lpthread \
-lpython3.11 \
-lsfml-graphics \
-lsfml-window \
-lsfml-system \
-lsfml-audio \
-ltcod \
|| abort_compile $fn
done
# Final executable
# --std= : c++2a vs c++17
g++ \
-g \
--std=c++2a \
-I../../deps_linux \
-I../../deps_linux/Python-3.11.1 \
-I../../platform/linux \
../../obj/*.o \
-o mcrogueface \
-Wl,-rpath lib \
-L../../deps_linux \
-lm \
-ldl \
-lutil \
-lpthread \
-lpython3.11 \
-lsfml-graphics \
-lsfml-window \
-lsfml-system \
-lsfml-audio \
-ltcod \