Compare commits
85 Commits
master
...
rebase-for
Author | SHA1 | Date |
---|---|---|
|
3325e4895e | |
|
705943abba | |
|
47b485ca7d | |
|
f2eaee95ec | |
|
494658e5c3 | |
|
6e820af8c4 | |
|
cf485ef327 | |
|
3a1432212f | |
|
38b6a3cade | |
|
1bbb0aa5b8 | |
|
bec2b3294d | |
|
9486104377 | |
|
5267287b05 | |
|
b8af8bc870 | |
|
0ef0a5d506 | |
|
a41d3d4a54 | |
|
6d4bc2989c | |
|
d5a7cbca85 | |
|
5d8510747c | |
|
06052c81c9 | |
|
6fe7b842ef | |
|
50d926fe37 | |
|
795701c986 | |
|
884a49a63a | |
|
c4d5a497d4 | |
|
ba97aebf3e | |
|
ac0ec4bb71 | |
|
a455c44b34 | |
|
96e78e6150 | |
|
0dd86056a8 | |
|
26cb410b8e | |
|
d09fc87499 | |
|
b022dfa6e8 | |
|
232ce34d54 | |
|
c1c17bab69 | |
|
e85861cbb2 | |
|
d6446e18ea | |
|
d3826804a0 | |
|
b4c49c4619 | |
|
76ac236be3 | |
|
97793fb26b | |
|
b3134f0890 | |
|
dfcc39dd43 | |
|
29ac89b489 | |
|
b4daac6e0c | |
|
3fd60d76ea | |
|
99fa92f8ba | |
|
9441f357df | |
|
34feb226e4 | |
|
486a1cd17c | |
|
8d9148b88d | |
|
f1798189f0 | |
|
5b168737ce | |
|
6875cb5fe1 | |
|
87483cc8ad | |
|
620def19f1 | |
|
c9b97b9b35 | |
|
8e59152a8f | |
|
fedfcd46a3 | |
|
c551c721ce | |
|
d74635ee4e | |
|
47e823d5b9 | |
|
6dbf8a5119 | |
|
a53ae29467 | |
|
257aa3c3d2 | |
|
a4b6c2c428 | |
|
b0ef1d2303 | |
|
b3f946ecb2 | |
|
6a4150ec05 | |
|
e295bfb742 | |
|
2ec97dfb1c | |
|
f89896176c | |
|
de753713d5 | |
|
c8124e84dc | |
|
a1e9129923 | |
|
f23dfbe4ba | |
|
1e9fd77a13 | |
|
2c1946c29b | |
|
6d05f8bc63 | |
|
a4d0efe334 | |
|
6a47bc1e28 | |
|
6a2c3c6c36 | |
|
a6f59085eb | |
|
d2499a67f8 | |
|
1784489dfb |
|
@ -17,6 +17,10 @@ include_directories(${CMAKE_SOURCE_DIR}/deps/libtcod)
|
|||
include_directories(${CMAKE_SOURCE_DIR}/deps/cpython)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/deps/Python)
|
||||
|
||||
# TODO: Move this into the WIN32 if block below (as 'else')
|
||||
#include_directories(${CMAKE_SOURCE_DIR}/platform/linux)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/deps/platform/linux)
|
||||
|
||||
# Collect all the source files
|
||||
file(GLOB_RECURSE SOURCES "src/*.cpp")
|
||||
|
||||
|
@ -39,9 +43,6 @@ if(WIN32)
|
|||
# include_directories(path_to_additional_includes)
|
||||
# link_directories(path_to_additional_libs)
|
||||
# list(APPEND LINK_LIBS additional_windows_libs)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/deps/platform/windows)
|
||||
else()
|
||||
include_directories(${CMAKE_SOURCE_DIR}/deps/platform/linux)
|
||||
endif()
|
||||
|
||||
# Add the directory where the linker should look for the libraries
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 181 KiB |
Binary file not shown.
Before Width: | Height: | Size: 674 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 3.0 MiB |
157
css_colors.txt
157
css_colors.txt
|
@ -1,157 +0,0 @@
|
|||
aqua #00FFFF
|
||||
black #000000
|
||||
blue #0000FF
|
||||
fuchsia #FF00FF
|
||||
gray #808080
|
||||
green #008000
|
||||
lime #00FF00
|
||||
maroon #800000
|
||||
navy #000080
|
||||
olive #808000
|
||||
purple #800080
|
||||
red #FF0000
|
||||
silver #C0C0C0
|
||||
teal #008080
|
||||
white #FFFFFF
|
||||
yellow #FFFF00
|
||||
aliceblue #F0F8FF
|
||||
antiquewhite #FAEBD7
|
||||
aqua #00FFFF
|
||||
aquamarine #7FFFD4
|
||||
azure #F0FFFF
|
||||
beige #F5F5DC
|
||||
bisque #FFE4C4
|
||||
black #000000
|
||||
blanchedalmond #FFEBCD
|
||||
blue #0000FF
|
||||
blueviolet #8A2BE2
|
||||
brown #A52A2A
|
||||
burlywood #DEB887
|
||||
cadetblue #5F9EA0
|
||||
chartreuse #7FFF00
|
||||
chocolate #D2691E
|
||||
coral #FF7F50
|
||||
cornflowerblue #6495ED
|
||||
cornsilk #FFF8DC
|
||||
crimson #DC143C
|
||||
cyan #00FFFF
|
||||
darkblue #00008B
|
||||
darkcyan #008B8B
|
||||
darkgoldenrod #B8860B
|
||||
darkgray #A9A9A9
|
||||
darkgreen #006400
|
||||
darkkhaki #BDB76B
|
||||
darkmagenta #8B008B
|
||||
darkolivegreen #556B2F
|
||||
darkorange #FF8C00
|
||||
darkorchid #9932CC
|
||||
darkred #8B0000
|
||||
darksalmon #E9967A
|
||||
darkseagreen #8FBC8F
|
||||
darkslateblue #483D8B
|
||||
darkslategray #2F4F4F
|
||||
darkturquoise #00CED1
|
||||
darkviolet #9400D3
|
||||
deeppink #FF1493
|
||||
deepskyblue #00BFFF
|
||||
dimgray #696969
|
||||
dodgerblue #1E90FF
|
||||
firebrick #B22222
|
||||
floralwhite #FFFAF0
|
||||
forestgreen #228B22
|
||||
fuchsia #FF00FF
|
||||
gainsboro #DCDCDC
|
||||
ghostwhite #F8F8FF
|
||||
gold #FFD700
|
||||
goldenrod #DAA520
|
||||
gray #7F7F7F
|
||||
green #008000
|
||||
greenyellow #ADFF2F
|
||||
honeydew #F0FFF0
|
||||
hotpink #FF69B4
|
||||
indianred #CD5C5C
|
||||
indigo #4B0082
|
||||
ivory #FFFFF0
|
||||
khaki #F0E68C
|
||||
lavender #E6E6FA
|
||||
lavenderblush #FFF0F5
|
||||
lawngreen #7CFC00
|
||||
lemonchiffon #FFFACD
|
||||
lightblue #ADD8E6
|
||||
lightcoral #F08080
|
||||
lightcyan #E0FFFF
|
||||
lightgoldenrodyellow #FAFAD2
|
||||
lightgreen #90EE90
|
||||
lightgrey #D3D3D3
|
||||
lightpink #FFB6C1
|
||||
lightsalmon #FFA07A
|
||||
lightseagreen #20B2AA
|
||||
lightskyblue #87CEFA
|
||||
lightslategray #778899
|
||||
lightsteelblue #B0C4DE
|
||||
lightyellow #FFFFE0
|
||||
lime #00FF00
|
||||
limegreen #32CD32
|
||||
linen #FAF0E6
|
||||
magenta #FF00FF
|
||||
maroon #800000
|
||||
mediumaquamarine #66CDAA
|
||||
mediumblue #0000CD
|
||||
mediumorchid #BA55D3
|
||||
mediumpurple #9370DB
|
||||
mediumseagreen #3CB371
|
||||
mediumslateblue #7B68EE
|
||||
mediumspringgreen #00FA9A
|
||||
mediumturquoise #48D1CC
|
||||
mediumvioletred #C71585
|
||||
midnightblue #191970
|
||||
mintcream #F5FFFA
|
||||
mistyrose #FFE4E1
|
||||
moccasin #FFE4B5
|
||||
navajowhite #FFDEAD
|
||||
navy #000080
|
||||
navyblue #9FAFDF
|
||||
oldlace #FDF5E6
|
||||
olive #808000
|
||||
olivedrab #6B8E23
|
||||
orange #FFA500
|
||||
orangered #FF4500
|
||||
orchid #DA70D6
|
||||
palegoldenrod #EEE8AA
|
||||
palegreen #98FB98
|
||||
paleturquoise #AFEEEE
|
||||
palevioletred #DB7093
|
||||
papayawhip #FFEFD5
|
||||
peachpuff #FFDAB9
|
||||
peru #CD853F
|
||||
pink #FFC0CB
|
||||
plum #DDA0DD
|
||||
powderblue #B0E0E6
|
||||
purple #800080
|
||||
red #FF0000
|
||||
rosybrown #BC8F8F
|
||||
royalblue #4169E1
|
||||
saddlebrown #8B4513
|
||||
salmon #FA8072
|
||||
sandybrown #FA8072
|
||||
seagreen #2E8B57
|
||||
seashell #FFF5EE
|
||||
sienna #A0522D
|
||||
silver #C0C0C0
|
||||
skyblue #87CEEB
|
||||
slateblue #6A5ACD
|
||||
slategray #708090
|
||||
snow #FFFAFA
|
||||
springgreen #00FF7F
|
||||
steelblue #4682B4
|
||||
tan #D2B48C
|
||||
teal #008080
|
||||
thistle #D8BFD8
|
||||
tomato #FF6347
|
||||
turquoise #40E0D0
|
||||
violet #EE82EE
|
||||
wheat #F5DEB3
|
||||
white #FFFFFF
|
||||
whitesmoke #F5F5F5
|
||||
yellow #FFFF00
|
||||
yellowgreen #9ACD32
|
|
@ -1,40 +0,0 @@
|
|||
#ifndef __PLATFORM
|
||||
#define __PLATFORM
|
||||
#define __PLATFORM_SET_PYTHON_SEARCH_PATHS 1
|
||||
std::wstring executable_path()
|
||||
{
|
||||
/*
|
||||
wchar_t buffer[MAX_PATH];
|
||||
GetModuleFileName(NULL, buffer, MAX_PATH);
|
||||
std::wstring exec_path = buffer;
|
||||
*/
|
||||
auto exec_path = std::filesystem::canonical("/proc/self/exe").parent_path();
|
||||
return exec_path.wstring();
|
||||
//size_t path_index = exec_path.find_last_of('/');
|
||||
//return exec_path.substr(0, path_index);
|
||||
|
||||
}
|
||||
|
||||
std::wstring executable_filename()
|
||||
{
|
||||
auto exec_path = std::filesystem::canonical("/proc/self/exe");
|
||||
return exec_path.wstring();
|
||||
}
|
||||
|
||||
std::wstring working_path()
|
||||
{
|
||||
auto cwd = std::filesystem::current_path();
|
||||
return cwd.wstring();
|
||||
}
|
||||
|
||||
std::string narrow_string(std::wstring convertme)
|
||||
{
|
||||
//setup converter
|
||||
using convert_type = std::codecvt_utf8<wchar_t>;
|
||||
std::wstring_convert<convert_type, wchar_t> converter;
|
||||
|
||||
//use converter (.to_bytes: wstr->str, .from_bytes: str->wstr)
|
||||
return converter.to_bytes(convertme);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,39 +0,0 @@
|
|||
#ifndef __PLATFORM
|
||||
#define __PLATFORM
|
||||
#define __PLATFORM_SET_PYTHON_SEARCH_PATHS 0
|
||||
#include <Windows.h>
|
||||
|
||||
std::wstring executable_path()
|
||||
{
|
||||
wchar_t buffer[MAX_PATH];
|
||||
GetModuleFileName(NULL, buffer, MAX_PATH);
|
||||
std::wstring exec_path = buffer;
|
||||
size_t path_index = exec_path.find_last_of(L"\\/");
|
||||
return exec_path.substr(0, path_index);
|
||||
}
|
||||
|
||||
std::wstring executable_filename()
|
||||
{
|
||||
wchar_t buffer[MAX_PATH];
|
||||
GetModuleFileName(NULL, buffer, MAX_PATH);
|
||||
std::wstring exec_path = buffer;
|
||||
return exec_path;
|
||||
}
|
||||
|
||||
std::wstring working_path()
|
||||
{
|
||||
auto cwd = std::filesystem::current_path();
|
||||
return cwd.wstring();
|
||||
}
|
||||
|
||||
std::string narrow_string(std::wstring convertme)
|
||||
{
|
||||
//setup converter
|
||||
using convert_type = std::codecvt_utf8<wchar_t>;
|
||||
std::wstring_convert<convert_type, wchar_t> converter;
|
||||
|
||||
//use converter (.to_bytes: wstr->str, .from_bytes: str->wstr)
|
||||
return converter.to_bytes(convertme);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,111 +0,0 @@
|
|||
# data sources: CSS docs, jennyscrayoncollection 2017 article on Crayola colors, XKCD color survey
|
||||
|
||||
# target: Single C++ header file to provide a struct of color RGB codes and names.
|
||||
# This file pre-computes the nearest neighbor of every color.
|
||||
# if an RGB code being searched for is closer than the nearest neighbor, it's the closest color name.
|
||||
|
||||
def hex_to_rgb(txt):
|
||||
if '#' in txt: txt = txt.replace('#', '')
|
||||
r = txt[0:2]
|
||||
g = txt[2:4]
|
||||
b = txt[4:6]
|
||||
return tuple([int(s, 16) for s in (r,g,b)])
|
||||
|
||||
class palette:
|
||||
def __init__(self, name, filename, priority):
|
||||
self.name = name
|
||||
self.priority = priority
|
||||
with open(filename, "r") as f:
|
||||
print(f"scanning {filename}")
|
||||
self.colors = {}
|
||||
for line in f.read().split('\n'):
|
||||
if len(line.split('\t')) < 2: continue
|
||||
name, code = line.split('\t')
|
||||
#print(name, code)
|
||||
self.colors[name] = hex_to_rgb(code)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Palette '{self.name}' - {len(self.colors)} colors, priority = {self.priority}>"
|
||||
|
||||
palettes = [
|
||||
#palette("jenny", "jenny_colors.txt", 3), # I should probably use wikipedia as a source for copyright reasons
|
||||
palette("crayon", "wikicrayons_colors.txt", 2),
|
||||
palette("xkcd", "xkcd_colors.txt", 1),
|
||||
palette("css", "css_colors.txt", 0),
|
||||
#palette("matplotlib", "matplotlib_colors.txt", 2) # there's like 10 colors total, I think we'll survive without them
|
||||
]
|
||||
|
||||
all_colors = []
|
||||
|
||||
from math import sqrt
|
||||
def rgbdist(c1, c2):
|
||||
return sqrt((c1.r - c2.r)**2 + (c1.g - c2.g)**2 + (c1.b - c2.b)**2)
|
||||
|
||||
class Color:
|
||||
def __init__(self, r, g, b, name, prefix, priority):
|
||||
self.r = r
|
||||
self.g = g
|
||||
self.b = b
|
||||
self.name = name
|
||||
self.prefix = prefix
|
||||
self.priority = priority
|
||||
self.nearest_neighbor = None
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Color ({self.r}, {self.g}, {self.b}) - '{self.prefix}:{self.name}', priority = {self.priority}, nearest_neighbor={self.nearest_neighbor.name if self.nearest_neighbor is not None else None}>"
|
||||
|
||||
def nn(self, colors):
|
||||
nearest = None
|
||||
nearest_dist = 999999
|
||||
for c in colors:
|
||||
dist = rgbdist(self, c)
|
||||
if dist == 0: continue
|
||||
if dist < nearest_dist:
|
||||
nearest = c
|
||||
nearest_dist = dist
|
||||
self.nearest_neighbor = nearest
|
||||
|
||||
for p in palettes:
|
||||
prefix = p.name
|
||||
priority = p.priority
|
||||
for name, rgb in p.colors.items():
|
||||
all_colors.append(Color(*rgb, name, prefix, priority))
|
||||
print(f"{prefix}->{len(all_colors)}")
|
||||
|
||||
for c in all_colors:
|
||||
c.nn(all_colors)
|
||||
|
||||
smallest_dist = 9999999999999
|
||||
largest_dist = 0
|
||||
for c in all_colors:
|
||||
dist = rgbdist(c, c.nearest_neighbor)
|
||||
if dist > largest_dist: largest_dist = dist
|
||||
if dist < smallest_dist: smallest_dist = dist
|
||||
#print(f"{c.prefix}:{c.name} -> {c.nearest_neighbor.prefix}:{c.nearest_neighbor.name}\t{rgbdist(c, c.nearest_neighbor):.2f}")
|
||||
# questions -
|
||||
|
||||
# are there any colors where their nearest neighbor's nearest neighbor isn't them? (There should be)
|
||||
nonnear_pairs = 0
|
||||
for c in all_colors:
|
||||
neighbor = c.nearest_neighbor
|
||||
their_neighbor = neighbor.nearest_neighbor
|
||||
if c is not their_neighbor:
|
||||
#print(f"{c.prefix}:{c.name} -> {neighbor.prefix}:{neighbor.name} -> {their_neighbor.prefix}:{their_neighbor.name}")
|
||||
nonnear_pairs += 1
|
||||
print("Non-near pairs:", nonnear_pairs)
|
||||
#print(f"{c.prefix}:{c.name} -> {c.nearest_neighbor.prefix}:{c.nearest_neighbor.name}\t{rgbdist(c, c.nearest_neighbor):.2f}")
|
||||
|
||||
# Are there duplicates? They should be removed from the palette that won't be used
|
||||
dupes = 0
|
||||
for c in all_colors:
|
||||
for c2 in all_colors:
|
||||
if c is c2: continue
|
||||
if c.r == c2.r and c.g == c2.g and c.b == c2.b:
|
||||
dupes += 1
|
||||
print("dupes:", dupes, "this many will need to be removed:", dupes / 2)
|
||||
|
||||
# What order to put them in? Do we want large radiuses first, or some sort of "common color" table?
|
||||
|
||||
# does manhattan distance change any answers over the 16.7M RGB values?
|
||||
|
||||
# What's the worst case lookup? (Checking all 1200 colors to find the name?)
|
|
@ -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}>"
|
|
@ -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()
|
||||
|
|
@ -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()
|
|
@ -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>"
|
|
@ -0,0 +1,10 @@
|
|||
print("[Python] Attempting import")
|
||||
import scriptable
|
||||
|
||||
print(f"[Python] calling fibonacci(8): {scriptable.fibonacci(8)}")
|
||||
print(f"[Python] calling fibonacci(15): {scriptable.fibonacci(15)}")
|
||||
|
||||
import venv
|
||||
print("[Python] Importing library installed with pip")
|
||||
import numpy
|
||||
|
|
@ -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)
|
112
src/ActionCode.h
112
src/ActionCode.h
|
@ -31,116 +31,4 @@ public:
|
|||
if (a & WHEEL_NEG) factor = -1;
|
||||
return (a & WHEEL_DEL) * factor;
|
||||
}
|
||||
|
||||
static std::string key_str(sf::Keyboard::Key& keycode)
|
||||
{
|
||||
switch(keycode)
|
||||
{
|
||||
case sf::Keyboard::Key::Unknown: return "Unknown"; break;
|
||||
case sf::Keyboard::Key::A: return "A"; break;
|
||||
case sf::Keyboard::Key::B: return "B"; break;
|
||||
case sf::Keyboard::Key::C: return "C"; break;
|
||||
case sf::Keyboard::Key::D: return "D"; break;
|
||||
case sf::Keyboard::Key::E: return "E"; break;
|
||||
case sf::Keyboard::Key::F: return "F"; break;
|
||||
case sf::Keyboard::Key::G: return "G"; break;
|
||||
case sf::Keyboard::Key::H: return "H"; break;
|
||||
case sf::Keyboard::Key::I: return "I"; break;
|
||||
case sf::Keyboard::Key::J: return "J"; break;
|
||||
case sf::Keyboard::Key::K: return "K"; break;
|
||||
case sf::Keyboard::Key::L: return "L"; break;
|
||||
case sf::Keyboard::Key::M: return "M"; break;
|
||||
case sf::Keyboard::Key::N: return "N"; break;
|
||||
case sf::Keyboard::Key::O: return "O"; break;
|
||||
case sf::Keyboard::Key::P: return "P"; break;
|
||||
case sf::Keyboard::Key::Q: return "Q"; break;
|
||||
case sf::Keyboard::Key::R: return "R"; break;
|
||||
case sf::Keyboard::Key::S: return "S"; break;
|
||||
case sf::Keyboard::Key::T: return "T"; break;
|
||||
case sf::Keyboard::Key::U: return "U"; break;
|
||||
case sf::Keyboard::Key::V: return "V"; break;
|
||||
case sf::Keyboard::Key::W: return "W"; break;
|
||||
case sf::Keyboard::Key::X: return "X"; break;
|
||||
case sf::Keyboard::Key::Y: return "Y"; break;
|
||||
case sf::Keyboard::Key::Z: return "Z"; break;
|
||||
case sf::Keyboard::Key::Num0: return "Num0"; break;
|
||||
case sf::Keyboard::Key::Num1: return "Num1"; break;
|
||||
case sf::Keyboard::Key::Num2: return "Num2"; break;
|
||||
case sf::Keyboard::Key::Num3: return "Num3"; break;
|
||||
case sf::Keyboard::Key::Num4: return "Num4"; break;
|
||||
case sf::Keyboard::Key::Num5: return "Num5"; break;
|
||||
case sf::Keyboard::Key::Num6: return "Num6"; break;
|
||||
case sf::Keyboard::Key::Num7: return "Num7"; break;
|
||||
case sf::Keyboard::Key::Num8: return "Num8"; break;
|
||||
case sf::Keyboard::Key::Num9: return "Num9"; break;
|
||||
case sf::Keyboard::Key::Escape: return "Escape"; break;
|
||||
case sf::Keyboard::Key::LControl: return "LControl"; break;
|
||||
case sf::Keyboard::Key::LShift: return "LShift"; break;
|
||||
case sf::Keyboard::Key::LAlt: return "LAlt"; break;
|
||||
case sf::Keyboard::Key::LSystem: return "LSystem"; break;
|
||||
case sf::Keyboard::Key::RControl: return "RControl"; break;
|
||||
case sf::Keyboard::Key::RShift: return "RShift"; break;
|
||||
case sf::Keyboard::Key::RAlt: return "RAlt"; break;
|
||||
case sf::Keyboard::Key::RSystem: return "RSystem"; break;
|
||||
case sf::Keyboard::Key::Menu: return "Menu"; break;
|
||||
case sf::Keyboard::Key::LBracket: return "LBracket"; break;
|
||||
case sf::Keyboard::Key::RBracket: return "RBracket"; break;
|
||||
case sf::Keyboard::Key::Semicolon: return "Semicolon"; break;
|
||||
case sf::Keyboard::Key::Comma: return "Comma"; break;
|
||||
case sf::Keyboard::Key::Period: return "Period"; break;
|
||||
case sf::Keyboard::Key::Apostrophe: return "Apostrophe"; break;
|
||||
case sf::Keyboard::Key::Slash: return "Slash"; break;
|
||||
case sf::Keyboard::Key::Backslash: return "Backslash"; break;
|
||||
case sf::Keyboard::Key::Grave: return "Grave"; break;
|
||||
case sf::Keyboard::Key::Equal: return "Equal"; break;
|
||||
case sf::Keyboard::Key::Hyphen: return "Hyphen"; break;
|
||||
case sf::Keyboard::Key::Space: return "Space"; break;
|
||||
case sf::Keyboard::Key::Enter: return "Enter"; break;
|
||||
case sf::Keyboard::Key::Backspace: return "Backspace"; break;
|
||||
case sf::Keyboard::Key::Tab: return "Tab"; break;
|
||||
case sf::Keyboard::Key::PageUp: return "PageUp"; break;
|
||||
case sf::Keyboard::Key::PageDown: return "PageDown"; break;
|
||||
case sf::Keyboard::Key::End: return "End"; break;
|
||||
case sf::Keyboard::Key::Home: return "Home"; break;
|
||||
case sf::Keyboard::Key::Insert: return "Insert"; break;
|
||||
case sf::Keyboard::Key::Delete: return "Delete"; break;
|
||||
case sf::Keyboard::Key::Add: return "Add"; break;
|
||||
case sf::Keyboard::Key::Subtract: return "Subtract"; break;
|
||||
case sf::Keyboard::Key::Multiply: return "Multiply"; break;
|
||||
case sf::Keyboard::Key::Divide: return "Divide"; break;
|
||||
case sf::Keyboard::Key::Left: return "Left"; break;
|
||||
case sf::Keyboard::Key::Right: return "Right"; break;
|
||||
case sf::Keyboard::Key::Up: return "Up"; break;
|
||||
case sf::Keyboard::Key::Down: return "Down"; break;
|
||||
case sf::Keyboard::Key::Numpad0: return "Numpad0"; break;
|
||||
case sf::Keyboard::Key::Numpad1: return "Numpad1"; break;
|
||||
case sf::Keyboard::Key::Numpad2: return "Numpad2"; break;
|
||||
case sf::Keyboard::Key::Numpad3: return "Numpad3"; break;
|
||||
case sf::Keyboard::Key::Numpad4: return "Numpad4"; break;
|
||||
case sf::Keyboard::Key::Numpad5: return "Numpad5"; break;
|
||||
case sf::Keyboard::Key::Numpad6: return "Numpad6"; break;
|
||||
case sf::Keyboard::Key::Numpad7: return "Numpad7"; break;
|
||||
case sf::Keyboard::Key::Numpad8: return "Numpad8"; break;
|
||||
case sf::Keyboard::Key::Numpad9: return "Numpad9"; break;
|
||||
case sf::Keyboard::Key::F1: return "F1"; break;
|
||||
case sf::Keyboard::Key::F2: return "F2"; break;
|
||||
case sf::Keyboard::Key::F3: return "F3"; break;
|
||||
case sf::Keyboard::Key::F4: return "F4"; break;
|
||||
case sf::Keyboard::Key::F5: return "F5"; break;
|
||||
case sf::Keyboard::Key::F6: return "F6"; break;
|
||||
case sf::Keyboard::Key::F7: return "F7"; break;
|
||||
case sf::Keyboard::Key::F8: return "F8"; break;
|
||||
case sf::Keyboard::Key::F9: return "F9"; break;
|
||||
case sf::Keyboard::Key::F10: return "F10"; break;
|
||||
case sf::Keyboard::Key::F11: return "F11"; break;
|
||||
case sf::Keyboard::Key::F12: return "F12"; break;
|
||||
case sf::Keyboard::Key::F13: return "F13"; break;
|
||||
case sf::Keyboard::Key::F14: return "F14"; break;
|
||||
case sf::Keyboard::Key::F15: return "F15"; break;
|
||||
case sf::Keyboard::Key::Pause: return "Pause"; break;
|
||||
default:
|
||||
return "Any";
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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:
|
||||
};
|
|
@ -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;
|
||||
};
|
||||
*/
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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();
|
||||
};
|
|
@ -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];
|
||||
}
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
#include "GameEngine.h"
|
||||
#include "MenuScene.h"
|
||||
//#include "UITestScene.h"
|
||||
#include "ActionCode.h"
|
||||
#include "McRFPy_API.h"
|
||||
#include "PyScene.h"
|
||||
#include "PythonScene.h"
|
||||
#include "UITestScene.h"
|
||||
#include "Resources.h"
|
||||
|
||||
|
@ -9,44 +11,39 @@ GameEngine::GameEngine()
|
|||
{
|
||||
Resources::font.loadFromFile("./assets/JetbrainsMono.ttf");
|
||||
Resources::game = this;
|
||||
window_title = "Crypt of Sokoban - 7DRL 2025, McRogueface Engine";
|
||||
window.create(sf::VideoMode(1024, 768), window_title, sf::Style::Titlebar | sf::Style::Close);
|
||||
window_title = "McRogueFace - r/RoguelikeDev Tutorial Run";
|
||||
window.create(sf::VideoMode(1024, 768), window_title);
|
||||
visible = window.getDefaultView();
|
||||
window.setFramerateLimit(60);
|
||||
window.setFramerateLimit(30);
|
||||
scene = "uitest";
|
||||
//std::cout << "Constructing MenuScene" << std::endl;
|
||||
scenes["menu"] = new MenuScene(this);
|
||||
scenes["uitest"] = new UITestScene(this);
|
||||
|
||||
//std::cout << "Constructed MenuScene" <<std::endl;
|
||||
//scenes["play"] = new UITestScene(this);
|
||||
//api = new McRFPy_API(this);
|
||||
|
||||
McRFPy_API::game = this;
|
||||
McRFPy_API::api_init();
|
||||
McRFPy_API::executePyString("import mcrfpy");
|
||||
McRFPy_API::executeScript("scripts/game.py");
|
||||
//McRFPy_API::executePyString("from UIMenu import *");
|
||||
//McRFPy_API::executePyString("from Grid import *");
|
||||
|
||||
//scenes["py"] = new PythonScene(this, "TestScene");
|
||||
|
||||
IndexSprite::game = this;
|
||||
|
||||
clock.restart();
|
||||
runtime.restart();
|
||||
}
|
||||
|
||||
Scene* GameEngine::currentScene() { return scenes[scene]; }
|
||||
void GameEngine::changeScene(std::string s)
|
||||
{
|
||||
/*std::cout << "Current scene is now '" << s << "'\n";*/
|
||||
if (scenes.find(s) != scenes.end())
|
||||
scene = s;
|
||||
else
|
||||
std::cout << "Attempted to change to a scene that doesn't exist (`" << s << "`)" << std::endl;
|
||||
}
|
||||
void GameEngine::changeScene(std::string s) { std::cout << "Current scene is now '" << s << "'\n"; scene = s; }
|
||||
void GameEngine::quit() { running = false; }
|
||||
void GameEngine::setPause(bool p) { paused = p; }
|
||||
sf::Font & GameEngine::getFont() { /*return font; */ return Resources::font; }
|
||||
sf::RenderWindow & GameEngine::getWindow() { return window; }
|
||||
|
||||
void GameEngine::createScene(std::string s) { scenes[s] = new PyScene(this); }
|
||||
|
||||
void GameEngine::setWindowScale(float multiplier)
|
||||
{
|
||||
window.setSize(sf::Vector2u(1024 * multiplier, 768 * multiplier)); // 7DRL 2024: window scaling
|
||||
//window.create(sf::VideoMode(1024 * multiplier, 768 * multiplier), window_title, sf::Style::Titlebar | sf::Style::Close);
|
||||
}
|
||||
|
||||
void GameEngine::run()
|
||||
{
|
||||
float fps = 0.0;
|
||||
|
@ -54,57 +51,15 @@ void GameEngine::run()
|
|||
while (running)
|
||||
{
|
||||
currentScene()->update();
|
||||
testTimers();
|
||||
sUserInput();
|
||||
if (!paused)
|
||||
{
|
||||
}
|
||||
currentScene()->render();
|
||||
currentScene()->sRender();
|
||||
currentFrame++;
|
||||
frameTime = clock.restart().asSeconds();
|
||||
fps = 1 / frameTime;
|
||||
int whole_fps = (int)fps;
|
||||
int tenth_fps = int(fps * 100) % 10;
|
||||
//window.setTitle(window_title + " " + std::to_string(fps) + " FPS");
|
||||
window.setTitle(window_title + " " + std::to_string(whole_fps) + "." + std::to_string(tenth_fps) + " FPS");
|
||||
}
|
||||
}
|
||||
|
||||
void GameEngine::manageTimer(std::string name, PyObject* target, int interval)
|
||||
{
|
||||
auto it = timers.find(name);
|
||||
if (it != timers.end()) // overwrite existing
|
||||
{
|
||||
if (target == NULL || target == Py_None)
|
||||
{
|
||||
// Delete: Overwrite existing timer with one that calls None. This will be deleted in the next timer check
|
||||
// see gitea issue #4: this allows for a timer to be deleted during its own call to itself
|
||||
timers[name] = std::make_shared<PyTimerCallable>(Py_None, 1000, runtime.getElapsedTime().asMilliseconds());
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (target == NULL || target == Py_None)
|
||||
{
|
||||
std::cout << "Refusing to initialize timer to None. It's not an error, it's just pointless." << std::endl;
|
||||
return;
|
||||
}
|
||||
timers[name] = std::make_shared<PyTimerCallable>(target, interval, runtime.getElapsedTime().asMilliseconds());
|
||||
}
|
||||
|
||||
void GameEngine::testTimers()
|
||||
{
|
||||
int now = runtime.getElapsedTime().asMilliseconds();
|
||||
auto it = timers.begin();
|
||||
while (it != timers.end())
|
||||
{
|
||||
it->second->test(now);
|
||||
|
||||
if (it->second->isNone())
|
||||
{
|
||||
it = timers.erase(it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
window.setTitle(window_title + " " + std::to_string(fps) + " FPS");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,20 +72,12 @@ void GameEngine::sUserInput()
|
|||
int actionCode = 0;
|
||||
|
||||
if (event.type == sf::Event::Closed) { running = false; continue; }
|
||||
// TODO: add resize event to Scene to react; call it after constructor too, maybe
|
||||
else if (event.type == sf::Event::Resized) {
|
||||
continue; // 7DRL short circuit. Resizing manually disabled
|
||||
/*
|
||||
sf::FloatRect area(0.f, 0.f, event.size.width, event.size.height);
|
||||
//sf::FloatRect area(0.f, 0.f, 1024.f, 768.f); // 7DRL 2024: attempt to set scale appropriately
|
||||
//sf::FloatRect area(0.f, 0.f, event.size.width, event.size.width * 0.75);
|
||||
visible = sf::View(area);
|
||||
window.setView(visible);
|
||||
//window.setSize(sf::Vector2u(event.size.width, event.size.width * 0.75)); // 7DRL 2024: window scaling
|
||||
std::cout << "Visible area set to (0, 0, " << event.size.width << ", " << event.size.height <<")"<<std::endl;
|
||||
//std::cout << "Visible area set to (0, 0, " << event.size.width << ", " << event.size.height <<")"<<std::endl;
|
||||
actionType = "resize";
|
||||
//window.setSize(sf::Vector2u(event.size.width, event.size.width * 0.75)); // 7DRL 2024: window scaling
|
||||
*/
|
||||
}
|
||||
|
||||
else if (event.type == sf::Event::KeyPressed || event.type == sf::Event::MouseButtonPressed || event.type == sf::Event::MouseWheelScrolled) actionType = "start";
|
||||
|
@ -167,23 +114,6 @@ void GameEngine::sUserInput()
|
|||
std::string name = currentScene()->action(actionCode);
|
||||
currentScene()->doAction(name, actionType);
|
||||
}
|
||||
else if (currentScene()->key_callable)
|
||||
{
|
||||
currentScene()->key_callable->call(ActionCode::key_str(event.key.code), actionType);
|
||||
/*
|
||||
PyObject* args = Py_BuildValue("(ss)", ActionCode::key_str(event.key.code).c_str(), actionType.c_str());
|
||||
PyObject* retval = PyObject_Call(currentScene()->key_callable, args, NULL);
|
||||
if (!retval)
|
||||
{
|
||||
std::cout << "key_callable has raised an exception. It's going to STDERR and being dropped:" << std::endl;
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
} else if (retval != Py_None)
|
||||
{
|
||||
std::cout << "key_callable returned a non-None value. It's not an error, it's just not being saved or used." << std::endl;
|
||||
}
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
//std::cout << "[GameEngine] Action not registered for input: " << actionCode << ": " << actionType << std::endl;
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Entity.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Scene.h"
|
||||
#include "McRFPy_API.h"
|
||||
#include "IndexTexture.h"
|
||||
#include "Timer.h"
|
||||
#include "PyCallable.h"
|
||||
|
||||
class GameEngine
|
||||
{
|
||||
sf::RenderWindow window;
|
||||
sf::Font font;
|
||||
std::string scene;
|
||||
std::map<std::string, Scene*> scenes;
|
||||
bool running = true;
|
||||
bool paused = false;
|
||||
|
@ -20,17 +21,10 @@ class GameEngine
|
|||
float frameTime;
|
||||
std::string window_title;
|
||||
|
||||
sf::Clock runtime;
|
||||
//std::map<std::string, Timer> timers;
|
||||
std::map<std::string, std::shared_ptr<PyTimerCallable>> timers;
|
||||
void testTimers();
|
||||
|
||||
public:
|
||||
std::string scene;
|
||||
GameEngine();
|
||||
Scene* currentScene();
|
||||
void changeScene(std::string);
|
||||
void createScene(std::string);
|
||||
void quit();
|
||||
void setPause(bool);
|
||||
sf::Font & getFont();
|
||||
|
@ -40,8 +34,6 @@ public:
|
|||
int getFrame() { return currentFrame; }
|
||||
float getFrameTime() { return frameTime; }
|
||||
sf::View getView() { return visible; }
|
||||
void manageTimer(std::string, PyObject*, int);
|
||||
void setWindowScale(float);
|
||||
|
||||
// global textures for scripts to access
|
||||
std::vector<IndexTexture> textures;
|
||||
|
|
|
@ -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];
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
class GameEngine; // forward declare
|
||||
|
||||
class IndexSprite {
|
||||
public:
|
||||
int texture_index, sprite_index;
|
||||
float x, y;
|
||||
float scale;
|
||||
static GameEngine* game;
|
||||
sf::Sprite drawable();
|
||||
IndexSprite(int, int, float, float, float);
|
||||
};
|
File diff suppressed because it is too large
Load Diff
133
src/McRFPy_API.h
133
src/McRFPy_API.h
|
@ -1,10 +1,19 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Entity.h"
|
||||
//#include "EntityManager.h"
|
||||
//#include "Scene.h"
|
||||
//#include "GameEngine.h" // can't - need forward declaration
|
||||
//#include "ActionCode.h"
|
||||
#include "Python.h"
|
||||
#include "UIMenu.h"
|
||||
#include "Grid.h"
|
||||
#include "IndexSprite.h"
|
||||
#include "EntityManager.h"
|
||||
#include <list>
|
||||
|
||||
#include "PyFont.h"
|
||||
#include "PyTexture.h"
|
||||
// implementation required to link templates
|
||||
#include "Animation.h"
|
||||
|
||||
class GameEngine; // forward declared (circular members)
|
||||
|
||||
|
@ -15,29 +24,63 @@ private:
|
|||
texture_width = 12, texture_height = 11, // w & h sprite/frame count
|
||||
texture_sprite_count = 11 * 12; // t_width * t_height, minus blanks?
|
||||
|
||||
// TODO: this is wrong, load resources @ GameEngineSprite sprite;
|
||||
// sf::Texture texture;
|
||||
|
||||
//std::vector<PyMethodDef> mcrfpyMethodsVector;
|
||||
//static PyObject* PyInit_mcrfpy();
|
||||
|
||||
McRFPy_API();
|
||||
|
||||
|
||||
public:
|
||||
static PyObject* mcrf_module;
|
||||
static std::shared_ptr<PyFont> default_font;
|
||||
static std::shared_ptr<PyTexture> default_texture;
|
||||
//inline static sf::Sprite sprite;
|
||||
//inline static sf::Texture texture;
|
||||
//static void setSpriteTexture(int);
|
||||
inline static sf::Sprite sprite;
|
||||
inline static sf::Texture texture;
|
||||
static void setSpriteTexture(int);
|
||||
inline static GameEngine* game;
|
||||
static void api_init();
|
||||
static void api_shutdown();
|
||||
// Python API functionality - use mcrfpy.* in scripts
|
||||
//static PyObject* _drawSprite(PyObject*, PyObject*);
|
||||
static PyObject* _drawSprite(PyObject*, PyObject*);
|
||||
static void REPL_device(FILE * fp, const char *filename);
|
||||
static void REPL();
|
||||
|
||||
// Jank mode engage: let the API hold data for Python to hack on
|
||||
static std::map<std::string, UIMenu*> menus;
|
||||
static EntityManager entities; // this is also kinda good, entities not on the current grid can still act (like monsters following you through doors??)
|
||||
static std::map<std::string, Grid*> grids;
|
||||
static std::list<Animation*> animations;
|
||||
static std::vector<sf::SoundBuffer> soundbuffers;
|
||||
static sf::Music music;
|
||||
static sf::Sound sfx;
|
||||
|
||||
static std::shared_ptr<Entity> player;
|
||||
|
||||
static std::map<std::string, PyObject*> callbacks;
|
||||
|
||||
// Jank Python Method Exposures
|
||||
static PyObject* _createMenu(PyObject*, PyObject*); // creates a new menu object in McRFPy_API::menus
|
||||
static PyObject* _listMenus(PyObject*, PyObject*);
|
||||
static PyObject* _modMenu(PyObject*, PyObject*);
|
||||
|
||||
static PyObject* _createCaption(PyObject*, PyObject*); // calls menu.add_caption
|
||||
static PyObject* _createButton(PyObject*, PyObject*);
|
||||
static PyObject* _createTexture(PyObject*, PyObject*);
|
||||
static PyObject* _listTextures(PyObject*, PyObject*);
|
||||
static PyObject* _createSprite(PyObject*, PyObject*);
|
||||
|
||||
// use _listMenus, probably will not implement
|
||||
//static PyObject* _listCaptions(PyObject*, PyObject*);
|
||||
//static PyObject* _listButtons(PyObject*, PyObject*);
|
||||
|
||||
static PyObject* _createEntity(PyObject*, PyObject*);
|
||||
//static PyObject* _listEntities(PyObject*, PyObject*);
|
||||
|
||||
static PyObject* _createGrid(PyObject*, PyObject*);
|
||||
static PyObject* _listGrids(PyObject*, PyObject*);
|
||||
static PyObject* _modGrid(PyObject*, PyObject*);
|
||||
|
||||
static PyObject* _createAnimation(PyObject*, PyObject*);
|
||||
|
||||
static PyObject* _registerPyAction(PyObject*, PyObject*);
|
||||
static PyObject* _registerInputAction(PyObject*, PyObject*);
|
||||
|
||||
|
@ -49,29 +92,71 @@ public:
|
|||
static PyObject* _getMusicVolume(PyObject*, PyObject*);
|
||||
static PyObject* _getSoundVolume(PyObject*, PyObject*);
|
||||
|
||||
// allow all player actions (items, menus, movement, combat)
|
||||
static PyObject* _unlockPlayerInput(PyObject*, PyObject*);
|
||||
// disallow player actions (animating enemy actions)
|
||||
static PyObject* _lockPlayerInput(PyObject*, PyObject*);
|
||||
// prompt C++/Grid Objects to callback with a target Entity or grid space
|
||||
static PyObject* _requestGridTarget(PyObject*, PyObject*);
|
||||
// string for labeling the map
|
||||
static std::string active_grid;
|
||||
static PyObject* _activeGrid(PyObject*, PyObject*);
|
||||
static PyObject* _setActiveGrid(PyObject*, PyObject*);
|
||||
// string for prompting input
|
||||
static std::string input_mode;
|
||||
static PyObject* _inputMode(PyObject*, PyObject*);
|
||||
// turn cycle
|
||||
static int turn_number;
|
||||
static PyObject* _turnNumber(PyObject*, PyObject*);
|
||||
static PyObject* _refreshFov(PyObject*, PyObject*);
|
||||
static bool do_camfollow;
|
||||
static void camFollow();
|
||||
static PyObject* _camFollow(PyObject*, PyObject*);
|
||||
|
||||
static PyObject* _sceneUI(PyObject*, PyObject*);
|
||||
|
||||
// scene control
|
||||
static PyObject* _setScene(PyObject*, PyObject*);
|
||||
static PyObject* _currentScene(PyObject*, PyObject*);
|
||||
static PyObject* _createScene(PyObject*, PyObject*);
|
||||
static PyObject* _keypressScene(PyObject*, PyObject*);
|
||||
|
||||
// timer control
|
||||
static PyObject* _setTimer(PyObject*, PyObject*);
|
||||
static PyObject* _delTimer(PyObject*, PyObject*);
|
||||
|
||||
static PyObject* _exit(PyObject*, PyObject*);
|
||||
static PyObject* _setScale(PyObject*, PyObject*);
|
||||
|
||||
|
||||
// accept keyboard input from scene
|
||||
static sf::Vector2i cursor_position;
|
||||
static void player_input(int, int);
|
||||
static void computerTurn();
|
||||
static void playerTurn();
|
||||
|
||||
// Jank Functionality
|
||||
static UIMenu* createMenu(int posx, int posy, int sizex, int sizey);
|
||||
static void createCaption(std::string menukey, std::string text, int fontsize, sf::Color textcolor);
|
||||
static void createButton(std::string menukey, int x, int y, int w, int h, sf::Color bgcolor, sf::Color textcolor, std::string caption, std::string action);
|
||||
static void createSprite(std::string menukey, int ti, int si, float x, float y, float scale);
|
||||
static int createTexture(std::string filename, int grid_size, int grid_width, int grid_height);
|
||||
//static void playSound(const char * filename);
|
||||
//static void playMusic(const char * filename);
|
||||
|
||||
static void doAction(std::string);
|
||||
|
||||
// McRFPy_API(GameEngine*);
|
||||
|
||||
// API functionality - use from C++ directly
|
||||
|
||||
//void spawnEntity(int tex_index, int grid_x, int grid_y, PyObject* script);
|
||||
|
||||
static void executeScript(std::string);
|
||||
static void executePyString(std::string);
|
||||
};
|
||||
|
||||
/*
|
||||
static PyMethodDef mcrfpyMethods[] = {
|
||||
{"drawSprite", McRFPy_API::_drawSprite, METH_VARARGS,
|
||||
"Draw a sprite (index, x, y)"},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
static PyModuleDef mcrfpyModule = {
|
||||
PyModuleDef_HEAD_INIT, "mcrfpy", NULL, -1, mcrfpyMethods,
|
||||
NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
// Module initializer fn, passed to PyImport_AppendInittab
|
||||
PyObject* PyInit_mcrfpy()
|
||||
{
|
||||
return PyModule_Create(&mcrfpyModule);
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -4,14 +4,15 @@
|
|||
#include "Scene.h"
|
||||
#include "GameEngine.h"
|
||||
|
||||
class PyScene: public Scene
|
||||
class MenuScene: public Scene
|
||||
{
|
||||
sf::Text text;
|
||||
sf::Text text2;
|
||||
sf::Text text3;
|
||||
|
||||
public:
|
||||
PyScene(GameEngine*);
|
||||
MenuScene(GameEngine*);
|
||||
void update() override final;
|
||||
void doAction(std::string, std::string) override final;
|
||||
void render() override final;
|
||||
|
||||
void do_mouse_input(std::string, std::string);
|
||||
void sRender() override final;
|
||||
};
|
|
@ -1,114 +0,0 @@
|
|||
#include "PyCallable.h"
|
||||
|
||||
PyCallable::PyCallable(PyObject* _target)
|
||||
{
|
||||
target = Py_XNewRef(_target);
|
||||
}
|
||||
|
||||
PyCallable::~PyCallable()
|
||||
{
|
||||
if (target)
|
||||
Py_DECREF(target);
|
||||
}
|
||||
|
||||
PyObject* PyCallable::call(PyObject* args, PyObject* kwargs)
|
||||
{
|
||||
return PyObject_Call(target, args, kwargs);
|
||||
}
|
||||
|
||||
bool PyCallable::isNone()
|
||||
{
|
||||
return (target == Py_None || target == NULL);
|
||||
}
|
||||
|
||||
PyTimerCallable::PyTimerCallable(PyObject* _target, int _interval, int now)
|
||||
: PyCallable(_target), interval(_interval), last_ran(now)
|
||||
{}
|
||||
|
||||
PyTimerCallable::PyTimerCallable()
|
||||
: PyCallable(Py_None), interval(0), last_ran(0)
|
||||
{}
|
||||
|
||||
bool PyTimerCallable::hasElapsed(int now)
|
||||
{
|
||||
return now >= last_ran + interval;
|
||||
}
|
||||
|
||||
void PyTimerCallable::call(int now)
|
||||
{
|
||||
PyObject* args = Py_BuildValue("(i)", now);
|
||||
PyObject* retval = PyCallable::call(args, NULL);
|
||||
if (!retval)
|
||||
{
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
} else if (retval != Py_None)
|
||||
{
|
||||
std::cout << "timer returned a non-None value. It's not an error, it's just not being saved or used." << std::endl;
|
||||
std::cout << PyUnicode_AsUTF8(PyObject_Repr(retval)) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool PyTimerCallable::test(int now)
|
||||
{
|
||||
if(hasElapsed(now))
|
||||
{
|
||||
call(now);
|
||||
last_ran = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
PyClickCallable::PyClickCallable(PyObject* _target)
|
||||
: PyCallable(_target)
|
||||
{}
|
||||
|
||||
PyClickCallable::PyClickCallable()
|
||||
: PyCallable(Py_None)
|
||||
{}
|
||||
|
||||
void PyClickCallable::call(sf::Vector2f mousepos, std::string button, std::string action)
|
||||
{
|
||||
PyObject* args = Py_BuildValue("(iiss)", (int)mousepos.x, (int)mousepos.y, button.c_str(), action.c_str());
|
||||
PyObject* retval = PyCallable::call(args, NULL);
|
||||
if (!retval)
|
||||
{
|
||||
std::cout << "ClickCallable has raised an exception. It's going to STDERR and being dropped:" << std::endl;
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
} else if (retval != Py_None)
|
||||
{
|
||||
std::cout << "ClickCallable returned a non-None value. It's not an error, it's just not being saved or used." << std::endl;
|
||||
std::cout << PyUnicode_AsUTF8(PyObject_Repr(retval)) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject* PyClickCallable::borrow()
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
PyKeyCallable::PyKeyCallable(PyObject* _target)
|
||||
: PyCallable(_target)
|
||||
{}
|
||||
|
||||
PyKeyCallable::PyKeyCallable()
|
||||
: PyCallable(Py_None)
|
||||
{}
|
||||
|
||||
void PyKeyCallable::call(std::string key, std::string action)
|
||||
{
|
||||
if (target == Py_None || target == NULL) return;
|
||||
PyObject* args = Py_BuildValue("(ss)", key.c_str(), action.c_str());
|
||||
PyObject* retval = PyCallable::call(args, NULL);
|
||||
if (!retval)
|
||||
{
|
||||
std::cout << "KeyCallable has raised an exception. It's going to STDERR and being dropped:" << std::endl;
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
} else if (retval != Py_None)
|
||||
{
|
||||
std::cout << "KeyCallable returned a non-None value. It's not an error, it's just not being saved or used." << std::endl;
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Python.h"
|
||||
|
||||
class PyCallable
|
||||
{
|
||||
protected:
|
||||
PyObject* target;
|
||||
PyCallable(PyObject*);
|
||||
~PyCallable();
|
||||
PyObject* call(PyObject*, PyObject*);
|
||||
public:
|
||||
bool isNone();
|
||||
};
|
||||
|
||||
class PyTimerCallable: public PyCallable
|
||||
{
|
||||
private:
|
||||
int interval;
|
||||
int last_ran;
|
||||
void call(int);
|
||||
public:
|
||||
bool hasElapsed(int);
|
||||
bool test(int);
|
||||
PyTimerCallable(PyObject*, int, int);
|
||||
PyTimerCallable();
|
||||
};
|
||||
|
||||
class PyClickCallable: public PyCallable
|
||||
{
|
||||
public:
|
||||
void call(sf::Vector2f, std::string, std::string);
|
||||
PyObject* borrow();
|
||||
PyClickCallable(PyObject*);
|
||||
PyClickCallable();
|
||||
};
|
||||
|
||||
class PyKeyCallable: public PyCallable
|
||||
{
|
||||
public:
|
||||
void call(std::string, std::string);
|
||||
//PyObject* borrow(); // not yet implemented
|
||||
PyKeyCallable(PyObject*);
|
||||
PyKeyCallable();
|
||||
};
|
150
src/PyColor.cpp
150
src/PyColor.cpp
|
@ -1,150 +0,0 @@
|
|||
#include "PyColor.h"
|
||||
#include "McRFPy_API.h"
|
||||
|
||||
PyGetSetDef PyColor::getsetters[] = {
|
||||
{"r", (getter)PyColor::get_member, (setter)PyColor::set_member, "Red component", (void*)0},
|
||||
{"g", (getter)PyColor::get_member, (setter)PyColor::set_member, "Green component", (void*)1},
|
||||
{"b", (getter)PyColor::get_member, (setter)PyColor::set_member, "Blue component", (void*)2},
|
||||
{"a", (getter)PyColor::get_member, (setter)PyColor::set_member, "Alpha component", (void*)3},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyColor::PyColor(sf::Color target)
|
||||
:data(target) {}
|
||||
|
||||
PyObject* PyColor::pyObject()
|
||||
{
|
||||
PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyColorType, 0);
|
||||
Py_INCREF(obj);
|
||||
PyColorObject* self = (PyColorObject*)obj;
|
||||
self->data = data;
|
||||
return obj;
|
||||
}
|
||||
|
||||
sf::Color PyColor::fromPy(PyObject* obj)
|
||||
{
|
||||
PyColorObject* self = (PyColorObject*)obj;
|
||||
return self->data;
|
||||
}
|
||||
|
||||
sf::Color PyColor::fromPy(PyColorObject* self)
|
||||
{
|
||||
return self->data;
|
||||
}
|
||||
|
||||
void PyColor::set(sf::Color color)
|
||||
{
|
||||
data = color;
|
||||
}
|
||||
|
||||
sf::Color PyColor::get()
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
Py_hash_t PyColor::hash(PyObject* obj)
|
||||
{
|
||||
auto self = (PyColorObject*)obj;
|
||||
Py_hash_t value = 0;
|
||||
value += self->data.r;
|
||||
value << 8; value += self->data.g;
|
||||
value << 8; value += self->data.b;
|
||||
value << 8; value += self->data.a;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
PyObject* PyColor::repr(PyObject* obj)
|
||||
{
|
||||
PyColorObject* self = (PyColorObject*)obj;
|
||||
std::ostringstream ss;
|
||||
sf::Color c = self->data;
|
||||
ss << "<Color (" << int(c.r) << ", " << int(c.g) << ", " << int(c.b) << ", " << int(c.a) << ")>";
|
||||
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
||||
|
||||
|
||||
int PyColor::init(PyColorObject* self, PyObject* args, PyObject* kwds) {
|
||||
//using namespace mcrfpydef;
|
||||
static const char* keywords[] = { "r", "g", "b", "a", nullptr };
|
||||
PyObject* leader;
|
||||
int r = -1, g = -1, b = -1, a = 255;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iii", const_cast<char**>(keywords), &leader, &g, &b, &a)) {
|
||||
PyErr_SetString(PyExc_TypeError, "mcrfpy.Color requires a 3-tuple, 4-tuple, color name, or integer values within 0-255 (r, g, b, optionally a)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//std::cout << "Arg parsing succeeded. Values: " << r << " " << g << " " << b << " " << a <<std::endl;
|
||||
//std::cout << PyUnicode_AsUTF8(PyObject_Repr(leader)) << std::endl;
|
||||
// Tuple cases
|
||||
if (PyTuple_Check(leader)) {
|
||||
Py_ssize_t tupleSize = PyTuple_Size(leader);
|
||||
if (tupleSize < 3 || tupleSize > 4) {
|
||||
PyErr_SetString(PyExc_TypeError, "Invalid tuple length: mcrfpy.Color requires a 3-tuple, 4-tuple, color name, or integer values within 0-255 (r, g, b, optionally a)");
|
||||
return -1;
|
||||
}
|
||||
r = PyLong_AsLong(PyTuple_GetItem(leader, 0));
|
||||
g = PyLong_AsLong(PyTuple_GetItem(leader, 1));
|
||||
b = PyLong_AsLong(PyTuple_GetItem(leader, 2));
|
||||
if (tupleSize == 4) {
|
||||
a = PyLong_AsLong(PyTuple_GetItem(leader, 3));
|
||||
}
|
||||
}
|
||||
// Color name (not implemented yet)
|
||||
else if (PyUnicode_Check(leader)) {
|
||||
PyErr_SetString(PyExc_NotImplementedError, "Color names aren't ready yet");
|
||||
return -1;
|
||||
}
|
||||
// Check if the leader is actually an integer for the r value
|
||||
else if (PyLong_Check(leader)) {
|
||||
r = PyLong_AsLong(leader);
|
||||
// Additional validation not shown; g, b are required to be parsed
|
||||
} else {
|
||||
PyErr_SetString(PyExc_TypeError, "mcrfpy.Color requires a 3-tuple, 4-tuple, color name, or integer values within 0-255 (r, g, b, optionally a)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Validate color values
|
||||
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) {
|
||||
PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
self->data = sf::Color(r, g, b, a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* PyColor::pynew(PyTypeObject* type, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
auto obj = (PyObject*)type->tp_alloc(type, 0);
|
||||
//Py_INCREF(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
PyObject* PyColor::get_member(PyObject* obj, void* closure)
|
||||
{
|
||||
// TODO
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
int PyColor::set_member(PyObject* obj, PyObject* value, void* closure)
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyColorObject* PyColor::from_arg(PyObject* args)
|
||||
{
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color");
|
||||
if (PyObject_IsInstance(args, (PyObject*)type)) return (PyColorObject*)args;
|
||||
auto obj = (PyColorObject*)type->tp_alloc(type, 0);
|
||||
int err = init(obj, args, NULL);
|
||||
if (err) {
|
||||
Py_DECREF(obj);
|
||||
return NULL;
|
||||
}
|
||||
return obj;
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Python.h"
|
||||
|
||||
class PyColor;
|
||||
class UIDrawable; // forward declare for pointer
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
sf::Color data;
|
||||
} PyColorObject;
|
||||
|
||||
class PyColor
|
||||
{
|
||||
private:
|
||||
public:
|
||||
sf::Color data;
|
||||
PyColor(sf::Color);
|
||||
void set(sf::Color);
|
||||
sf::Color get();
|
||||
PyObject* pyObject();
|
||||
static sf::Color fromPy(PyObject*);
|
||||
static sf::Color fromPy(PyColorObject*);
|
||||
static PyObject* repr(PyObject*);
|
||||
static Py_hash_t hash(PyObject*);
|
||||
static int init(PyColorObject*, PyObject*, PyObject*);
|
||||
static PyObject* pynew(PyTypeObject* type, PyObject* args=NULL, PyObject* kwds=NULL);
|
||||
static PyObject* get_member(PyObject*, void*);
|
||||
static int set_member(PyObject*, PyObject*, void*);
|
||||
|
||||
static PyGetSetDef getsetters[];
|
||||
static PyColorObject* from_arg(PyObject*);
|
||||
};
|
||||
|
||||
namespace mcrfpydef {
|
||||
static PyTypeObject PyColorType = {
|
||||
.tp_name = "mcrfpy.Color",
|
||||
.tp_basicsize = sizeof(PyColorObject),
|
||||
.tp_itemsize = 0,
|
||||
.tp_repr = PyColor::repr,
|
||||
.tp_hash = PyColor::hash,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = PyDoc_STR("SFML Color Object"),
|
||||
.tp_getset = PyColor::getsetters,
|
||||
.tp_init = (initproc)PyColor::init,
|
||||
.tp_new = PyColor::pynew,
|
||||
};
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
#include "PyFont.h"
|
||||
#include "McRFPy_API.h"
|
||||
|
||||
|
||||
PyFont::PyFont(std::string filename)
|
||||
: source(filename)
|
||||
{
|
||||
font = sf::Font();
|
||||
font.loadFromFile(source);
|
||||
}
|
||||
|
||||
PyObject* PyFont::pyObject()
|
||||
{
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Font");
|
||||
//PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyFontType, 0);
|
||||
PyObject* obj = PyFont::pynew(type, Py_None, Py_None);
|
||||
try {
|
||||
((PyFontObject*)obj)->data = shared_from_this();
|
||||
}
|
||||
catch (std::bad_weak_ptr& e)
|
||||
{
|
||||
std::cout << "Bad weak ptr: shared_from_this() failed in PyFont::pyObject(); did you create a PyFont outside of std::make_shared? enjoy your segfault, soon!" << std::endl;
|
||||
}
|
||||
// TODO - shared_from_this will raise an exception if the object does not have a shared pointer. Constructor should be made private; write a factory function
|
||||
return obj;
|
||||
}
|
||||
|
||||
PyObject* PyFont::repr(PyObject* obj)
|
||||
{
|
||||
PyFontObject* self = (PyFontObject*)obj;
|
||||
std::ostringstream ss;
|
||||
if (!self->data)
|
||||
{
|
||||
ss << "<Font [invalid internal object]>";
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
||||
auto& pfont = *(self->data);
|
||||
ss << "<Font (family=" << pfont.font.getInfo().family << ") source=`" << pfont.source << "`>";
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
||||
|
||||
Py_hash_t PyFont::hash(PyObject* obj)
|
||||
{
|
||||
auto self = (PyFontObject*)obj;
|
||||
return reinterpret_cast<Py_hash_t>(self->data.get());
|
||||
}
|
||||
|
||||
int PyFont::init(PyFontObject* self, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
static const char* keywords[] = { "filename", nullptr };
|
||||
char* filename;
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", const_cast<char**>(keywords), &filename))
|
||||
return -1;
|
||||
self->data = std::make_shared<PyFont>(filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* PyFont::pynew(PyTypeObject* type, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
return (PyObject*)type->tp_alloc(type, 0);
|
||||
}
|
39
src/PyFont.h
39
src/PyFont.h
|
@ -1,39 +0,0 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Python.h"
|
||||
|
||||
class PyFont;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
std::shared_ptr<PyFont> data;
|
||||
} PyFontObject;
|
||||
|
||||
class PyFont : public std::enable_shared_from_this<PyFont>
|
||||
{
|
||||
private:
|
||||
std::string source;
|
||||
public:
|
||||
PyFont(std::string filename);
|
||||
sf::Font font;
|
||||
PyObject* pyObject();
|
||||
static PyObject* repr(PyObject*);
|
||||
static Py_hash_t hash(PyObject*);
|
||||
static int init(PyFontObject*, PyObject*, PyObject*);
|
||||
static PyObject* pynew(PyTypeObject* type, PyObject* args=NULL, PyObject* kwds=NULL);
|
||||
};
|
||||
|
||||
namespace mcrfpydef {
|
||||
static PyTypeObject PyFontType = {
|
||||
.tp_name = "mcrfpy.Font",
|
||||
.tp_basicsize = sizeof(PyFontObject),
|
||||
.tp_itemsize = 0,
|
||||
.tp_repr = PyFont::repr,
|
||||
//.tp_hash = PyFont::hash,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = PyDoc_STR("SFML Font Object"),
|
||||
//.tp_base = &PyBaseObject_Type,
|
||||
.tp_init = (initproc)PyFont::init,
|
||||
.tp_new = PyType_GenericNew, //PyFont::pynew,
|
||||
};
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
#include "PyScene.h"
|
||||
#include "ActionCode.h"
|
||||
#include "Resources.h"
|
||||
#include "PyCallable.h"
|
||||
|
||||
PyScene::PyScene(GameEngine* g) : Scene(g)
|
||||
{
|
||||
// mouse events
|
||||
registerAction(ActionCode::MOUSEBUTTON + sf::Mouse::Left, "left");
|
||||
registerAction(ActionCode::MOUSEBUTTON + sf::Mouse::Right, "right");
|
||||
registerAction(ActionCode::MOUSEWHEEL + ActionCode::WHEEL_DEL, "wheel_up");
|
||||
registerAction(ActionCode::MOUSEWHEEL + ActionCode::WHEEL_NEG + ActionCode::WHEEL_DEL, "wheel_down");
|
||||
|
||||
// console (` / ~ key) - don't hard code.
|
||||
//registerAction(ActionCode::KEY + sf::Keyboard::Grave, "debug_menu");
|
||||
}
|
||||
|
||||
void PyScene::update()
|
||||
{
|
||||
}
|
||||
|
||||
void PyScene::do_mouse_input(std::string button, std::string type)
|
||||
{
|
||||
auto unscaledmousepos = sf::Mouse::getPosition(game->getWindow());
|
||||
auto mousepos = game->getWindow().mapPixelToCoords(unscaledmousepos);
|
||||
UIDrawable* target;
|
||||
for (auto d: *ui_elements)
|
||||
{
|
||||
target = d->click_at(sf::Vector2f(mousepos));
|
||||
if (target)
|
||||
{
|
||||
/*
|
||||
PyObject* args = Py_BuildValue("(iiss)", (int)mousepos.x, (int)mousepos.y, button.c_str(), type.c_str());
|
||||
PyObject* retval = PyObject_Call(target->click_callable, args, NULL);
|
||||
if (!retval)
|
||||
{
|
||||
std::cout << "click_callable has raised an exception. It's going to STDERR and being dropped:" << std::endl;
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
} else if (retval != Py_None)
|
||||
{
|
||||
std::cout << "click_callable returned a non-None value. It's not an error, it's just not being saved or used." << std::endl;
|
||||
}
|
||||
*/
|
||||
target->click_callable->call(mousepos, button, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PyScene::doAction(std::string name, std::string type)
|
||||
{
|
||||
if (ACTIONPY) {
|
||||
McRFPy_API::doAction(name.substr(0, name.size() - 3));
|
||||
}
|
||||
else if (name.compare("left") == 0 || name.compare("rclick") == 0 || name.compare("wheel_up") == 0 || name.compare("wheel_down") == 0) {
|
||||
do_mouse_input(name, type);
|
||||
}
|
||||
else if ACTIONONCE("debug_menu") {
|
||||
McRFPy_API::REPL();
|
||||
}
|
||||
}
|
||||
|
||||
void PyScene::render()
|
||||
{
|
||||
game->getWindow().clear();
|
||||
|
||||
auto vec = *ui_elements;
|
||||
for (auto e: vec)
|
||||
{
|
||||
if (e)
|
||||
e->render();
|
||||
}
|
||||
|
||||
game->getWindow().display();
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
#include "PyTexture.h"
|
||||
#include "McRFPy_API.h"
|
||||
|
||||
PyTexture::PyTexture(std::string filename, int sprite_w, int sprite_h)
|
||||
: source(filename), sprite_width(sprite_w), sprite_height(sprite_h)
|
||||
{
|
||||
texture = sf::Texture();
|
||||
texture.loadFromFile(source);
|
||||
auto size = texture.getSize();
|
||||
sheet_width = (size.x / sprite_width);
|
||||
sheet_height = (size.y / sprite_height);
|
||||
if (size.x % sprite_width != 0 || size.y % sprite_height != 0)
|
||||
{
|
||||
std::cout << "Warning: Texture `" << source << "` is not an even number of sprite widths or heights across." << std::endl
|
||||
<< "Sprite size given was " << sprite_w << "x" << sprite_h << "px but the file has a resolution of " << sheet_width << "x" << sheet_height << "px." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
sf::Sprite PyTexture::sprite(int index, sf::Vector2f pos, sf::Vector2f s)
|
||||
{
|
||||
int tx = index % sheet_width, ty = index / sheet_width;
|
||||
auto ir = sf::IntRect(tx * sprite_width, ty * sprite_height, sprite_width, sprite_height);
|
||||
auto sprite = sf::Sprite(texture, ir);
|
||||
sprite.setPosition(pos);
|
||||
sprite.setScale(s);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
PyObject* PyTexture::pyObject()
|
||||
{
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture");
|
||||
PyObject* obj = PyTexture::pynew(type, Py_None, Py_None);
|
||||
|
||||
try {
|
||||
((PyTextureObject*)obj)->data = shared_from_this();
|
||||
}
|
||||
catch (std::bad_weak_ptr& e)
|
||||
{
|
||||
std::cout << "Bad weak ptr: shared_from_this() failed in PyTexture::pyObject(); did you create a PyTexture outside of std::make_shared? enjoy your segfault, soon!" << std::endl;
|
||||
}
|
||||
// TODO - shared_from_this will raise an exception if the object does not have a shared pointer. Constructor should be made private; write a factory function
|
||||
return obj;
|
||||
}
|
||||
|
||||
PyObject* PyTexture::repr(PyObject* obj)
|
||||
{
|
||||
PyTextureObject* self = (PyTextureObject*)obj;
|
||||
std::ostringstream ss;
|
||||
if (!self->data)
|
||||
{
|
||||
ss << "<Texture [invalid internal object]>";
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
||||
auto& ptex = *(self->data);
|
||||
ss << "<Texture " << ptex.sheet_height << " rows, " << ptex.sheet_width << " columns; " << ptex.sprite_width << "x" << ptex.sprite_height << "px sprites. source='" << ptex.source << "'>";
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
||||
|
||||
Py_hash_t PyTexture::hash(PyObject* obj)
|
||||
{
|
||||
auto self = (PyTextureObject*)obj;
|
||||
return reinterpret_cast<Py_hash_t>(self->data.get());
|
||||
}
|
||||
|
||||
int PyTexture::init(PyTextureObject* self, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
static const char* keywords[] = { "filename", "sprite_width", "sprite_height", nullptr };
|
||||
char* filename;
|
||||
int sprite_width, sprite_height;
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "sii", const_cast<char**>(keywords), &filename, &sprite_width, &sprite_height))
|
||||
return -1;
|
||||
self->data = std::make_shared<PyTexture>(filename, sprite_width, sprite_height);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* PyTexture::pynew(PyTypeObject* type, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
return (PyObject*)type->tp_alloc(type, 0);
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Python.h"
|
||||
|
||||
class PyTexture;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
std::shared_ptr<PyTexture> data;
|
||||
} PyTextureObject;
|
||||
|
||||
class PyTexture : public std::enable_shared_from_this<PyTexture>
|
||||
{
|
||||
private:
|
||||
sf::Texture texture;
|
||||
std::string source;
|
||||
int sheet_width, sheet_height;
|
||||
public:
|
||||
int sprite_width, sprite_height; // just use them read only, OK?
|
||||
PyTexture(std::string filename, int sprite_w, int sprite_h);
|
||||
sf::Sprite sprite(int index, sf::Vector2f pos = sf::Vector2f(0, 0), sf::Vector2f s = sf::Vector2f(1.0, 1.0));
|
||||
|
||||
PyObject* pyObject();
|
||||
static PyObject* repr(PyObject*);
|
||||
static Py_hash_t hash(PyObject*);
|
||||
static int init(PyTextureObject*, PyObject*, PyObject*);
|
||||
static PyObject* pynew(PyTypeObject* type, PyObject* args=NULL, PyObject* kwds=NULL);
|
||||
};
|
||||
|
||||
namespace mcrfpydef {
|
||||
static PyTypeObject PyTextureType = {
|
||||
.tp_name = "mcrfpy.Texture",
|
||||
.tp_basicsize = sizeof(PyTextureObject),
|
||||
.tp_itemsize = 0,
|
||||
.tp_repr = PyTexture::repr,
|
||||
.tp_hash = PyTexture::hash,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = PyDoc_STR("SFML Texture Object"),
|
||||
//.tp_base = &PyBaseObject_Type,
|
||||
.tp_init = (initproc)PyTexture::init,
|
||||
.tp_new = PyType_GenericNew, //PyTexture::pynew,
|
||||
};
|
||||
}
|
124
src/PyVector.cpp
124
src/PyVector.cpp
|
@ -1,124 +0,0 @@
|
|||
#include "PyVector.h"
|
||||
|
||||
PyGetSetDef PyVector::getsetters[] = {
|
||||
{"x", (getter)PyVector::get_member, (setter)PyVector::set_member, "X/horizontal component", (void*)0},
|
||||
{"y", (getter)PyVector::get_member, (setter)PyVector::set_member, "Y/vertical component", (void*)1},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyVector::PyVector(sf::Vector2f target)
|
||||
:data(target) {}
|
||||
|
||||
PyObject* PyVector::pyObject()
|
||||
{
|
||||
PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyVectorType, 0);
|
||||
Py_INCREF(obj);
|
||||
PyVectorObject* self = (PyVectorObject*)obj;
|
||||
self->data = data;
|
||||
return obj;
|
||||
}
|
||||
|
||||
sf::Vector2f PyVector::fromPy(PyObject* obj)
|
||||
{
|
||||
PyVectorObject* self = (PyVectorObject*)obj;
|
||||
return self->data;
|
||||
}
|
||||
|
||||
sf::Vector2f PyVector::fromPy(PyVectorObject* self)
|
||||
{
|
||||
return self->data;
|
||||
}
|
||||
|
||||
Py_hash_t PyVector::hash(PyObject* obj)
|
||||
{
|
||||
auto self = (PyVectorObject*)obj;
|
||||
Py_hash_t value = 0;
|
||||
value += self->data.x;
|
||||
value << 8; value += self->data.y;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
PyObject* PyVector::repr(PyObject* obj)
|
||||
{
|
||||
PyVectorObject* self = (PyVectorObject*)obj;
|
||||
std::ostringstream ss;
|
||||
sf::Vector2f v = self->data;
|
||||
ss << "<Vector (" << v.x << ", " << v.y << ")>";
|
||||
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
||||
|
||||
|
||||
int PyVector::init(PyVectorObject* self, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
using namespace mcrfpydef;
|
||||
static const char* keywords[] = { "x", "y", nullptr };
|
||||
PyObject* leader = NULL;
|
||||
float x=0, y=0;
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Of", const_cast<char**>(keywords), &leader, &y))
|
||||
{
|
||||
//PyErr_SetString(PyExc_TypeError, "mcrfpy.Vector requires a 2-tuple or two numeric values");
|
||||
return -1;
|
||||
}
|
||||
if (leader == NULL || leader == Py_None)
|
||||
{
|
||||
self->data = sf::Vector2f();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (PyTuple_Check(leader))
|
||||
{
|
||||
if (PyTuple_Size(leader) != 2)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "Invalid tuple length: mcrfpy.Vector requires a 2-tuple");
|
||||
return -1;
|
||||
}
|
||||
x = PyFloat_AsDouble(PyTuple_GetItem(leader, 0));
|
||||
y = PyFloat_AsDouble(PyTuple_GetItem(leader, 1));
|
||||
|
||||
self->data = sf::Vector2f(x, y);
|
||||
return 0;
|
||||
}
|
||||
// else -
|
||||
else if (!PyFloat_Check(leader) && !(PyLong_Check(leader)))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "mcrfpy.Vector requires a 2-tuple or two numeric values");
|
||||
return -1;
|
||||
}
|
||||
if (PyFloat_Check(leader)) x = PyFloat_AsDouble(leader);
|
||||
else x = PyLong_AsDouble(leader);
|
||||
self->data = sf::Vector2f(x, y);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* PyVector::pynew(PyTypeObject* type, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
return (PyObject*)type->tp_alloc(type, 0);
|
||||
}
|
||||
|
||||
PyObject* PyVector::get_member(PyObject* obj, void* closure)
|
||||
{
|
||||
// TODO
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
int PyVector::set_member(PyObject* obj, PyObject* value, void* closure)
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyVectorObject* PyVector::from_arg(PyObject* args)
|
||||
{
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
|
||||
if (PyObject_IsInstance(args, (PyObject*)type)) return (PyVectorObject*)args;
|
||||
auto obj = (PyVectorObject*)type->tp_alloc(type, 0);
|
||||
int err = init(obj, args, NULL);
|
||||
if (err) {
|
||||
Py_DECREF(obj);
|
||||
return NULL;
|
||||
}
|
||||
return obj;
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Python.h"
|
||||
#include "McRFPy_API.h"
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
sf::Vector2f data;
|
||||
} PyVectorObject;
|
||||
|
||||
class PyVector
|
||||
{
|
||||
public:
|
||||
sf::Vector2f data;
|
||||
PyVector(sf::Vector2f);
|
||||
PyVector();
|
||||
PyObject* pyObject();
|
||||
static sf::Vector2f fromPy(PyObject*);
|
||||
static sf::Vector2f fromPy(PyVectorObject*);
|
||||
static PyObject* repr(PyObject*);
|
||||
static Py_hash_t hash(PyObject*);
|
||||
static int init(PyVectorObject*, PyObject*, PyObject*);
|
||||
static PyObject* pynew(PyTypeObject* type, PyObject* args=NULL, PyObject* kwds=NULL);
|
||||
static PyObject* get_member(PyObject*, void*);
|
||||
static int set_member(PyObject*, PyObject*, void*);
|
||||
static PyVectorObject* from_arg(PyObject*);
|
||||
|
||||
static PyGetSetDef getsetters[];
|
||||
};
|
||||
|
||||
namespace mcrfpydef {
|
||||
static PyTypeObject PyVectorType = {
|
||||
.tp_name = "mcrfpy.Vector",
|
||||
.tp_basicsize = sizeof(PyVectorObject),
|
||||
.tp_itemsize = 0,
|
||||
.tp_repr = PyVector::repr,
|
||||
.tp_hash = PyVector::hash,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = PyDoc_STR("SFML Vector Object"),
|
||||
.tp_getset = PyVector::getsetters,
|
||||
.tp_init = (initproc)PyVector::init,
|
||||
.tp_new = PyVector::pynew,
|
||||
};
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -1,8 +1,7 @@
|
|||
#include "Resources.h"
|
||||
//#include <list>
|
||||
//#include "UI.h"
|
||||
#include <list>
|
||||
#include "UI.h"
|
||||
// Resources class members memory allocation
|
||||
|
||||
sf::Font Resources::font;
|
||||
GameEngine* Resources::game;
|
||||
std::string Resources::caption_buffer;
|
||||
GameEngine* Resources::game;
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
//#include <list>
|
||||
//#include "UI.h"
|
||||
#include <list>
|
||||
#include "UI.h"
|
||||
|
||||
class GameEngine; // forward declared
|
||||
|
||||
|
@ -10,5 +10,4 @@ class Resources
|
|||
public:
|
||||
static sf::Font font;
|
||||
static GameEngine* game;
|
||||
static std::string caption_buffer;
|
||||
};
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
//Scene::Scene() { game = 0; std::cout << "WARN: default Scene constructor called. (game = " << game << ")" << std::endl;};
|
||||
Scene::Scene(GameEngine* g)
|
||||
{
|
||||
key_callable = std::make_unique<PyKeyCallable>();
|
||||
game = g;
|
||||
ui_elements = std::make_shared<std::vector<std::shared_ptr<UIDrawable>>>();
|
||||
}
|
||||
|
@ -40,27 +39,3 @@ bool Scene::unregisterActionInjected(int code, std::string name)
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Scene::key_register(PyObject* callable)
|
||||
{
|
||||
/*
|
||||
if (key_callable)
|
||||
{
|
||||
// decrement reference before overwriting
|
||||
Py_DECREF(key_callable);
|
||||
}
|
||||
key_callable = callable;
|
||||
Py_INCREF(key_callable);
|
||||
*/
|
||||
key_callable = std::make_unique<PyKeyCallable>(callable);
|
||||
}
|
||||
|
||||
void Scene::key_unregister()
|
||||
{
|
||||
/*
|
||||
if (key_callable == NULL) return;
|
||||
Py_DECREF(key_callable);
|
||||
key_callable = NULL;
|
||||
*/
|
||||
key_callable.reset();
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include "Common.h"
|
||||
#include <list>
|
||||
#include "UI.h"
|
||||
#include "PyCallable.h"
|
||||
//#include "GameEngine.h"
|
||||
|
||||
class GameEngine; // forward declare
|
||||
|
@ -31,7 +30,7 @@ public:
|
|||
//Scene();
|
||||
Scene(GameEngine*);
|
||||
virtual void update() = 0;
|
||||
virtual void render() = 0;
|
||||
virtual void sRender() = 0;
|
||||
virtual void doAction(std::string, std::string) = 0;
|
||||
bool hasAction(std::string);
|
||||
bool hasAction(int);
|
||||
|
@ -42,8 +41,4 @@ public:
|
|||
|
||||
std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>> ui_elements;
|
||||
|
||||
//PyObject* key_callable;
|
||||
std::unique_ptr<PyKeyCallable> key_callable;
|
||||
void key_register(PyObject*);
|
||||
void key_unregister();
|
||||
};
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
#include "Timer.h"
|
||||
|
||||
Timer::Timer(PyObject* _target, int _interval, int now)
|
||||
: target(_target), interval(_interval), last_ran(now)
|
||||
{}
|
||||
|
||||
Timer::Timer()
|
||||
: target(Py_None), interval(0), last_ran(0)
|
||||
{}
|
||||
|
||||
bool Timer::test(int now)
|
||||
{
|
||||
if (!target || target == Py_None) return false;
|
||||
if (now > last_ran + interval)
|
||||
{
|
||||
last_ran = now;
|
||||
PyObject* args = Py_BuildValue("(i)", now);
|
||||
PyObject* retval = PyObject_Call(target, args, NULL);
|
||||
if (!retval)
|
||||
{
|
||||
std::cout << "timer has raised an exception. It's going to STDERR and being dropped:" << std::endl;
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
} else if (retval != Py_None)
|
||||
{
|
||||
std::cout << "timer returned a non-None value. It's not an error, it's just not being saved or used." << std::endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
15
src/Timer.h
15
src/Timer.h
|
@ -1,15 +0,0 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Python.h"
|
||||
class GameEngine; // forward declare
|
||||
|
||||
class Timer
|
||||
{
|
||||
public:
|
||||
PyObject* target;
|
||||
int interval;
|
||||
int last_ran;
|
||||
Timer(); // for map to build
|
||||
Timer(PyObject*, int, int);
|
||||
bool test(int);
|
||||
};
|
|
@ -0,0 +1,237 @@
|
|||
#include "UI.h"
|
||||
#include "Resources.h"
|
||||
#include "GameEngine.h"
|
||||
|
||||
void UIDrawable::render()
|
||||
{
|
||||
//std::cout << "Rendering base UIDrawable\n";
|
||||
render(sf::Vector2f());
|
||||
}
|
||||
UIFrame::UIFrame():
|
||||
x(0), y(0), w(0), h(0), outline(0)
|
||||
{
|
||||
children = std::make_shared<std::vector<std::shared_ptr<UIDrawable>>>();
|
||||
/*
|
||||
pyOutlineColor = NULL;
|
||||
pyFillColor = NULL;
|
||||
_outlineColor = NULL;
|
||||
_fillColor = NULL;
|
||||
*/
|
||||
}
|
||||
|
||||
UIFrame::UIFrame(float _x, float _y, float _w, float _h):
|
||||
x(_x), y(_y), w(_w), h(_h), outline(0)
|
||||
{
|
||||
children = std::make_shared<std::vector<std::shared_ptr<UIDrawable>>>();
|
||||
/*
|
||||
pyOutlineColor = NULL;
|
||||
pyFillColor = NULL;
|
||||
_outlineColor = NULL;
|
||||
_fillColor = NULL;
|
||||
*/
|
||||
}
|
||||
|
||||
UIFrame::~UIFrame()
|
||||
{
|
||||
children.reset();
|
||||
/*
|
||||
if (pyOutlineColor) Py_DECREF(pyOutlineColor);
|
||||
else if (_outlineColor) delete _outlineColor;
|
||||
if (pyFillColor) Py_DECREF(pyFillColor);
|
||||
else if (_fillColor) delete _fillColor;
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
sf::Color& fillColor(); // getter
|
||||
void fillColor(sf::Color c); // C++ setter
|
||||
void fillColor(PyObject* pyColor); // Python setter
|
||||
|
||||
sf::Color& outlineColor(); // getter
|
||||
void outlineColor(sf::Color c); // C++ setter
|
||||
void outlineColor(PyObject* pyColor); // Python setter
|
||||
*/
|
||||
|
||||
PyObjectsEnum UIFrame::derived_type()
|
||||
{
|
||||
return PyObjectsEnum::UIFRAME;
|
||||
}
|
||||
|
||||
void UIFrame::render(sf::Vector2f offset)
|
||||
{
|
||||
//std::cout << "Rendering UIFrame w/ offset " << offset.x << ", " << offset.y << "\n";
|
||||
//std::cout << "position = " << x << ", " << y << "\n";
|
||||
box.move(offset);
|
||||
Resources::game->getWindow().draw(box);
|
||||
box.move(-offset);
|
||||
//sf::RectangleShape box = sf::RectangleShape(sf::Vector2f(w,h));
|
||||
//sf::Vector2f pos = sf::Vector2f(x, y);
|
||||
//box.setPosition(offset + pos);
|
||||
//if (_fillColor) { box.setFillColor(fillColor()); }
|
||||
//if (_outlineColor) { box.setOutlineColor(outlineColor()); }
|
||||
//box.setOutlineThickness(outline);
|
||||
//Resources::game->getWindow().draw(box);
|
||||
for (auto drawable : *children) {
|
||||
drawable->render(offset + box.getPosition());
|
||||
}
|
||||
}
|
||||
|
||||
void UICaption::render(sf::Vector2f offset)
|
||||
{
|
||||
//std::cout << "Rendering Caption with offset\n";
|
||||
text.move(offset);
|
||||
Resources::game->getWindow().draw(text);
|
||||
text.move(-offset);
|
||||
}
|
||||
|
||||
UISprite::UISprite() {}
|
||||
|
||||
UISprite::UISprite(IndexTexture* _itex, int _sprite_index, float x = 0.0, float y = 0.0, float s = 1.0)
|
||||
: itex(_itex), sprite_index(_sprite_index)
|
||||
{
|
||||
sprite.setTexture(_itex->texture);
|
||||
sprite.setTextureRect(_itex->spriteCoordinates(_sprite_index));
|
||||
sprite.setPosition(sf::Vector2f(x, y));
|
||||
sprite.setScale(sf::Vector2f(s, s));
|
||||
}
|
||||
|
||||
UISprite::UISprite(IndexTexture* _itex, int _sprite_index, sf::Vector2f pos, float s = 1.0)
|
||||
: itex(_itex), sprite_index(_sprite_index)
|
||||
{
|
||||
sprite.setTexture(_itex->texture);
|
||||
sprite.setTextureRect(_itex->spriteCoordinates(_sprite_index));
|
||||
sprite.setPosition(pos);
|
||||
sprite.setScale(sf::Vector2f(s, s));
|
||||
}
|
||||
|
||||
//void UISprite::update()
|
||||
//{
|
||||
//auto& tex = Resources::game->textures[texture_index];
|
||||
//sprite.setTexture(tex.texture);
|
||||
//sprite.setScale(sf::Vector2f(scale, scale));
|
||||
//sprite.setPosition(sf::Vector2f(x, y));
|
||||
//std::cout << "Drawable position: " << x << ", " << y << " -> " << s.getPosition().x << ", " << s.getPosition().y << std::endl;
|
||||
//sprite.setTextureRect(tex.spriteCoordinates(sprite_index));
|
||||
//}
|
||||
|
||||
void UISprite::render(sf::Vector2f offset)
|
||||
{
|
||||
sprite.move(offset);
|
||||
Resources::game->getWindow().draw(sprite);
|
||||
sprite.move(-offset);
|
||||
}
|
||||
|
||||
void UISprite::setPosition(float x, float y)
|
||||
{
|
||||
setPosition(sf::Vector2f(x, y));
|
||||
}
|
||||
|
||||
void UISprite::setPosition(sf::Vector2f pos)
|
||||
{
|
||||
sprite.setPosition(pos);
|
||||
}
|
||||
|
||||
void UISprite::setScale(float s)
|
||||
{
|
||||
sprite.setScale(sf::Vector2f(s, s));
|
||||
}
|
||||
|
||||
PyObjectsEnum UICaption::derived_type()
|
||||
{
|
||||
return PyObjectsEnum::UICAPTION;
|
||||
}
|
||||
|
||||
PyObjectsEnum UISprite::derived_type()
|
||||
{
|
||||
return PyObjectsEnum::UISPRITE;
|
||||
}
|
||||
|
||||
PyObject* DEFUNCT_py_instance(std::shared_ptr<UIDrawable> source)
|
||||
{
|
||||
// takes a UI drawable, calls its derived_type virtual function, and builds a Python object based on the return value.
|
||||
using namespace mcrfpydef;
|
||||
|
||||
PyObject* newobj = NULL;
|
||||
std::cout << "py_instance called\n";
|
||||
switch (source->derived_type())
|
||||
{
|
||||
case PyObjectsEnum::UIFRAME:
|
||||
{
|
||||
std::cout << "UIFRAME case\n" << std::flush;
|
||||
PyTypeObject* UIFrameType = &PyUIFrameType;
|
||||
//std::cout << "tp_alloc\n" << std::flush;
|
||||
//PyObject* _o = UIFrameType->tp_alloc(UIFrameType, 0);
|
||||
//std::cout << "reinterpret_cast\n" << std::flush;
|
||||
//auto o = reinterpret_cast<PyUICollectionObject*>(_o);
|
||||
//PyUIFrameObject* o = (PyUIFrameObject*)PyObject_New(PyUIFrameObject, UIFrameType);
|
||||
|
||||
PyUIFrameObject* o = (PyUIFrameObject*)(UIFrameType->tp_alloc(UIFrameType, 0));
|
||||
//PyUIFrameObject* o = PyObject_New(PyUIFrameObject, UIFrameType);
|
||||
|
||||
/*
|
||||
// backtracking the problem: instantiate a PyColor of (255, 0, 0) for testing
|
||||
PyTypeObject* colorType = &PyColorType;
|
||||
PyObject* pyColor = colorType->tp_alloc(colorType, 0);
|
||||
if (pyColor == NULL)
|
||||
{
|
||||
std::cout << "failure to allocate mcrfpy.Color / PyColorType" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
PyColorObject* pyColorObj = reinterpret_cast<PyColorObject*>(pyColor);
|
||||
pyColorObj->data = std::make_shared<sf::Color>();
|
||||
pyColorObj->data-> r = 255;
|
||||
return (PyObject*)pyColorObj;
|
||||
*/
|
||||
|
||||
|
||||
std::cout << "pointer check: " << o << "\n" << std::flush;
|
||||
if (o)
|
||||
{
|
||||
std::cout << "Casting data...\n" << std::flush;
|
||||
auto p = std::static_pointer_cast<UIFrame>(source);
|
||||
std::cout << "casted. Assigning...\n" << std::flush;
|
||||
//o->data = std::make_shared<UIFrame>();
|
||||
|
||||
o->data = p;
|
||||
//std::cout << "assigned.\n" << std::flush;
|
||||
auto usource = o->data; //(UIFrame*)source.get();
|
||||
std::cout << "Loaded data into object. " << usource->box.getPosition().x << " " << usource->box.getPosition().y << " " <<
|
||||
usource->box.getSize().x << " " << usource->box.getSize().y << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Allocation failed.\n" << std::flush;
|
||||
}
|
||||
newobj = (PyObject*)o;
|
||||
break;
|
||||
|
||||
}
|
||||
case PyObjectsEnum::UICAPTION:
|
||||
{
|
||||
std::cout << "UICAPTION case\n";
|
||||
PyErr_SetString(PyExc_NotImplementedError, "UICaption class not implemented in Python yet.");
|
||||
/* not yet implemented
|
||||
PyUICaptionObject* o = (PyUICaptionObject*)PyUICaptionType.tp_alloc(&PyUICaptionType, 0);
|
||||
if (o)
|
||||
o->data = std::static_pointer_cast<UICaption>(source);
|
||||
*/
|
||||
break;
|
||||
}
|
||||
case PyObjectsEnum::UISPRITE:
|
||||
{
|
||||
std::cout << "UISPRITE case\n";
|
||||
PyErr_SetString(PyExc_NotImplementedError, "UISprite class not implemented in Python yet.");
|
||||
/*
|
||||
PyUISpriteObject* o = (PyUISpriteObject*)PyUISpriteType.tp_alloc(&PyUISpriteType, 0);
|
||||
if (o)
|
||||
o->data = std::static_pointer_cast<UISprite>(source);
|
||||
*/
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return newobj;
|
||||
}
|
32
src/UIBase.h
32
src/UIBase.h
|
@ -1,32 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
class UIEntity;
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
std::shared_ptr<UIEntity> data;
|
||||
} PyUIEntityObject;
|
||||
|
||||
class UIFrame;
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
std::shared_ptr<UIFrame> data;
|
||||
} PyUIFrameObject;
|
||||
|
||||
class UICaption;
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
std::shared_ptr<UICaption> data;
|
||||
PyObject* font;
|
||||
} PyUICaptionObject;
|
||||
|
||||
class UIGrid;
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
std::shared_ptr<UIGrid> data;
|
||||
} PyUIGridObject;
|
||||
|
||||
class UISprite;
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
std::shared_ptr<UISprite> data;
|
||||
} PyUISpriteObject;
|
|
@ -1,296 +0,0 @@
|
|||
#include "UICaption.h"
|
||||
#include "GameEngine.h"
|
||||
#include "PyColor.h"
|
||||
#include "PyVector.h"
|
||||
#include "PyFont.h"
|
||||
|
||||
UIDrawable* UICaption::click_at(sf::Vector2f point)
|
||||
{
|
||||
if (click_callable)
|
||||
{
|
||||
if (text.getGlobalBounds().contains(point)) return this;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void UICaption::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||
{
|
||||
text.move(offset);
|
||||
//Resources::game->getWindow().draw(text);
|
||||
target.draw(text);
|
||||
text.move(-offset);
|
||||
}
|
||||
|
||||
PyObjectsEnum UICaption::derived_type()
|
||||
{
|
||||
return PyObjectsEnum::UICAPTION;
|
||||
}
|
||||
|
||||
PyObject* UICaption::get_float_member(PyUICaptionObject* self, void* closure)
|
||||
{
|
||||
auto member_ptr = reinterpret_cast<long>(closure);
|
||||
if (member_ptr == 0)
|
||||
return PyFloat_FromDouble(self->data->text.getPosition().x);
|
||||
else if (member_ptr == 1)
|
||||
return PyFloat_FromDouble(self->data->text.getPosition().y);
|
||||
else if (member_ptr == 4)
|
||||
return PyFloat_FromDouble(self->data->text.getOutlineThickness());
|
||||
else if (member_ptr == 5)
|
||||
return PyLong_FromLong(self->data->text.getCharacterSize());
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int UICaption::set_float_member(PyUICaptionObject* self, PyObject* value, void* closure)
|
||||
{
|
||||
float val;
|
||||
auto member_ptr = reinterpret_cast<long>(closure);
|
||||
if (PyFloat_Check(value))
|
||||
{
|
||||
val = PyFloat_AsDouble(value);
|
||||
}
|
||||
else if (PyLong_Check(value))
|
||||
{
|
||||
val = PyLong_AsLong(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "Value must be an integer.");
|
||||
return -1;
|
||||
}
|
||||
if (member_ptr == 0) //x
|
||||
self->data->text.setPosition(val, self->data->text.getPosition().y);
|
||||
else if (member_ptr == 1) //y
|
||||
self->data->text.setPosition(self->data->text.getPosition().x, val);
|
||||
else if (member_ptr == 4) //outline
|
||||
self->data->text.setOutlineThickness(val);
|
||||
else if (member_ptr == 5) // character size
|
||||
self->data->text.setCharacterSize(val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* UICaption::get_vec_member(PyUICaptionObject* self, void* closure)
|
||||
{
|
||||
return PyVector(self->data->text.getPosition()).pyObject();
|
||||
}
|
||||
|
||||
int UICaption::set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure)
|
||||
{
|
||||
self->data->text.setPosition(PyVector::fromPy(value));
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* UICaption::get_color_member(PyUICaptionObject* self, void* closure)
|
||||
{
|
||||
// TODO: migrate this code to a switch statement - validate closure & return values in one tighter, more extensible structure
|
||||
|
||||
// validate closure (should be impossible to be wrong, but it's thorough)
|
||||
auto member_ptr = reinterpret_cast<long>(closure);
|
||||
if (member_ptr != 0 && member_ptr != 1)
|
||||
{
|
||||
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO: manually calling tp_alloc to create a PyColorObject seems like an antipattern
|
||||
// fetch correct member data
|
||||
sf::Color color;
|
||||
|
||||
if (member_ptr == 0)
|
||||
{
|
||||
color = self->data->text.getFillColor();
|
||||
}
|
||||
else if (member_ptr == 1)
|
||||
{
|
||||
color = self->data->text.getOutlineColor();
|
||||
}
|
||||
|
||||
return PyColor(color).pyObject();
|
||||
}
|
||||
|
||||
int UICaption::set_color_member(PyUICaptionObject* self, PyObject* value, void* closure)
|
||||
{
|
||||
auto member_ptr = reinterpret_cast<long>(closure);
|
||||
//TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse
|
||||
int r, g, b, a;
|
||||
if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color") /*(PyObject*)&mcrfpydef::PyColorType)*/))
|
||||
{
|
||||
// get value from mcrfpy.Color instance
|
||||
auto c = ((PyColorObject*)value)->data;
|
||||
r = c.r; g = c.g; b = c.b; a = c.a;
|
||||
std::cout << "got " << int(r) << ", " << int(g) << ", " << int(b) << ", " << int(a) << std::endl;
|
||||
}
|
||||
else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4)
|
||||
{
|
||||
// reject non-Color, non-tuple value
|
||||
PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object.");
|
||||
return -1;
|
||||
}
|
||||
else // get value from tuples
|
||||
{
|
||||
r = PyLong_AsLong(PyTuple_GetItem(value, 0));
|
||||
g = PyLong_AsLong(PyTuple_GetItem(value, 1));
|
||||
b = PyLong_AsLong(PyTuple_GetItem(value, 2));
|
||||
a = 255;
|
||||
|
||||
if (PyTuple_Size(value) == 4)
|
||||
{
|
||||
a = PyLong_AsLong(PyTuple_GetItem(value, 3));
|
||||
}
|
||||
}
|
||||
|
||||
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (member_ptr == 0)
|
||||
{
|
||||
self->data->text.setFillColor(sf::Color(r, g, b, a));
|
||||
}
|
||||
else if (member_ptr == 1)
|
||||
{
|
||||
self->data->text.setOutlineColor(sf::Color(r, g, b, a));
|
||||
}
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//TODO: evaluate use of Resources::caption_buffer... can't I do this with a std::string?
|
||||
PyObject* UICaption::get_text(PyUICaptionObject* self, void* closure)
|
||||
{
|
||||
Resources::caption_buffer = self->data->text.getString();
|
||||
return PyUnicode_FromString(Resources::caption_buffer.c_str());
|
||||
}
|
||||
|
||||
int UICaption::set_text(PyUICaptionObject* self, PyObject* value, void* closure)
|
||||
{
|
||||
PyObject* s = PyObject_Str(value);
|
||||
PyObject * temp_bytes = PyUnicode_AsEncodedString(s, "UTF-8", "strict"); // Owned reference
|
||||
if (temp_bytes != NULL) {
|
||||
Resources::caption_buffer = PyBytes_AS_STRING(temp_bytes); // Borrowed pointer
|
||||
Py_DECREF(temp_bytes);
|
||||
}
|
||||
self->data->text.setString(Resources::caption_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyGetSetDef UICaption::getsetters[] = {
|
||||
{"x", (getter)UICaption::get_float_member, (setter)UICaption::set_float_member, "X coordinate of top-left corner", (void*)0},
|
||||
{"y", (getter)UICaption::get_float_member, (setter)UICaption::set_float_member, "Y coordinate of top-left corner", (void*)1},
|
||||
{"pos", (getter)UICaption::get_vec_member, (setter)UICaption::set_vec_member, "(x, y) vector", (void*)0},
|
||||
//{"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2},
|
||||
//{"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3},
|
||||
{"outline", (getter)UICaption::get_float_member, (setter)UICaption::set_float_member, "Thickness of the border", (void*)4},
|
||||
{"fill_color", (getter)UICaption::get_color_member, (setter)UICaption::set_color_member, "Fill color of the text", (void*)0},
|
||||
{"outline_color", (getter)UICaption::get_color_member, (setter)UICaption::set_color_member, "Outline color of the text", (void*)1},
|
||||
//{"children", (getter)PyUIFrame_get_children, NULL, "UICollection of objects on top of this one", NULL},
|
||||
{"text", (getter)UICaption::get_text, (setter)UICaption::set_text, "The text displayed", NULL},
|
||||
{"size", (getter)UICaption::get_float_member, (setter)UICaption::set_float_member, "Text size (integer) in points", (void*)5},
|
||||
{"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UICAPTION},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyObject* UICaption::repr(PyUICaptionObject* self)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
if (!self->data) ss << "<Caption (invalid internal object)>";
|
||||
else {
|
||||
auto text = self->data->text;
|
||||
auto fc = text.getFillColor();
|
||||
auto oc = text.getOutlineColor();
|
||||
ss << "<Caption (x=" << text.getPosition().x << ", y=" << text.getPosition().y << ", " <<
|
||||
"text='" << (std::string)text.getString() << "', " <<
|
||||
"outline=" << text.getOutlineThickness() << ", " <<
|
||||
"fill_color=(" << (int)fc.r << ", " << (int)fc.g << ", " << (int)fc.b << ", " << (int)fc.a <<"), " <<
|
||||
"outline_color=(" << (int)oc.r << ", " << (int)oc.g << ", " << (int)oc.b << ", " << (int)oc.a <<"), " <<
|
||||
")>";
|
||||
}
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
||||
|
||||
int UICaption::init(PyUICaptionObject* self, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
using namespace mcrfpydef;
|
||||
// Constructor switch to Vector position
|
||||
//static const char* keywords[] = { "x", "y", "text", "font", "fill_color", "outline_color", "outline", nullptr };
|
||||
//float x = 0.0f, y = 0.0f, outline = 0.0f;
|
||||
static const char* keywords[] = { "pos", "text", "font", "fill_color", "outline_color", "outline", nullptr };
|
||||
PyObject* pos;
|
||||
float outline = 0.0f;
|
||||
char* text;
|
||||
PyObject* font=NULL, *fill_color=NULL, *outline_color=NULL;
|
||||
|
||||
//if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffzOOOf",
|
||||
// const_cast<char**>(keywords), &x, &y, &text, &font, &fill_color, &outline_color, &outline))
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|zOOOf",
|
||||
const_cast<char**>(keywords), &pos, &text, &font, &fill_color, &outline_color, &outline))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyVectorObject* pos_result = PyVector::from_arg(pos);
|
||||
if (!pos_result)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "pos must be a mcrfpy.Vector instance or arguments to mcrfpy.Vector.__init__");
|
||||
return -1;
|
||||
}
|
||||
self->data->text.setPosition(pos_result->data);
|
||||
// check types for font, fill_color, outline_color
|
||||
|
||||
//std::cout << PyUnicode_AsUTF8(PyObject_Repr(font)) << std::endl;
|
||||
if (font != NULL && !PyObject_IsInstance(font, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Font")/*(PyObject*)&PyFontType)*/)){
|
||||
PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance");
|
||||
return -1;
|
||||
} else if (font != NULL)
|
||||
{
|
||||
auto font_obj = (PyFontObject*)font;
|
||||
self->data->text.setFont(font_obj->data->font);
|
||||
self->font = font;
|
||||
Py_INCREF(font);
|
||||
} else
|
||||
{
|
||||
// default font
|
||||
//self->data->text.setFont(Resources::game->getFont());
|
||||
}
|
||||
|
||||
self->data->text.setString((std::string)text);
|
||||
self->data->text.setOutlineThickness(outline);
|
||||
if (fill_color) {
|
||||
auto fc = PyColor::from_arg(fill_color);
|
||||
if (!fc) {
|
||||
PyErr_SetString(PyExc_TypeError, "fill_color must be mcrfpy.Color or arguments to mcrfpy.Color.__init__");
|
||||
return -1;
|
||||
}
|
||||
self->data->text.setFillColor(PyColor::fromPy(fc));
|
||||
//Py_DECREF(fc);
|
||||
} else {
|
||||
self->data->text.setFillColor(sf::Color(0,0,0,255));
|
||||
}
|
||||
|
||||
if (outline_color) {
|
||||
auto oc = PyColor::from_arg(outline_color);
|
||||
if (!oc) {
|
||||
PyErr_SetString(PyExc_TypeError, "outline_color must be mcrfpy.Color or arguments to mcrfpy.Color.__init__");
|
||||
return -1;
|
||||
}
|
||||
self->data->text.setOutlineColor(PyColor::fromPy(oc));
|
||||
//Py_DECREF(oc);
|
||||
} else {
|
||||
self->data->text.setOutlineColor(sf::Color(128,128,128,255));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Python.h"
|
||||
#include "UIDrawable.h"
|
||||
|
||||
class UICaption: public UIDrawable
|
||||
{
|
||||
public:
|
||||
sf::Text text;
|
||||
void render(sf::Vector2f, sf::RenderTarget&) override final;
|
||||
PyObjectsEnum derived_type() override final;
|
||||
virtual UIDrawable* click_at(sf::Vector2f point) override final;
|
||||
|
||||
static PyObject* get_float_member(PyUICaptionObject* self, void* closure);
|
||||
static int set_float_member(PyUICaptionObject* self, PyObject* value, void* closure);
|
||||
static PyObject* get_vec_member(PyUICaptionObject* self, void* closure);
|
||||
static int set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure);
|
||||
static PyObject* get_color_member(PyUICaptionObject* self, void* closure);
|
||||
static int set_color_member(PyUICaptionObject* self, PyObject* value, void* closure);
|
||||
static PyObject* get_text(PyUICaptionObject* self, void* closure);
|
||||
static int set_text(PyUICaptionObject* self, PyObject* value, void* closure);
|
||||
static PyGetSetDef getsetters[];
|
||||
static PyObject* repr(PyUICaptionObject* self);
|
||||
static int init(PyUICaptionObject* self, PyObject* args, PyObject* kwds);
|
||||
|
||||
};
|
||||
|
||||
namespace mcrfpydef {
|
||||
static PyTypeObject PyUICaptionType = {
|
||||
.tp_name = "mcrfpy.Caption",
|
||||
.tp_basicsize = sizeof(PyUICaptionObject),
|
||||
.tp_itemsize = 0,
|
||||
// TODO - move tp_dealloc to .cpp file as static function (UICaption::dealloc)
|
||||
.tp_dealloc = (destructor)[](PyObject* self)
|
||||
{
|
||||
PyUICaptionObject* obj = (PyUICaptionObject*)self;
|
||||
// TODO - reevaluate with PyFont usage; UICaption does not own the font
|
||||
// release reference to font object
|
||||
if (obj->font) Py_DECREF(obj->font);
|
||||
obj->data.reset();
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
},
|
||||
.tp_repr = (reprfunc)UICaption::repr,
|
||||
//.tp_hash = NULL,
|
||||
//.tp_iter
|
||||
//.tp_iternext
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = PyDoc_STR("docstring"),
|
||||
//.tp_methods = PyUIFrame_methods,
|
||||
//.tp_members = PyUIFrame_members,
|
||||
.tp_getset = UICaption::getsetters,
|
||||
//.tp_base = NULL,
|
||||
.tp_init = (initproc)UICaption::init,
|
||||
// TODO - move tp_new to .cpp file as a static function (UICaption::new)
|
||||
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
|
||||
{
|
||||
PyUICaptionObject* self = (PyUICaptionObject*)type->tp_alloc(type, 0);
|
||||
if (self) self->data = std::make_shared<UICaption>();
|
||||
return (PyObject*)self;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
#include "UICollection.h"
|
||||
|
||||
#include "UIFrame.h"
|
||||
#include "UICaption.h"
|
||||
#include "UISprite.h"
|
||||
#include "UIGrid.h"
|
||||
#include "McRFPy_API.h"
|
||||
|
||||
using namespace mcrfpydef;
|
||||
|
||||
int UICollectionIter::init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject* UICollectionIter::next(PyUICollectionIterObject* self)
|
||||
{
|
||||
if (self->data->size() != self->start_size)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (self->index > self->start_size - 1)
|
||||
{
|
||||
PyErr_SetNone(PyExc_StopIteration);
|
||||
return NULL;
|
||||
}
|
||||
self->index++;
|
||||
auto vec = self->data.get();
|
||||
if (!vec)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer");
|
||||
return NULL;
|
||||
}
|
||||
auto target = (*vec)[self->index-1];
|
||||
// TODO build PyObject* of the correct UIDrawable subclass to return
|
||||
//return py_instance(target);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject* UICollectionIter::repr(PyUICollectionIterObject* self)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
if (!self->data) ss << "<UICollectionIter (invalid internal object)>";
|
||||
else {
|
||||
ss << "<UICollectionIter (" << self->data->size() << " child objects, @ index " << self->index << ")>";
|
||||
}
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
||||
|
||||
Py_ssize_t UICollection::len(PyUICollectionObject* self) {
|
||||
return self->data->size();
|
||||
}
|
||||
|
||||
PyObject* UICollection::getitem(PyUICollectionObject* self, Py_ssize_t index) {
|
||||
// build a Python version of item at self->data[index]
|
||||
// Copy pasted::
|
||||
auto vec = self->data.get();
|
||||
if (!vec)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer");
|
||||
return NULL;
|
||||
}
|
||||
while (index < 0) index += self->data->size();
|
||||
if (index > self->data->size() - 1)
|
||||
{
|
||||
PyErr_SetString(PyExc_IndexError, "UICollection index out of range");
|
||||
return NULL;
|
||||
}
|
||||
auto target = (*vec)[index];
|
||||
RET_PY_INSTANCE(target);
|
||||
return NULL;
|
||||
|
||||
|
||||
}
|
||||
|
||||
PySequenceMethods UICollection::sqmethods = {
|
||||
.sq_length = (lenfunc)UICollection::len,
|
||||
.sq_item = (ssizeargfunc)UICollection::getitem,
|
||||
//.sq_item_by_index = PyUICollection_getitem
|
||||
//.sq_slice - return a subset of the iterable
|
||||
//.sq_ass_item - called when `o[x] = y` is executed (x is any object type)
|
||||
//.sq_ass_slice - cool; no thanks, for now
|
||||
//.sq_contains - called when `x in o` is executed
|
||||
//.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer)
|
||||
};
|
||||
|
||||
/* Idiomatic way to fetch complete types from the API rather than referencing their PyTypeObject struct
|
||||
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture");
|
||||
|
||||
I never identified why `using namespace mcrfpydef;` doesn't solve the segfault issue.
|
||||
The horrible macro in UIDrawable was originally a workaround for this, but as I interact with the types outside of the monster UI.h, a more general (and less icky) solution is required.
|
||||
|
||||
*/
|
||||
|
||||
PyObject* UICollection::append(PyUICollectionObject* self, PyObject* o)
|
||||
{
|
||||
// if not UIDrawable subclass, reject it
|
||||
// self->data->push_back( c++ object inside o );
|
||||
|
||||
// this would be a great use case for .tp_base
|
||||
if (!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) &&
|
||||
!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) &&
|
||||
!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) &&
|
||||
!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))
|
||||
)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "Only Frame, Caption, Sprite, and Grid objects can be added to UICollection");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")))
|
||||
{
|
||||
PyUIFrameObject* frame = (PyUIFrameObject*)o;
|
||||
self->data->push_back(frame->data);
|
||||
}
|
||||
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")))
|
||||
{
|
||||
PyUICaptionObject* caption = (PyUICaptionObject*)o;
|
||||
self->data->push_back(caption->data);
|
||||
}
|
||||
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")))
|
||||
{
|
||||
PyUISpriteObject* sprite = (PyUISpriteObject*)o;
|
||||
self->data->push_back(sprite->data);
|
||||
}
|
||||
if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid")))
|
||||
{
|
||||
PyUIGridObject* grid = (PyUIGridObject*)o;
|
||||
self->data->push_back(grid->data);
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PyObject* UICollection::remove(PyUICollectionObject* self, PyObject* o)
|
||||
{
|
||||
if (!PyLong_Check(o))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove");
|
||||
return NULL;
|
||||
}
|
||||
long index = PyLong_AsLong(o);
|
||||
if (index >= self->data->size())
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "Index out of range");
|
||||
return NULL;
|
||||
}
|
||||
else if (index < 0)
|
||||
{
|
||||
PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// release the shared pointer at self->data[index];
|
||||
self->data->erase(self->data->begin() + index);
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PyMethodDef UICollection::methods[] = {
|
||||
{"append", (PyCFunction)UICollection::append, METH_O},
|
||||
//{"extend", (PyCFunction)PyUICollection_extend, METH_O}, // TODO
|
||||
{"remove", (PyCFunction)UICollection::remove, METH_O},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
PyObject* UICollection::repr(PyUICollectionObject* self)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
if (!self->data) ss << "<UICollection (invalid internal object)>";
|
||||
else {
|
||||
ss << "<UICollection (" << self->data->size() << " child objects)>";
|
||||
}
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
||||
|
||||
int UICollection::init(PyUICollectionObject* self, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject* UICollection::iter(PyUICollectionObject* self)
|
||||
{
|
||||
PyUICollectionIterObject* iterObj;
|
||||
iterObj = (PyUICollectionIterObject*)PyUICollectionIterType.tp_alloc(&PyUICollectionIterType, 0);
|
||||
if (iterObj == NULL) {
|
||||
return NULL; // Failed to allocate memory for the iterator object
|
||||
}
|
||||
|
||||
iterObj->data = self->data;
|
||||
iterObj->index = 0;
|
||||
iterObj->start_size = self->data->size();
|
||||
|
||||
return (PyObject*)iterObj;
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Python.h"
|
||||
|
||||
#include "UIDrawable.h"
|
||||
|
||||
class UICollectionIter
|
||||
{
|
||||
// really more of a namespace: all the members are public and static. But being consistent with other UI objects
|
||||
public:
|
||||
static int init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds);
|
||||
static PyObject* next(PyUICollectionIterObject* self);
|
||||
static PyObject* repr(PyUICollectionIterObject* self);
|
||||
};
|
||||
|
||||
class UICollection
|
||||
{
|
||||
// really more of a namespace: all the members are public and static. But being consistent with other UI objects
|
||||
public:
|
||||
static Py_ssize_t len(PyUICollectionObject* self);
|
||||
static PyObject* getitem(PyUICollectionObject* self, Py_ssize_t index);
|
||||
static PySequenceMethods sqmethods;
|
||||
static PyObject* append(PyUICollectionObject* self, PyObject* o);
|
||||
static PyObject* remove(PyUICollectionObject* self, PyObject* o);
|
||||
static PyMethodDef methods[];
|
||||
static PyObject* repr(PyUICollectionObject* self);
|
||||
static int init(PyUICollectionObject* self, PyObject* args, PyObject* kwds);
|
||||
static PyObject* iter(PyUICollectionObject* self);
|
||||
};
|
||||
|
||||
namespace mcrfpydef {
|
||||
static PyTypeObject PyUICollectionIterType = {
|
||||
//PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "mcrfpy.UICollectionIter",
|
||||
.tp_basicsize = sizeof(PyUICollectionIterObject),
|
||||
.tp_itemsize = 0,
|
||||
//TODO - as static method, not inline lambda def, please
|
||||
.tp_dealloc = (destructor)[](PyObject* self)
|
||||
{
|
||||
PyUICollectionIterObject* obj = (PyUICollectionIterObject*)self;
|
||||
obj->data.reset();
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
},
|
||||
.tp_repr = (reprfunc)UICollectionIter::repr,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = PyDoc_STR("Iterator for a collection of UI objects"),
|
||||
.tp_iternext = (iternextfunc)UICollectionIter::next,
|
||||
//.tp_getset = PyUICollection_getset,
|
||||
.tp_init = (initproc)UICollectionIter::init, // just raise an exception
|
||||
//TODO - as static method, not inline lambda def, please
|
||||
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required.");
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
static PyTypeObject PyUICollectionType = {
|
||||
//PyVarObject_/HEAD_INIT(NULL, 0)
|
||||
.tp_name = "mcrfpy.UICollection",
|
||||
.tp_basicsize = sizeof(PyUICollectionObject),
|
||||
.tp_itemsize = 0,
|
||||
//TODO - as static method, not inline lambda def, please
|
||||
.tp_dealloc = (destructor)[](PyObject* self)
|
||||
{
|
||||
PyUICollectionObject* obj = (PyUICollectionObject*)self;
|
||||
obj->data.reset();
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
},
|
||||
.tp_repr = (reprfunc)UICollection::repr,
|
||||
.tp_as_sequence = &UICollection::sqmethods,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = PyDoc_STR("Iterable, indexable collection of UI objects"),
|
||||
.tp_iter = (getiterfunc)UICollection::iter,
|
||||
.tp_methods = UICollection::methods, // append, remove
|
||||
//.tp_getset = PyUICollection_getset,
|
||||
.tp_init = (initproc)UICollection::init, // just raise an exception
|
||||
//TODO - as static method, not inline lambda def, please
|
||||
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
|
||||
{
|
||||
// Does PyUICollectionType need __new__ if it's not supposed to be instantiable by the user?
|
||||
// Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker?
|
||||
PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required.");
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
#include "UIDrawable.h"
|
||||
#include "UIFrame.h"
|
||||
#include "UICaption.h"
|
||||
#include "UISprite.h"
|
||||
#include "UIGrid.h"
|
||||
#include "GameEngine.h"
|
||||
|
||||
UIDrawable::UIDrawable() { click_callable = NULL; }
|
||||
|
||||
void UIDrawable::click_unregister()
|
||||
{
|
||||
click_callable.reset();
|
||||
}
|
||||
|
||||
void UIDrawable::render()
|
||||
{
|
||||
render(sf::Vector2f(), Resources::game->getWindow());
|
||||
}
|
||||
|
||||
PyObject* UIDrawable::get_click(PyObject* self, void* closure) {
|
||||
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(closure)); // trust me bro, it's an Enum
|
||||
PyObject* ptr;
|
||||
|
||||
switch (objtype)
|
||||
{
|
||||
case PyObjectsEnum::UIFRAME:
|
||||
ptr = ((PyUIFrameObject*)self)->data->click_callable->borrow();
|
||||
break;
|
||||
case PyObjectsEnum::UICAPTION:
|
||||
ptr = ((PyUICaptionObject*)self)->data->click_callable->borrow();
|
||||
break;
|
||||
case PyObjectsEnum::UISPRITE:
|
||||
ptr = ((PyUISpriteObject*)self)->data->click_callable->borrow();
|
||||
break;
|
||||
case PyObjectsEnum::UIGRID:
|
||||
ptr = ((PyUIGridObject*)self)->data->click_callable->borrow();
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _get_click");
|
||||
return NULL;
|
||||
}
|
||||
if (ptr && ptr != Py_None)
|
||||
return ptr;
|
||||
else
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
int UIDrawable::set_click(PyObject* self, PyObject* value, void* closure) {
|
||||
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(closure)); // trust me bro, it's an Enum
|
||||
UIDrawable* target;
|
||||
switch (objtype)
|
||||
{
|
||||
case PyObjectsEnum::UIFRAME:
|
||||
target = (((PyUIFrameObject*)self)->data.get());
|
||||
break;
|
||||
case PyObjectsEnum::UICAPTION:
|
||||
target = (((PyUICaptionObject*)self)->data.get());
|
||||
break;
|
||||
case PyObjectsEnum::UISPRITE:
|
||||
target = (((PyUISpriteObject*)self)->data.get());
|
||||
break;
|
||||
case PyObjectsEnum::UIGRID:
|
||||
target = (((PyUIGridObject*)self)->data.get());
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _set_click");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (value == Py_None)
|
||||
{
|
||||
target->click_unregister();
|
||||
} else {
|
||||
target->click_register(value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UIDrawable::click_register(PyObject* callable)
|
||||
{
|
||||
click_callable = std::make_unique<PyClickCallable>(callable);
|
||||
}
|
177
src/UIDrawable.h
177
src/UIDrawable.h
|
@ -1,177 +0,0 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Python.h"
|
||||
#include "structmember.h"
|
||||
#include "IndexTexture.h"
|
||||
#include "Resources.h"
|
||||
#include <list>
|
||||
|
||||
#include "PyCallable.h"
|
||||
#include "PyTexture.h"
|
||||
#include "PyColor.h"
|
||||
#include "PyVector.h"
|
||||
#include "PyFont.h"
|
||||
|
||||
#include "Resources.h"
|
||||
#include "UIBase.h"
|
||||
class UIFrame; class UICaption; class UISprite; class UIEntity; class UIGrid;
|
||||
|
||||
enum PyObjectsEnum : int
|
||||
{
|
||||
UIFRAME = 1,
|
||||
UICAPTION,
|
||||
UISPRITE,
|
||||
UIGRID
|
||||
};
|
||||
|
||||
class UIDrawable
|
||||
{
|
||||
public:
|
||||
void render();
|
||||
//virtual void render(sf::Vector2f) = 0;
|
||||
virtual void render(sf::Vector2f, sf::RenderTarget&) = 0;
|
||||
virtual PyObjectsEnum derived_type() = 0;
|
||||
|
||||
// Mouse input handling - callable object, methods to find event's destination
|
||||
std::unique_ptr<PyClickCallable> click_callable;
|
||||
virtual UIDrawable* click_at(sf::Vector2f point) = 0;
|
||||
void click_register(PyObject*);
|
||||
void click_unregister();
|
||||
|
||||
UIDrawable();
|
||||
|
||||
static PyObject* get_click(PyObject* self, void* closure);
|
||||
static int set_click(PyObject* self, PyObject* value, void* closure);
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>> data;
|
||||
} PyUICollectionObject;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>> data;
|
||||
int index;
|
||||
int start_size;
|
||||
} PyUICollectionIterObject;
|
||||
|
||||
namespace mcrfpydef {
|
||||
//PyObject* py_instance(std::shared_ptr<UIDrawable> source);
|
||||
// This function segfaults on tp_alloc for an unknown reason, but works inline with mcrfpydef:: methods.
|
||||
|
||||
#define RET_PY_INSTANCE(target) { \
|
||||
switch (target->derived_type()) \
|
||||
{ \
|
||||
case PyObjectsEnum::UIFRAME: \
|
||||
{ \
|
||||
PyUIFrameObject* o = (PyUIFrameObject*)((&PyUIFrameType)->tp_alloc(&PyUIFrameType, 0)); \
|
||||
if (o) \
|
||||
{ \
|
||||
auto p = std::static_pointer_cast<UIFrame>(target); \
|
||||
o->data = p; \
|
||||
auto utarget = o->data; \
|
||||
} \
|
||||
return (PyObject*)o; \
|
||||
} \
|
||||
case PyObjectsEnum::UICAPTION: \
|
||||
{ \
|
||||
PyUICaptionObject* o = (PyUICaptionObject*)((&PyUICaptionType)->tp_alloc(&PyUICaptionType, 0)); \
|
||||
if (o) \
|
||||
{ \
|
||||
auto p = std::static_pointer_cast<UICaption>(target); \
|
||||
o->data = p; \
|
||||
auto utarget = o->data; \
|
||||
} \
|
||||
return (PyObject*)o; \
|
||||
} \
|
||||
case PyObjectsEnum::UISPRITE: \
|
||||
{ \
|
||||
PyUISpriteObject* o = (PyUISpriteObject*)((&PyUISpriteType)->tp_alloc(&PyUISpriteType, 0)); \
|
||||
if (o) \
|
||||
{ \
|
||||
auto p = std::static_pointer_cast<UISprite>(target); \
|
||||
o->data = p; \
|
||||
auto utarget = o->data; \
|
||||
} \
|
||||
return (PyObject*)o; \
|
||||
} \
|
||||
case PyObjectsEnum::UIGRID: \
|
||||
{ \
|
||||
PyUIGridObject* o = (PyUIGridObject*)((&PyUIGridType)->tp_alloc(&PyUIGridType, 0)); \
|
||||
if (o) \
|
||||
{ \
|
||||
auto p = std::static_pointer_cast<UIGrid>(target); \
|
||||
o->data = p; \
|
||||
auto utarget = o->data; \
|
||||
} \
|
||||
return (PyObject*)o; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
// end macro definition
|
||||
|
||||
//TODO: add this method to class scope; move implementation to .cpp file
|
||||
/*
|
||||
static PyObject* PyUIDrawable_get_click(PyObject* self, void* closure) {
|
||||
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(closure)); // trust me bro, it's an Enum
|
||||
PyObject* ptr;
|
||||
|
||||
switch (objtype)
|
||||
{
|
||||
case PyObjectsEnum::UIFRAME:
|
||||
ptr = ((PyUIFrameObject*)self)->data->click_callable->borrow();
|
||||
break;
|
||||
case PyObjectsEnum::UICAPTION:
|
||||
ptr = ((PyUICaptionObject*)self)->data->click_callable->borrow();
|
||||
break;
|
||||
case PyObjectsEnum::UISPRITE:
|
||||
ptr = ((PyUISpriteObject*)self)->data->click_callable->borrow();
|
||||
break;
|
||||
case PyObjectsEnum::UIGRID:
|
||||
ptr = ((PyUIGridObject*)self)->data->click_callable->borrow();
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _get_click");
|
||||
return NULL;
|
||||
}
|
||||
if (ptr && ptr != Py_None)
|
||||
return ptr;
|
||||
else
|
||||
return Py_None;
|
||||
}*/
|
||||
|
||||
//TODO: add this method to class scope; move implementation to .cpp file
|
||||
/*
|
||||
static int PyUIDrawable_set_click(PyObject* self, PyObject* value, void* closure) {
|
||||
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(closure)); // trust me bro, it's an Enum
|
||||
UIDrawable* target;
|
||||
switch (objtype)
|
||||
{
|
||||
case PyObjectsEnum::UIFRAME:
|
||||
target = (((PyUIFrameObject*)self)->data.get());
|
||||
break;
|
||||
case PyObjectsEnum::UICAPTION:
|
||||
target = (((PyUICaptionObject*)self)->data.get());
|
||||
break;
|
||||
case PyObjectsEnum::UISPRITE:
|
||||
target = (((PyUISpriteObject*)self)->data.get());
|
||||
break;
|
||||
case PyObjectsEnum::UIGRID:
|
||||
target = (((PyUIGridObject*)self)->data.get());
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _set_click");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (value == Py_None)
|
||||
{
|
||||
target->click_unregister();
|
||||
} else {
|
||||
target->click_register(value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
}
|
210
src/UIEntity.cpp
210
src/UIEntity.cpp
|
@ -1,210 +0,0 @@
|
|||
#include "UIEntity.h"
|
||||
#include "UIGrid.h"
|
||||
#include "McRFPy_API.h"
|
||||
|
||||
UIEntity::UIEntity() {} // this will not work lol. TODO remove default constructor by finding the shared pointer inits that use it
|
||||
|
||||
UIEntity::UIEntity(UIGrid& grid)
|
||||
: gridstate(grid.grid_x * grid.grid_y)
|
||||
{
|
||||
}
|
||||
|
||||
PyObject* UIEntity::at(PyUIEntityObject* self, PyObject* o) {
|
||||
int x, y;
|
||||
if (!PyArg_ParseTuple(o, "ii", &x, &y)) {
|
||||
PyErr_SetString(PyExc_TypeError, "UIEntity.at requires two integer arguments: (x, y)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (self->data->grid == NULL) {
|
||||
PyErr_SetString(PyExc_ValueError, "Entity cannot access surroundings because it is not associated with a grid");
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
PyUIGridPointStateObject* obj = (PyUIGridPointStateObject*)((&mcrfpydef::PyUIGridPointStateType)->tp_alloc(&mcrfpydef::PyUIGridPointStateType, 0));
|
||||
*/
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPointState");
|
||||
auto obj = (PyUIGridPointStateObject*)type->tp_alloc(type, 0);
|
||||
//auto target = std::static_pointer_cast<UIEntity>(target);
|
||||
obj->data = &(self->data->gridstate[y + self->data->grid->grid_x * x]);
|
||||
obj->grid = self->data->grid;
|
||||
obj->entity = self->data;
|
||||
return (PyObject*)obj;
|
||||
|
||||
}
|
||||
|
||||
int UIEntity::init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) {
|
||||
//static const char* keywords[] = { "x", "y", "texture", "sprite_index", "grid", nullptr };
|
||||
//float x = 0.0f, y = 0.0f, scale = 1.0f;
|
||||
static const char* keywords[] = { "pos", "texture", "sprite_index", "grid", nullptr };
|
||||
PyObject* pos;
|
||||
float scale = 1.0f;
|
||||
int sprite_index = -1;
|
||||
PyObject* texture = NULL;
|
||||
PyObject* grid = NULL;
|
||||
|
||||
//if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffOi|O",
|
||||
// const_cast<char**>(keywords), &x, &y, &texture, &sprite_index, &grid))
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOi|O",
|
||||
const_cast<char**>(keywords), &pos, &texture, &sprite_index, &grid))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyVectorObject* pos_result = PyVector::from_arg(pos);
|
||||
if (!pos_result)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "pos must be a mcrfpy.Vector instance or arguments to mcrfpy.Vector.__init__");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// check types for texture
|
||||
//
|
||||
// Set Texture
|
||||
//
|
||||
if (texture != NULL && !PyObject_IsInstance(texture, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))){
|
||||
PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance");
|
||||
return -1;
|
||||
} /*else if (texture != NULL) // this section needs to go; texture isn't optional and isn't managed by the UI objects anymore
|
||||
{
|
||||
self->texture = texture;
|
||||
Py_INCREF(texture);
|
||||
} else
|
||||
{
|
||||
// default tex?
|
||||
}*/
|
||||
|
||||
if (grid != NULL && !PyObject_IsInstance(grid, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) {
|
||||
PyErr_SetString(PyExc_TypeError, "grid must be a mcrfpy.Grid instance");
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto pytexture = (PyTextureObject*)texture;
|
||||
if (grid == NULL)
|
||||
self->data = std::make_shared<UIEntity>();
|
||||
else
|
||||
self->data = std::make_shared<UIEntity>(*((PyUIGridObject*)grid)->data);
|
||||
|
||||
// TODO - PyTextureObjects and IndexTextures are a little bit of a mess with shared/unshared pointers
|
||||
self->data->sprite = UISprite(pytexture->data, sprite_index, sf::Vector2f(0,0), 1.0);
|
||||
self->data->position = pos_result->data;
|
||||
if (grid != NULL) {
|
||||
PyUIGridObject* pygrid = (PyUIGridObject*)grid;
|
||||
self->data->grid = pygrid->data;
|
||||
// todone - on creation of Entity with Grid assignment, also append it to the entity list
|
||||
pygrid->data->entities->push_back(self->data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
PyObject* UIEntity::get_spritenumber(PyUIEntityObject* self, void* closure) {
|
||||
return PyLong_FromDouble(self->data->sprite.getSpriteIndex());
|
||||
}
|
||||
|
||||
PyObject* sfVector2f_to_PyObject(sf::Vector2f vector) {
|
||||
return Py_BuildValue("(ff)", vector.x, vector.y);
|
||||
}
|
||||
|
||||
PyObject* sfVector2i_to_PyObject(sf::Vector2i vector) {
|
||||
return Py_BuildValue("(ii)", vector.x, vector.y);
|
||||
}
|
||||
|
||||
sf::Vector2f PyObject_to_sfVector2f(PyObject* obj) {
|
||||
float x, y;
|
||||
if (!PyArg_ParseTuple(obj, "ff", &x, &y)) {
|
||||
return sf::Vector2f(); // TODO / reconsider this default: Return default vector on parse error
|
||||
}
|
||||
return sf::Vector2f(x, y);
|
||||
}
|
||||
|
||||
sf::Vector2i PyObject_to_sfVector2i(PyObject* obj) {
|
||||
int x, y;
|
||||
if (!PyArg_ParseTuple(obj, "ii", &x, &y)) {
|
||||
return sf::Vector2i(); // TODO / reconsider this default: Return default vector on parse error
|
||||
}
|
||||
return sf::Vector2i(x, y);
|
||||
}
|
||||
|
||||
// TODO - deprecate / remove this helper
|
||||
PyObject* UIGridPointState_to_PyObject(const UIGridPointState& state) {
|
||||
return PyObject_New(PyObject, (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPointState"));
|
||||
}
|
||||
|
||||
PyObject* UIGridPointStateVector_to_PyList(const std::vector<UIGridPointState>& vec) {
|
||||
PyObject* list = PyList_New(vec.size());
|
||||
if (!list) return PyErr_NoMemory();
|
||||
|
||||
for (size_t i = 0; i < vec.size(); ++i) {
|
||||
PyObject* obj = UIGridPointState_to_PyObject(vec[i]);
|
||||
if (!obj) { // Cleanup on failure
|
||||
Py_DECREF(list);
|
||||
return NULL;
|
||||
}
|
||||
PyList_SET_ITEM(list, i, obj); // This steals a reference to obj
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
PyObject* UIEntity::get_position(PyUIEntityObject* self, void* closure) {
|
||||
if (reinterpret_cast<long>(closure) == 0) {
|
||||
return sfVector2f_to_PyObject(self->data->position);
|
||||
} else {
|
||||
return sfVector2i_to_PyObject(self->data->collision_pos);
|
||||
}
|
||||
}
|
||||
|
||||
int UIEntity::set_position(PyUIEntityObject* self, PyObject* value, void* closure) {
|
||||
if (reinterpret_cast<long>(closure) == 0) {
|
||||
self->data->position = PyObject_to_sfVector2f(value);
|
||||
} else {
|
||||
self->data->collision_pos = PyObject_to_sfVector2i(value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* UIEntity::get_gridstate(PyUIEntityObject* self, void* closure) {
|
||||
// Assuming a function to convert std::vector<UIGridPointState> to PyObject* list
|
||||
return UIGridPointStateVector_to_PyList(self->data->gridstate);
|
||||
}
|
||||
|
||||
int UIEntity::set_spritenumber(PyUIEntityObject* self, PyObject* value, void* closure) {
|
||||
int val;
|
||||
if (PyLong_Check(value))
|
||||
val = PyLong_AsLong(value);
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "Value must be an integer.");
|
||||
return -1;
|
||||
}
|
||||
//self->data->sprite.sprite_index = val;
|
||||
self->data->sprite.setSpriteIndex(val); // todone - I don't like ".sprite.sprite" in this stack of UIEntity.UISprite.sf::Sprite
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyMethodDef UIEntity::methods[] = {
|
||||
{"at", (PyCFunction)UIEntity::at, METH_O},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
PyGetSetDef UIEntity::getsetters[] = {
|
||||
{"draw_pos", (getter)UIEntity::get_position, (setter)UIEntity::set_position, "Entity position (graphically)", (void*)0},
|
||||
{"pos", (getter)UIEntity::get_position, (setter)UIEntity::set_position, "Entity position (integer grid coordinates)", (void*)1},
|
||||
{"gridstate", (getter)UIEntity::get_gridstate, NULL, "Grid point states for the entity", NULL},
|
||||
{"sprite_number", (getter)UIEntity::get_spritenumber, (setter)UIEntity::set_spritenumber, "Sprite number (index) on the texture on the display", NULL},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
PyObject* UIEntity::repr(PyUIEntityObject* self) {
|
||||
std::ostringstream ss;
|
||||
if (!self->data) ss << "<Entity (invalid internal object)>";
|
||||
else {
|
||||
auto ent = self->data;
|
||||
ss << "<Entity (x=" << self->data->position.x << ", y=" << self->data->position.y << ", sprite_number=" << self->data->sprite.getSpriteIndex() <<
|
||||
")>";
|
||||
}
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Python.h"
|
||||
#include "structmember.h"
|
||||
#include "IndexTexture.h"
|
||||
#include "Resources.h"
|
||||
#include <list>
|
||||
|
||||
#include "PyCallable.h"
|
||||
#include "PyTexture.h"
|
||||
#include "PyColor.h"
|
||||
#include "PyVector.h"
|
||||
#include "PyFont.h"
|
||||
|
||||
#include "UIGridPoint.h"
|
||||
#include "UIDrawable.h"
|
||||
#include "UIBase.h"
|
||||
#include "UISprite.h"
|
||||
|
||||
class UIGrid;
|
||||
|
||||
//class UIEntity;
|
||||
//typedef struct {
|
||||
// PyObject_HEAD
|
||||
// std::shared_ptr<UIEntity> data;
|
||||
//} PyUIEntityObject;
|
||||
|
||||
// helper methods with no namespace requirement
|
||||
static PyObject* sfVector2f_to_PyObject(sf::Vector2f vector);
|
||||
static sf::Vector2f PyObject_to_sfVector2f(PyObject* obj);
|
||||
static PyObject* UIGridPointState_to_PyObject(const UIGridPointState& state);
|
||||
static PyObject* UIGridPointStateVector_to_PyList(const std::vector<UIGridPointState>& vec);
|
||||
|
||||
// TODO: make UIEntity a drawable
|
||||
class UIEntity//: public UIDrawable
|
||||
{
|
||||
public:
|
||||
//PyObject* self;
|
||||
std::shared_ptr<UIGrid> grid;
|
||||
std::vector<UIGridPointState> gridstate;
|
||||
UISprite sprite;
|
||||
sf::Vector2f position; //(x,y) in grid coordinates; float for animation
|
||||
sf::Vector2i collision_pos; //(x, y) in grid coordinates: int for collision
|
||||
//void render(sf::Vector2f); //override final;
|
||||
|
||||
UIEntity();
|
||||
UIEntity(UIGrid&);
|
||||
|
||||
static PyObject* at(PyUIEntityObject* self, PyObject* o);
|
||||
static int init(PyUIEntityObject* self, PyObject* args, PyObject* kwds);
|
||||
|
||||
static PyObject* get_position(PyUIEntityObject* self, void* closure);
|
||||
static int set_position(PyUIEntityObject* self, PyObject* value, void* closure);
|
||||
static PyObject* get_gridstate(PyUIEntityObject* self, void* closure);
|
||||
static PyObject* get_spritenumber(PyUIEntityObject* self, void* closure);
|
||||
static int set_spritenumber(PyUIEntityObject* self, PyObject* value, void* closure);
|
||||
static PyMethodDef methods[];
|
||||
static PyGetSetDef getsetters[];
|
||||
static PyObject* repr(PyUIEntityObject* self);
|
||||
};
|
||||
|
||||
namespace mcrfpydef {
|
||||
static PyTypeObject PyUIEntityType = {
|
||||
//PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "mcrfpy.Entity",
|
||||
.tp_basicsize = sizeof(PyUIEntityObject),
|
||||
.tp_itemsize = 0,
|
||||
.tp_repr = (reprfunc)UIEntity::repr,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
||||
.tp_doc = "UIEntity objects",
|
||||
.tp_methods = UIEntity::methods,
|
||||
.tp_getset = UIEntity::getsetters,
|
||||
.tp_init = (initproc)UIEntity::init,
|
||||
.tp_new = PyType_GenericNew,
|
||||
};
|
||||
}
|
266
src/UIFrame.cpp
266
src/UIFrame.cpp
|
@ -1,266 +0,0 @@
|
|||
#include "UIFrame.h"
|
||||
#include "UICollection.h"
|
||||
#include "GameEngine.h"
|
||||
|
||||
UIDrawable* UIFrame::click_at(sf::Vector2f point)
|
||||
{
|
||||
for (auto e: *children)
|
||||
{
|
||||
auto p = e->click_at(point + box.getPosition());
|
||||
if (p)
|
||||
return p;
|
||||
}
|
||||
if (click_callable)
|
||||
{
|
||||
float x = box.getPosition().x, y = box.getPosition().y, w = box.getSize().x, h = box.getSize().y;
|
||||
if (point.x > x && point.y > y && point.x < x+w && point.y < y+h) return this;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UIFrame::UIFrame()
|
||||
: outline(0)
|
||||
{
|
||||
children = std::make_shared<std::vector<std::shared_ptr<UIDrawable>>>();
|
||||
box.setPosition(0, 0);
|
||||
box.setSize(sf::Vector2f(0, 0));
|
||||
}
|
||||
|
||||
UIFrame::UIFrame(float _x, float _y, float _w, float _h)
|
||||
: outline(0)
|
||||
{
|
||||
box.setPosition(_x, _y);
|
||||
box.setSize(sf::Vector2f(_w, _h));
|
||||
children = std::make_shared<std::vector<std::shared_ptr<UIDrawable>>>();
|
||||
}
|
||||
|
||||
UIFrame::~UIFrame()
|
||||
{
|
||||
children.reset();
|
||||
}
|
||||
|
||||
PyObjectsEnum UIFrame::derived_type()
|
||||
{
|
||||
return PyObjectsEnum::UIFRAME;
|
||||
}
|
||||
|
||||
void UIFrame::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||
{
|
||||
box.move(offset);
|
||||
//Resources::game->getWindow().draw(box);
|
||||
target.draw(box);
|
||||
box.move(-offset);
|
||||
|
||||
for (auto drawable : *children) {
|
||||
drawable->render(offset + box.getPosition(), target);
|
||||
}
|
||||
}
|
||||
|
||||
PyObject* UIFrame::get_children(PyUIFrameObject* self, void* closure)
|
||||
{
|
||||
// create PyUICollection instance pointing to self->data->children
|
||||
//PyUICollectionObject* o = (PyUICollectionObject*)mcrfpydef::PyUICollectionType.tp_alloc(&mcrfpydef::PyUICollectionType, 0);
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "UICollection");
|
||||
auto o = (PyUICollectionObject*)type->tp_alloc(type, 0);
|
||||
if (o)
|
||||
o->data = self->data->children;
|
||||
return (PyObject*)o;
|
||||
}
|
||||
|
||||
|
||||
PyObject* UIFrame::get_float_member(PyUIFrameObject* self, void* closure)
|
||||
{
|
||||
auto member_ptr = reinterpret_cast<long>(closure);
|
||||
if (member_ptr == 0)
|
||||
return PyFloat_FromDouble(self->data->box.getPosition().x);
|
||||
else if (member_ptr == 1)
|
||||
return PyFloat_FromDouble(self->data->box.getPosition().y);
|
||||
else if (member_ptr == 2)
|
||||
return PyFloat_FromDouble(self->data->box.getSize().x);
|
||||
else if (member_ptr == 3)
|
||||
return PyFloat_FromDouble(self->data->box.getSize().y);
|
||||
else if (member_ptr == 4)
|
||||
return PyFloat_FromDouble(self->data->box.getOutlineThickness());
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int UIFrame::set_float_member(PyUIFrameObject* self, PyObject* value, void* closure)
|
||||
{
|
||||
float val;
|
||||
auto member_ptr = reinterpret_cast<long>(closure);
|
||||
if (PyFloat_Check(value))
|
||||
{
|
||||
val = PyFloat_AsDouble(value);
|
||||
}
|
||||
else if (PyLong_Check(value))
|
||||
{
|
||||
val = PyLong_AsLong(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "Value must be an integer.");
|
||||
return -1;
|
||||
}
|
||||
if (member_ptr == 0) //x
|
||||
self->data->box.setPosition(val, self->data->box.getPosition().y);
|
||||
else if (member_ptr == 1) //y
|
||||
self->data->box.setPosition(self->data->box.getPosition().x, val);
|
||||
else if (member_ptr == 2) //w
|
||||
self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y));
|
||||
else if (member_ptr == 3) //h
|
||||
self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val));
|
||||
else if (member_ptr == 4) //outline
|
||||
self->data->box.setOutlineThickness(val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* UIFrame::get_color_member(PyUIFrameObject* self, void* closure)
|
||||
{
|
||||
// validate closure (should be impossible to be wrong, but it's thorough)
|
||||
auto member_ptr = reinterpret_cast<long>(closure);
|
||||
if (member_ptr != 0 && member_ptr != 1)
|
||||
{
|
||||
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
|
||||
return nullptr;
|
||||
}
|
||||
//PyTypeObject* colorType = &PyColorType;
|
||||
auto colorType = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color");
|
||||
PyObject* pyColor = colorType->tp_alloc(colorType, 0);
|
||||
if (pyColor == NULL)
|
||||
{
|
||||
std::cout << "failure to allocate mcrfpy.Color / PyColorType" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
PyColorObject* pyColorObj = reinterpret_cast<PyColorObject*>(pyColor);
|
||||
|
||||
// fetch correct member data
|
||||
sf::Color color;
|
||||
if (member_ptr == 0)
|
||||
{
|
||||
color = self->data->box.getFillColor();
|
||||
//return Py_BuildValue("(iii)", color.r, color.g, color.b);
|
||||
}
|
||||
else if (member_ptr == 1)
|
||||
{
|
||||
color = self->data->box.getOutlineColor();
|
||||
//return Py_BuildValue("(iii)", color.r, color.g, color.b);
|
||||
}
|
||||
|
||||
return PyColor(color).pyObject();
|
||||
}
|
||||
|
||||
int UIFrame::set_color_member(PyUIFrameObject* self, PyObject* value, void* closure)
|
||||
{
|
||||
//TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse
|
||||
auto member_ptr = reinterpret_cast<long>(closure);
|
||||
int r, g, b, a;
|
||||
if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color")))
|
||||
{
|
||||
sf::Color c = ((PyColorObject*)value)->data;
|
||||
r = c.r; g = c.g; b = c.b; a = c.a;
|
||||
}
|
||||
else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4)
|
||||
{
|
||||
// reject non-Color, non-tuple value
|
||||
PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object.");
|
||||
return -1;
|
||||
}
|
||||
else // get value from tuples
|
||||
{
|
||||
r = PyLong_AsLong(PyTuple_GetItem(value, 0));
|
||||
g = PyLong_AsLong(PyTuple_GetItem(value, 1));
|
||||
b = PyLong_AsLong(PyTuple_GetItem(value, 2));
|
||||
a = 255;
|
||||
|
||||
if (PyTuple_Size(value) == 4)
|
||||
{
|
||||
a = PyLong_AsLong(PyTuple_GetItem(value, 3));
|
||||
}
|
||||
}
|
||||
|
||||
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (member_ptr == 0)
|
||||
{
|
||||
self->data->box.setFillColor(sf::Color(r, g, b, a));
|
||||
}
|
||||
else if (member_ptr == 1)
|
||||
{
|
||||
self->data->box.setOutlineColor(sf::Color(r, g, b, a));
|
||||
}
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyGetSetDef UIFrame::getsetters[] = {
|
||||
{"x", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "X coordinate of top-left corner", (void*)0},
|
||||
{"y", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "Y coordinate of top-left corner", (void*)1},
|
||||
{"w", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "width of the rectangle", (void*)2},
|
||||
{"h", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "height of the rectangle", (void*)3},
|
||||
{"outline", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "Thickness of the border", (void*)4},
|
||||
{"fill_color", (getter)UIFrame::get_color_member, (setter)UIFrame::set_color_member, "Fill color of the rectangle", (void*)0},
|
||||
{"outline_color", (getter)UIFrame::get_color_member, (setter)UIFrame::set_color_member, "Outline color of the rectangle", (void*)1},
|
||||
{"children", (getter)UIFrame::get_children, NULL, "UICollection of objects on top of this one", NULL},
|
||||
{"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIFRAME},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyObject* UIFrame::repr(PyUIFrameObject* self)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
if (!self->data) ss << "<Frame (invalid internal object)>";
|
||||
else {
|
||||
auto box = self->data->box;
|
||||
auto fc = box.getFillColor();
|
||||
auto oc = box.getOutlineColor();
|
||||
ss << "<Frame (x=" << box.getPosition().x << ", y=" << box.getPosition().y << ", w=" <<
|
||||
box.getSize().x << ", w=" << box.getSize().y << ", " <<
|
||||
"outline=" << box.getOutlineThickness() << ", " <<
|
||||
"fill_color=(" << (int)fc.r << ", " << (int)fc.g << ", " << (int)fc.b << ", " << (int)fc.a <<"), " <<
|
||||
"outline_color=(" << (int)oc.r << ", " << (int)oc.g << ", " << (int)oc.b << ", " << (int)oc.a <<"), " <<
|
||||
self->data->children->size() << " child objects" <<
|
||||
")>";
|
||||
}
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
||||
|
||||
int UIFrame::init(PyUIFrameObject* self, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
//std::cout << "Init called\n";
|
||||
const char* keywords[] = { "x", "y", "w", "h", "fill_color", "outline_color", "outline", nullptr };
|
||||
float x = 0.0f, y = 0.0f, w = 0.0f, h=0.0f, outline=0.0f;
|
||||
PyObject* fill_color = 0;
|
||||
PyObject* outline_color = 0;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffff|OOf", const_cast<char**>(keywords), &x, &y, &w, &h, &fill_color, &outline_color, &outline))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
self->data->box.setPosition(sf::Vector2f(x, y));
|
||||
self->data->box.setSize(sf::Vector2f(w, h));
|
||||
self->data->box.setOutlineThickness(outline);
|
||||
// getsetter abuse because I haven't standardized Color object parsing (TODO)
|
||||
int err_val = 0;
|
||||
if (fill_color && fill_color != Py_None) err_val = UIFrame::set_color_member(self, fill_color, (void*)0);
|
||||
else self->data->box.setFillColor(sf::Color(0,0,0,255));
|
||||
if (err_val) return err_val;
|
||||
if (outline_color && outline_color != Py_None) err_val = UIFrame::set_color_member(self, outline_color, (void*)1);
|
||||
else self->data->box.setOutlineColor(sf::Color(128,128,128,255));
|
||||
if (err_val) return err_val;
|
||||
return 0;
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Python.h"
|
||||
#include "structmember.h"
|
||||
#include "IndexTexture.h"
|
||||
#include "Resources.h"
|
||||
#include <list>
|
||||
|
||||
#include "PyCallable.h"
|
||||
#include "PyColor.h"
|
||||
#include "PyVector.h"
|
||||
#include "UIDrawable.h"
|
||||
#include "UIBase.h"
|
||||
|
||||
//class UIFrame;
|
||||
//
|
||||
//typedef struct {
|
||||
// PyObject_HEAD
|
||||
// std::shared_ptr<UIFrame> data;
|
||||
//} PyUIFrameObject;
|
||||
|
||||
class UIFrame: public UIDrawable
|
||||
{
|
||||
public:
|
||||
UIFrame(float, float, float, float);
|
||||
UIFrame();
|
||||
~UIFrame();
|
||||
sf::RectangleShape box;
|
||||
float outline;
|
||||
std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>> children;
|
||||
void render(sf::Vector2f, sf::RenderTarget&) override final;
|
||||
void move(sf::Vector2f);
|
||||
PyObjectsEnum derived_type() override final;
|
||||
virtual UIDrawable* click_at(sf::Vector2f point) override final;
|
||||
|
||||
static PyObject* get_children(PyUIFrameObject* self, void* closure);
|
||||
|
||||
static PyObject* get_float_member(PyUIFrameObject* self, void* closure);
|
||||
static int set_float_member(PyUIFrameObject* self, PyObject* value, void* closure);
|
||||
static PyObject* get_color_member(PyUIFrameObject* self, void* closure);
|
||||
static int set_color_member(PyUIFrameObject* self, PyObject* value, void* closure);
|
||||
static PyGetSetDef getsetters[];
|
||||
static PyObject* repr(PyUIFrameObject* self);
|
||||
static int init(PyUIFrameObject* self, PyObject* args, PyObject* kwds);
|
||||
};
|
||||
|
||||
namespace mcrfpydef {
|
||||
static PyTypeObject PyUIFrameType = {
|
||||
//PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "mcrfpy.Frame",
|
||||
.tp_basicsize = sizeof(PyUIFrameObject),
|
||||
.tp_itemsize = 0,
|
||||
.tp_dealloc = (destructor)[](PyObject* self)
|
||||
{
|
||||
PyUIFrameObject* obj = (PyUIFrameObject*)self;
|
||||
obj->data.reset();
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
},
|
||||
.tp_repr = (reprfunc)UIFrame::repr,
|
||||
//.tp_hash = NULL,
|
||||
//.tp_iter
|
||||
//.tp_iternext
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = PyDoc_STR("docstring"),
|
||||
//.tp_methods = PyUIFrame_methods,
|
||||
//.tp_members = PyUIFrame_members,
|
||||
.tp_getset = UIFrame::getsetters,
|
||||
//.tp_base = NULL,
|
||||
.tp_init = (initproc)UIFrame::init,
|
||||
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
|
||||
{
|
||||
PyUIFrameObject* self = (PyUIFrameObject*)type->tp_alloc(type, 0);
|
||||
if (self) self->data = std::make_shared<UIFrame>();
|
||||
return (PyObject*)self;
|
||||
}
|
||||
};
|
||||
}
|
641
src/UIGrid.cpp
641
src/UIGrid.cpp
|
@ -1,641 +0,0 @@
|
|||
#include "UIGrid.h"
|
||||
#include "GameEngine.h"
|
||||
#include "McRFPy_API.h"
|
||||
|
||||
UIGrid::UIGrid() {}
|
||||
|
||||
UIGrid::UIGrid(int gx, int gy, std::shared_ptr<PyTexture> _ptex, sf::Vector2f _xy, sf::Vector2f _wh)
|
||||
: grid_x(gx), grid_y(gy),
|
||||
zoom(1.0f), center_x((gx/2) * _ptex->sprite_width), center_y((gy/2) * _ptex->sprite_height),
|
||||
ptex(_ptex), points(gx * gy)
|
||||
{
|
||||
entities = std::make_shared<std::list<std::shared_ptr<UIEntity>>>();
|
||||
|
||||
box.setSize(_wh);
|
||||
box.setPosition(_xy);
|
||||
|
||||
box.setFillColor(sf::Color(0,0,0,0));
|
||||
// create renderTexture with maximum theoretical size; sprite can resize to show whatever amount needs to be rendered
|
||||
renderTexture.create(1920, 1080); // TODO - renderTexture should be window size; above 1080p this will cause rendering errors
|
||||
|
||||
sprite = ptex->sprite(0);
|
||||
|
||||
output.setTextureRect(
|
||||
sf::IntRect(0, 0,
|
||||
box.getSize().x, box.getSize().y));
|
||||
output.setPosition(box.getPosition());
|
||||
// textures are upside-down inside renderTexture
|
||||
output.setTexture(renderTexture.getTexture());
|
||||
|
||||
}
|
||||
|
||||
void UIGrid::update() {}
|
||||
|
||||
|
||||
void UIGrid::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||
{
|
||||
output.setPosition(box.getPosition() + offset); // output sprite can move; update position when drawing
|
||||
// output size can change; update size when drawing
|
||||
output.setTextureRect(
|
||||
sf::IntRect(0, 0,
|
||||
box.getSize().x, box.getSize().y));
|
||||
renderTexture.clear(sf::Color(8, 8, 8, 255)); // TODO - UIGrid needs a "background color" field
|
||||
// sprites that are visible according to zoom, center_x, center_y, and box width
|
||||
float center_x_sq = center_x / ptex->sprite_width;
|
||||
float center_y_sq = center_y / ptex->sprite_height;
|
||||
|
||||
float width_sq = box.getSize().x / (ptex->sprite_width * zoom);
|
||||
float height_sq = box.getSize().y / (ptex->sprite_height * zoom);
|
||||
float left_edge = center_x_sq - (width_sq / 2.0);
|
||||
float top_edge = center_y_sq - (height_sq / 2.0);
|
||||
|
||||
int left_spritepixels = center_x - (box.getSize().x / 2.0 / zoom);
|
||||
int top_spritepixels = center_y - (box.getSize().y / 2.0 / zoom);
|
||||
|
||||
//sprite.setScale(sf::Vector2f(zoom, zoom));
|
||||
sf::RectangleShape r; // for colors and overlays
|
||||
r.setSize(sf::Vector2f(ptex->sprite_width * zoom, ptex->sprite_height * zoom));
|
||||
r.setOutlineThickness(0);
|
||||
|
||||
int x_limit = left_edge + width_sq + 2;
|
||||
if (x_limit > grid_x) x_limit = grid_x;
|
||||
|
||||
int y_limit = top_edge + height_sq + 2;
|
||||
if (y_limit > grid_y) y_limit = grid_y;
|
||||
|
||||
// base layer - bottom color, tile sprite ("ground")
|
||||
for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0);
|
||||
x < x_limit; //x < view_width;
|
||||
x+=1)
|
||||
{
|
||||
//for (float y = (top_edge >= 0 ? top_edge : 0);
|
||||
for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0);
|
||||
y < y_limit; //y < view_height;
|
||||
y+=1)
|
||||
{
|
||||
auto pixel_pos = sf::Vector2f(
|
||||
(x*ptex->sprite_width - left_spritepixels) * zoom,
|
||||
(y*ptex->sprite_height - top_spritepixels) * zoom );
|
||||
|
||||
auto gridpoint = at(std::floor(x), std::floor(y));
|
||||
|
||||
//sprite.setPosition(pixel_pos);
|
||||
|
||||
r.setPosition(pixel_pos);
|
||||
r.setFillColor(gridpoint.color);
|
||||
renderTexture.draw(r);
|
||||
|
||||
// tilesprite
|
||||
// if discovered but not visible, set opacity to 90%
|
||||
// if not discovered... just don't draw it?
|
||||
if (gridpoint.tilesprite != -1) {
|
||||
sprite = ptex->sprite(gridpoint.tilesprite, pixel_pos, sf::Vector2f(zoom, zoom)); //setSprite(gridpoint.tilesprite);;
|
||||
renderTexture.draw(sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// middle layer - entities
|
||||
// disabling entity rendering until I can render their UISprite inside the rendertexture (not directly to window)
|
||||
for (auto e : *entities) {
|
||||
// TODO skip out-of-bounds entities (grid square not visible at all, check for partially on visible grid squares / floating point grid position)
|
||||
//auto drawent = e->cGrid->indexsprite.drawable();
|
||||
auto& drawent = e->sprite;
|
||||
//drawent.setScale(zoom, zoom);
|
||||
drawent.setScale(sf::Vector2f(zoom, zoom));
|
||||
auto pixel_pos = sf::Vector2f(
|
||||
(e->position.x*ptex->sprite_width - left_spritepixels) * zoom,
|
||||
(e->position.y*ptex->sprite_height - top_spritepixels) * zoom );
|
||||
//drawent.setPosition(pixel_pos);
|
||||
//renderTexture.draw(drawent);
|
||||
drawent.render(pixel_pos, renderTexture);
|
||||
}
|
||||
|
||||
|
||||
// top layer - opacity for discovered / visible status (debug, basically)
|
||||
/* // Disabled until I attach a "perspective"
|
||||
for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0);
|
||||
x < x_limit; //x < view_width;
|
||||
x+=1)
|
||||
{
|
||||
//for (float y = (top_edge >= 0 ? top_edge : 0);
|
||||
for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0);
|
||||
y < y_limit; //y < view_height;
|
||||
y+=1)
|
||||
{
|
||||
|
||||
auto pixel_pos = sf::Vector2f(
|
||||
(x*itex->grid_size - left_spritepixels) * zoom,
|
||||
(y*itex->grid_size - top_spritepixels) * zoom );
|
||||
|
||||
auto gridpoint = at(std::floor(x), std::floor(y));
|
||||
|
||||
sprite.setPosition(pixel_pos);
|
||||
|
||||
r.setPosition(pixel_pos);
|
||||
|
||||
// visible & discovered layers for testing purposes
|
||||
if (!gridpoint.discovered) {
|
||||
r.setFillColor(sf::Color(16, 16, 20, 192)); // 255 opacity for actual blackout
|
||||
renderTexture.draw(r);
|
||||
} else if (!gridpoint.visible) {
|
||||
r.setFillColor(sf::Color(32, 32, 40, 128));
|
||||
renderTexture.draw(r);
|
||||
}
|
||||
|
||||
// overlay
|
||||
|
||||
// uisprite
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// grid lines for testing & validation
|
||||
/*
|
||||
sf::Vertex line[] =
|
||||
{
|
||||
sf::Vertex(sf::Vector2f(0, 0), sf::Color::Red),
|
||||
sf::Vertex(box.getSize(), sf::Color::Red),
|
||||
|
||||
};
|
||||
|
||||
renderTexture.draw(line, 2, sf::Lines);
|
||||
sf::Vertex lineb[] =
|
||||
{
|
||||
sf::Vertex(sf::Vector2f(0, box.getSize().y), sf::Color::Blue),
|
||||
sf::Vertex(sf::Vector2f(box.getSize().x, 0), sf::Color::Blue),
|
||||
|
||||
};
|
||||
|
||||
renderTexture.draw(lineb, 2, sf::Lines);
|
||||
*/
|
||||
|
||||
// render to window
|
||||
renderTexture.display();
|
||||
//Resources::game->getWindow().draw(output);
|
||||
target.draw(output);
|
||||
|
||||
}
|
||||
|
||||
UIGridPoint& UIGrid::at(int x, int y)
|
||||
{
|
||||
return points[y * grid_x + x];
|
||||
}
|
||||
|
||||
PyObjectsEnum UIGrid::derived_type()
|
||||
{
|
||||
return PyObjectsEnum::UIGRID;
|
||||
}
|
||||
|
||||
std::shared_ptr<PyTexture> UIGrid::getTexture()
|
||||
{
|
||||
return ptex;
|
||||
}
|
||||
|
||||
UIDrawable* UIGrid::click_at(sf::Vector2f point)
|
||||
{
|
||||
if (click_callable)
|
||||
{
|
||||
if(box.getGlobalBounds().contains(point)) return this;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
||||
int grid_x, grid_y;
|
||||
PyObject* textureObj;
|
||||
//float box_x, box_y, box_w, box_h;
|
||||
PyObject* pos, *size;
|
||||
|
||||
//if (!PyArg_ParseTuple(args, "iiOffff", &grid_x, &grid_y, &textureObj, &box_x, &box_y, &box_w, &box_h)) {
|
||||
if (!PyArg_ParseTuple(args, "iiOOO", &grid_x, &grid_y, &textureObj, &pos, &size)) {
|
||||
return -1; // If parsing fails, return an error
|
||||
}
|
||||
|
||||
PyVectorObject* pos_result = PyVector::from_arg(pos);
|
||||
if (!pos_result)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "pos must be a mcrfpy.Vector instance or arguments to mcrfpy.Vector.__init__");
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyVectorObject* size_result = PyVector::from_arg(size);
|
||||
if (!size_result)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "pos must be a mcrfpy.Vector instance or arguments to mcrfpy.Vector.__init__");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Convert PyObject texture to IndexTexture*
|
||||
// This requires the texture object to have been initialized similar to UISprite's texture handling
|
||||
|
||||
//if (!PyObject_IsInstance(textureObj, (PyObject*)&PyTextureType)) {
|
||||
if (!PyObject_IsInstance(textureObj, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))) {
|
||||
PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance");
|
||||
return -1;
|
||||
}
|
||||
PyTextureObject* pyTexture = reinterpret_cast<PyTextureObject*>(textureObj);
|
||||
// TODO (7DRL day 2, item 4.) use shared_ptr / PyTextureObject on UIGrid
|
||||
//IndexTexture* texture = pyTexture->data.get();
|
||||
|
||||
// Initialize UIGrid
|
||||
//self->data = new UIGrid(grid_x, grid_y, texture, sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h));
|
||||
//self->data = std::make_shared<UIGrid>(grid_x, grid_y, pyTexture->data,
|
||||
// sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h));
|
||||
self->data = std::make_shared<UIGrid>(grid_x, grid_y, pyTexture->data, pos_result->data, size_result->data);
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
PyObject* UIGrid::get_grid_size(PyUIGridObject* self, void* closure) {
|
||||
return Py_BuildValue("(ii)", self->data->grid_x, self->data->grid_y);
|
||||
}
|
||||
|
||||
PyObject* UIGrid::get_position(PyUIGridObject* self, void* closure) {
|
||||
auto& box = self->data->box;
|
||||
return Py_BuildValue("(ff)", box.getPosition().x, box.getPosition().y);
|
||||
}
|
||||
|
||||
int UIGrid::set_position(PyUIGridObject* self, PyObject* value, void* closure) {
|
||||
float x, y;
|
||||
if (!PyArg_ParseTuple(value, "ff", &x, &y)) {
|
||||
PyErr_SetString(PyExc_ValueError, "Position must be a tuple of two floats");
|
||||
return -1;
|
||||
}
|
||||
self->data->box.setPosition(x, y);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* UIGrid::get_size(PyUIGridObject* self, void* closure) {
|
||||
auto& box = self->data->box;
|
||||
return Py_BuildValue("(ff)", box.getSize().x, box.getSize().y);
|
||||
}
|
||||
|
||||
int UIGrid::set_size(PyUIGridObject* self, PyObject* value, void* closure) {
|
||||
float w, h;
|
||||
if (!PyArg_ParseTuple(value, "ff", &w, &h)) {
|
||||
PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats");
|
||||
return -1;
|
||||
}
|
||||
self->data->box.setSize(sf::Vector2f(w, h));
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* UIGrid::get_center(PyUIGridObject* self, void* closure) {
|
||||
return Py_BuildValue("(ff)", self->data->center_x, self->data->center_y);
|
||||
}
|
||||
|
||||
int UIGrid::set_center(PyUIGridObject* self, PyObject* value, void* closure) {
|
||||
float x, y;
|
||||
if (!PyArg_ParseTuple(value, "ff", &x, &y)) {
|
||||
PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats");
|
||||
return -1;
|
||||
}
|
||||
self->data->center_x = x;
|
||||
self->data->center_y = y;
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* UIGrid::get_float_member(PyUIGridObject* self, void* closure)
|
||||
{
|
||||
auto member_ptr = reinterpret_cast<long>(closure);
|
||||
if (member_ptr == 0) // x
|
||||
return PyFloat_FromDouble(self->data->box.getPosition().x);
|
||||
else if (member_ptr == 1) // y
|
||||
return PyFloat_FromDouble(self->data->box.getPosition().y);
|
||||
else if (member_ptr == 2) // w
|
||||
return PyFloat_FromDouble(self->data->box.getSize().x);
|
||||
else if (member_ptr == 3) // h
|
||||
return PyFloat_FromDouble(self->data->box.getSize().y);
|
||||
else if (member_ptr == 4) // center_x
|
||||
return PyFloat_FromDouble(self->data->center_x);
|
||||
else if (member_ptr == 5) // center_y
|
||||
return PyFloat_FromDouble(self->data->center_y);
|
||||
else if (member_ptr == 6) // zoom
|
||||
return PyFloat_FromDouble(self->data->zoom);
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int UIGrid::set_float_member(PyUIGridObject* self, PyObject* value, void* closure)
|
||||
{
|
||||
float val;
|
||||
auto member_ptr = reinterpret_cast<long>(closure);
|
||||
if (PyFloat_Check(value))
|
||||
{
|
||||
val = PyFloat_AsDouble(value);
|
||||
}
|
||||
else if (PyLong_Check(value))
|
||||
{
|
||||
val = PyLong_AsLong(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "Value must be a floating point number.");
|
||||
return -1;
|
||||
}
|
||||
if (member_ptr == 0) // x
|
||||
self->data->box.setPosition(val, self->data->box.getPosition().y);
|
||||
else if (member_ptr == 1) // y
|
||||
self->data->box.setPosition(self->data->box.getPosition().x, val);
|
||||
else if (member_ptr == 2) // w
|
||||
self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y));
|
||||
else if (member_ptr == 3) // h
|
||||
self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val));
|
||||
else if (member_ptr == 4) // center_x
|
||||
self->data->center_x = val;
|
||||
else if (member_ptr == 5) // center_y
|
||||
self->data->center_y = val;
|
||||
else if (member_ptr == 6) // zoom
|
||||
self->data->zoom = val;
|
||||
return 0;
|
||||
}
|
||||
// TODO (7DRL Day 2, item 5.) return Texture object
|
||||
/*
|
||||
PyObject* UIGrid::get_texture(PyUIGridObject* self, void* closure) {
|
||||
Py_INCREF(self->texture);
|
||||
return self->texture;
|
||||
}
|
||||
*/
|
||||
|
||||
PyObject* UIGrid::get_texture(PyUIGridObject* self, void* closure) {
|
||||
//return self->data->getTexture()->pyObject();
|
||||
// PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPointState")
|
||||
//PyTextureObject* obj = (PyTextureObject*)((&PyTextureType)->tp_alloc(&PyTextureType, 0));
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture");
|
||||
auto obj = (PyTextureObject*)type->tp_alloc(type, 0);
|
||||
obj->data = self->data->getTexture();
|
||||
return (PyObject*)obj;
|
||||
}
|
||||
|
||||
PyObject* UIGrid::py_at(PyUIGridObject* self, PyObject* o)
|
||||
{
|
||||
int x, y;
|
||||
if (!PyArg_ParseTuple(o, "ii", &x, &y)) {
|
||||
PyErr_SetString(PyExc_TypeError, "UIGrid.at requires two integer arguments: (x, y)");
|
||||
return NULL;
|
||||
}
|
||||
if (x < 0 || x >= self->data->grid_x) {
|
||||
PyErr_SetString(PyExc_ValueError, "x value out of range (0, Grid.grid_y)");
|
||||
return NULL;
|
||||
}
|
||||
if (y < 0 || y >= self->data->grid_y) {
|
||||
PyErr_SetString(PyExc_ValueError, "y value out of range (0, Grid.grid_y)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//PyUIGridPointObject* obj = (PyUIGridPointObject*)((&PyUIGridPointType)->tp_alloc(&PyUIGridPointType, 0));
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPoint");
|
||||
auto obj = (PyUIGridPointObject*)type->tp_alloc(type, 0);
|
||||
//auto target = std::static_pointer_cast<UIEntity>(target);
|
||||
obj->data = &(self->data->points[x + self->data->grid_x * y]);
|
||||
obj->grid = self->data;
|
||||
return (PyObject*)obj;
|
||||
}
|
||||
|
||||
PyMethodDef UIGrid::methods[] = {
|
||||
{"at", (PyCFunction)UIGrid::py_at, METH_O},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
|
||||
PyGetSetDef UIGrid::getsetters[] = {
|
||||
|
||||
// TODO - refactor into get_vector_member with field identifier values `(void*)n`
|
||||
{"grid_size", (getter)UIGrid::get_grid_size, NULL, "Grid dimensions (grid_x, grid_y)", NULL},
|
||||
{"position", (getter)UIGrid::get_position, (setter)UIGrid::set_position, "Position of the grid (x, y)", NULL},
|
||||
{"size", (getter)UIGrid::get_size, (setter)UIGrid::set_size, "Size of the grid (width, height)", NULL},
|
||||
{"center", (getter)UIGrid::get_center, (setter)UIGrid::set_center, "Grid coordinate at the center of the Grid's view (pan)", NULL},
|
||||
|
||||
{"entities", (getter)UIGrid::get_children, NULL, "EntityCollection of entities on this grid", NULL},
|
||||
|
||||
{"x", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "top-left corner X-coordinate", (void*)0},
|
||||
{"y", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "top-left corner Y-coordinate", (void*)1},
|
||||
{"w", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "visible widget width", (void*)2},
|
||||
{"h", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "visible widget height", (void*)3},
|
||||
{"center_x", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "center of the view X-coordinate", (void*)4},
|
||||
{"center_y", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "center of the view Y-coordinate", (void*)5},
|
||||
{"zoom", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "zoom factor for displaying the Grid", (void*)6},
|
||||
|
||||
{"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIGRID},
|
||||
|
||||
{"texture", (getter)UIGrid::get_texture, NULL, "Texture of the grid", NULL}, //TODO 7DRL-day2-item5
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
PyObject* UIGrid::get_children(PyUIGridObject* self, void* closure)
|
||||
{
|
||||
// create PyUICollection instance pointing to self->data->children
|
||||
//PyUIEntityCollectionObject* o = (PyUIEntityCollectionObject*)PyUIEntityCollectionType.tp_alloc(&PyUIEntityCollectionType, 0);
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "EntityCollection");
|
||||
auto o = (PyUIEntityCollectionObject*)type->tp_alloc(type, 0);
|
||||
if (o) {
|
||||
o->data = self->data->entities; // todone. / BUGFIX - entities isn't a shared pointer on UIGrid, what to do? -- I made it a sp<list<sp<UIEntity>>>
|
||||
o->grid = self->data;
|
||||
}
|
||||
return (PyObject*)o;
|
||||
}
|
||||
|
||||
PyObject* UIGrid::repr(PyUIGridObject* self)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
if (!self->data) ss << "<Grid (invalid internal object)>";
|
||||
else {
|
||||
auto grid = self->data;
|
||||
auto box = grid->box;
|
||||
ss << "<Grid (x=" << box.getPosition().x << ", y=" << box.getPosition().y << ", w=" << box.getSize().x << ", h=" << box.getSize().y << ", " <<
|
||||
"center=(" << grid->center_x << ", " << grid->center_y << "), zoom=" << grid->zoom <<
|
||||
")>";
|
||||
}
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
||||
|
||||
/* // TODO standard pointer would need deleted, but I opted for a shared pointer. tp_dealloc currently not even defined in the PyTypeObject
|
||||
void PyUIGrid_dealloc(PyUIGridObject* self) {
|
||||
delete self->data; // Clean up the allocated UIGrid object
|
||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||
}
|
||||
*/
|
||||
|
||||
int UIEntityCollectionIter::init(PyUIEntityCollectionIterObject* self, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject* UIEntityCollectionIter::next(PyUIEntityCollectionIterObject* self)
|
||||
{
|
||||
if (self->data->size() != self->start_size)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (self->index > self->start_size - 1)
|
||||
{
|
||||
PyErr_SetNone(PyExc_StopIteration);
|
||||
return NULL;
|
||||
}
|
||||
self->index++;
|
||||
auto vec = self->data.get();
|
||||
if (!vec)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer");
|
||||
return NULL;
|
||||
}
|
||||
// Advance list iterator since Entities are not stored in a vector (if this code even worked)
|
||||
// vectors only: //auto target = (*vec)[self->index-1];
|
||||
//auto l_front = (*vec).begin();
|
||||
//std::advance(l_front, self->index-1);
|
||||
|
||||
// TODO build PyObject* of the correct UIDrawable subclass to return
|
||||
//return py_instance(target);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject* UIEntityCollectionIter::repr(PyUIEntityCollectionIterObject* self)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
if (!self->data) ss << "<UICollectionIter (invalid internal object)>";
|
||||
else {
|
||||
ss << "<UICollectionIter (" << self->data->size() << " child objects, @ index " << self->index << ")>";
|
||||
}
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
||||
|
||||
Py_ssize_t UIEntityCollection::len(PyUIEntityCollectionObject* self) {
|
||||
return self->data->size();
|
||||
}
|
||||
|
||||
PyObject* UIEntityCollection::getitem(PyUIEntityCollectionObject* self, Py_ssize_t index) {
|
||||
// build a Python version of item at self->data[index]
|
||||
// Copy pasted::
|
||||
auto vec = self->data.get();
|
||||
if (!vec)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer");
|
||||
return NULL;
|
||||
}
|
||||
while (index < 0) index += self->data->size();
|
||||
if (index > self->data->size() - 1)
|
||||
{
|
||||
PyErr_SetString(PyExc_IndexError, "EntityCollection index out of range");
|
||||
return NULL;
|
||||
}
|
||||
auto l_begin = (*vec).begin();
|
||||
std::advance(l_begin, index);
|
||||
auto target = *l_begin; //auto target = (*vec)[index];
|
||||
//RET_PY_INSTANCE(target);
|
||||
// construct and return an entity object that points directly into the UIGrid's entity vector
|
||||
//PyUIEntityObject* o = (PyUIEntityObject*)((&PyUIEntityType)->tp_alloc(&PyUIEntityType, 0));
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity");
|
||||
auto o = (PyUIEntityObject*)type->tp_alloc(type, 0);
|
||||
auto p = std::static_pointer_cast<UIEntity>(target);
|
||||
o->data = p;
|
||||
return (PyObject*)o;
|
||||
return NULL;
|
||||
|
||||
|
||||
}
|
||||
|
||||
PySequenceMethods UIEntityCollection::sqmethods = {
|
||||
.sq_length = (lenfunc)UIEntityCollection::len,
|
||||
.sq_item = (ssizeargfunc)UIEntityCollection::getitem,
|
||||
//.sq_item_by_index = UIEntityCollection::getitem
|
||||
//.sq_slice - return a subset of the iterable
|
||||
//.sq_ass_item - called when `o[x] = y` is executed (x is any object type)
|
||||
//.sq_ass_slice - cool; no thanks, for now
|
||||
//.sq_contains - called when `x in o` is executed
|
||||
//.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer)
|
||||
};
|
||||
|
||||
PyObject* UIEntityCollection::append(PyUIEntityCollectionObject* self, PyObject* o)
|
||||
{
|
||||
// if not UIDrawable subclass, reject it
|
||||
// self->data->push_back( c++ object inside o );
|
||||
|
||||
// this would be a great use case for .tp_base
|
||||
//if (!PyObject_IsInstance(o, (PyObject*)&PyUIEntityType))
|
||||
if (!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity")))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "Only Entity objects can be added to EntityCollection");
|
||||
return NULL;
|
||||
}
|
||||
PyUIEntityObject* entity = (PyUIEntityObject*)o;
|
||||
self->data->push_back(entity->data);
|
||||
entity->data->grid = self->grid;
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PyObject* UIEntityCollection::remove(PyUIEntityCollectionObject* self, PyObject* o)
|
||||
{
|
||||
if (!PyLong_Check(o))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove");
|
||||
return NULL;
|
||||
}
|
||||
long index = PyLong_AsLong(o);
|
||||
if (index >= self->data->size())
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "Index out of range");
|
||||
return NULL;
|
||||
}
|
||||
else if (index < 0)
|
||||
{
|
||||
PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// release the shared pointer at correct part of the list
|
||||
self->data->erase(std::next(self->data->begin(), index));
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PyMethodDef UIEntityCollection::methods[] = {
|
||||
{"append", (PyCFunction)UIEntityCollection::append, METH_O},
|
||||
//{"extend", (PyCFunction)UIEntityCollection::extend, METH_O}, // TODO
|
||||
{"remove", (PyCFunction)UIEntityCollection::remove, METH_O},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
PyObject* UIEntityCollection::repr(PyUIEntityCollectionObject* self)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
if (!self->data) ss << "<UICollection (invalid internal object)>";
|
||||
else {
|
||||
ss << "<UICollection (" << self->data->size() << " child objects)>";
|
||||
}
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
||||
|
||||
int UIEntityCollection::init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject* UIEntityCollection::iter(PyUIEntityCollectionObject* self)
|
||||
{
|
||||
//PyUIEntityCollectionIterObject* iterObj;
|
||||
//iterObj = (PyUIEntityCollectionIterObject*)PyUIEntityCollectionIterType.tp_alloc(&PyUIEntityCollectionIterType, 0);
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "EntityCollectionIter");
|
||||
auto iterObj = (PyUIEntityCollectionIterObject*)type->tp_alloc(type, 0);
|
||||
if (iterObj == NULL) {
|
||||
return NULL; // Failed to allocate memory for the iterator object
|
||||
}
|
||||
|
||||
iterObj->data = self->data;
|
||||
iterObj->index = 0;
|
||||
iterObj->start_size = self->data->size();
|
||||
|
||||
return (PyObject*)iterObj;
|
||||
}
|
184
src/UIGrid.h
184
src/UIGrid.h
|
@ -1,184 +0,0 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Python.h"
|
||||
#include "structmember.h"
|
||||
#include "IndexTexture.h"
|
||||
#include "Resources.h"
|
||||
#include <list>
|
||||
|
||||
#include "PyCallable.h"
|
||||
#include "PyTexture.h"
|
||||
#include "PyColor.h"
|
||||
#include "PyVector.h"
|
||||
#include "PyFont.h"
|
||||
|
||||
#include "UIGridPoint.h"
|
||||
#include "UIEntity.h"
|
||||
#include "UIDrawable.h"
|
||||
#include "UIBase.h"
|
||||
|
||||
class UIGrid: public UIDrawable
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<PyTexture> ptex;
|
||||
public:
|
||||
UIGrid();
|
||||
//UIGrid(int, int, IndexTexture*, float, float, float, float);
|
||||
UIGrid(int, int, std::shared_ptr<PyTexture>, sf::Vector2f, sf::Vector2f);
|
||||
void update();
|
||||
void render(sf::Vector2f, sf::RenderTarget&) override final;
|
||||
UIGridPoint& at(int, int);
|
||||
PyObjectsEnum derived_type() override final;
|
||||
//void setSprite(int);
|
||||
virtual UIDrawable* click_at(sf::Vector2f point) override final;
|
||||
|
||||
int grid_x, grid_y;
|
||||
//int grid_size; // grid sizes are implied by IndexTexture now
|
||||
sf::RectangleShape box;
|
||||
float center_x, center_y, zoom;
|
||||
//IndexTexture* itex;
|
||||
std::shared_ptr<PyTexture> getTexture();
|
||||
sf::Sprite sprite, output;
|
||||
sf::RenderTexture renderTexture;
|
||||
std::vector<UIGridPoint> points;
|
||||
std::shared_ptr<std::list<std::shared_ptr<UIEntity>>> entities;
|
||||
|
||||
static int init(PyUIGridObject* self, PyObject* args, PyObject* kwds);
|
||||
static PyObject* get_grid_size(PyUIGridObject* self, void* closure);
|
||||
static PyObject* get_position(PyUIGridObject* self, void* closure);
|
||||
static int set_position(PyUIGridObject* self, PyObject* value, void* closure);
|
||||
static PyObject* get_size(PyUIGridObject* self, void* closure);
|
||||
static int set_size(PyUIGridObject* self, PyObject* value, void* closure);
|
||||
static PyObject* get_center(PyUIGridObject* self, void* closure);
|
||||
static int set_center(PyUIGridObject* self, PyObject* value, void* closure);
|
||||
static PyObject* get_float_member(PyUIGridObject* self, void* closure);
|
||||
static int set_float_member(PyUIGridObject* self, PyObject* value, void* closure);
|
||||
static PyObject* get_texture(PyUIGridObject* self, void* closure);
|
||||
static PyObject* py_at(PyUIGridObject* self, PyObject* o);
|
||||
static PyMethodDef methods[];
|
||||
static PyGetSetDef getsetters[];
|
||||
static PyObject* get_children(PyUIGridObject* self, void* closure);
|
||||
static PyObject* repr(PyUIGridObject* self);
|
||||
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
std::shared_ptr<std::list<std::shared_ptr<UIEntity>>> data;
|
||||
std::shared_ptr<UIGrid> grid;
|
||||
} PyUIEntityCollectionObject;
|
||||
|
||||
class UIEntityCollection {
|
||||
public:
|
||||
static PySequenceMethods sqmethods;
|
||||
static PyObject* append(PyUIEntityCollectionObject* self, PyObject* o);
|
||||
static PyObject* remove(PyUIEntityCollectionObject* self, PyObject* o);
|
||||
static PyMethodDef methods[];
|
||||
static PyObject* repr(PyUIEntityCollectionObject* self);
|
||||
static int init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds);
|
||||
static PyObject* iter(PyUIEntityCollectionObject* self);
|
||||
static Py_ssize_t len(PyUIEntityCollectionObject* self);
|
||||
static PyObject* getitem(PyUIEntityCollectionObject* self, Py_ssize_t index);
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
std::shared_ptr<std::list<std::shared_ptr<UIEntity>>> data;
|
||||
int index;
|
||||
int start_size;
|
||||
} PyUIEntityCollectionIterObject;
|
||||
|
||||
class UIEntityCollectionIter {
|
||||
public:
|
||||
static int init(PyUIEntityCollectionIterObject* self, PyObject* args, PyObject* kwds);
|
||||
static PyObject* next(PyUIEntityCollectionIterObject* self);
|
||||
static PyObject* repr(PyUIEntityCollectionIterObject* self);
|
||||
static PyObject* getitem(PyUIEntityCollectionObject* self, Py_ssize_t index);
|
||||
|
||||
};
|
||||
|
||||
namespace mcrfpydef {
|
||||
static PyTypeObject PyUIGridType = {
|
||||
//PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "mcrfpy.Grid",
|
||||
.tp_basicsize = sizeof(PyUIGridObject),
|
||||
.tp_itemsize = 0,
|
||||
//.tp_dealloc = (destructor)[](PyObject* self)
|
||||
//{
|
||||
// PyUIGridObject* obj = (PyUIGridObject*)self;
|
||||
// obj->data.reset();
|
||||
// Py_TYPE(self)->tp_free(self);
|
||||
//},
|
||||
//TODO - PyUIGrid REPR def:
|
||||
.tp_repr = (reprfunc)UIGrid::repr,
|
||||
//.tp_hash = NULL,
|
||||
//.tp_iter
|
||||
//.tp_iternext
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = PyDoc_STR("docstring"),
|
||||
.tp_methods = UIGrid::methods,
|
||||
//.tp_members = UIGrid::members,
|
||||
.tp_getset = UIGrid::getsetters,
|
||||
//.tp_base = NULL,
|
||||
.tp_init = (initproc)UIGrid::init,
|
||||
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
|
||||
{
|
||||
PyUIGridObject* self = (PyUIGridObject*)type->tp_alloc(type, 0);
|
||||
if (self) self->data = std::make_shared<UIGrid>();
|
||||
return (PyObject*)self;
|
||||
}
|
||||
};
|
||||
|
||||
static PyTypeObject PyUIEntityCollectionIterType = {
|
||||
//PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "mcrfpy.UICollectionIter",
|
||||
.tp_basicsize = sizeof(PyUIEntityCollectionIterObject),
|
||||
.tp_itemsize = 0,
|
||||
.tp_dealloc = (destructor)[](PyObject* self)
|
||||
{
|
||||
PyUIEntityCollectionIterObject* obj = (PyUIEntityCollectionIterObject*)self;
|
||||
obj->data.reset();
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
},
|
||||
.tp_repr = (reprfunc)UIEntityCollectionIter::repr,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = PyDoc_STR("Iterator for a collection of UI objects"),
|
||||
.tp_iternext = (iternextfunc)UIEntityCollectionIter::next,
|
||||
//.tp_getset = UIEntityCollection::getset,
|
||||
.tp_init = (initproc)UIEntityCollectionIter::init, // just raise an exception
|
||||
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required.");
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
static PyTypeObject PyUIEntityCollectionType = {
|
||||
//PyVarObject_/HEAD_INIT(NULL, 0)
|
||||
.tp_name = "mcrfpy.EntityCollection",
|
||||
.tp_basicsize = sizeof(PyUIEntityCollectionObject),
|
||||
.tp_itemsize = 0,
|
||||
.tp_dealloc = (destructor)[](PyObject* self)
|
||||
{
|
||||
PyUIEntityCollectionObject* obj = (PyUIEntityCollectionObject*)self;
|
||||
obj->data.reset();
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
},
|
||||
.tp_repr = (reprfunc)UIEntityCollection::repr,
|
||||
.tp_as_sequence = &UIEntityCollection::sqmethods,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = PyDoc_STR("Iterable, indexable collection of Entities"),
|
||||
.tp_iter = (getiterfunc)UIEntityCollection::iter,
|
||||
.tp_methods = UIEntityCollection::methods, // append, remove
|
||||
//.tp_getset = UIEntityCollection::getset,
|
||||
.tp_init = (initproc)UIEntityCollection::init, // just raise an exception
|
||||
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
|
||||
{
|
||||
// Does PyUIEntityCollectionType need __new__ if it's not supposed to be instantiable by the user?
|
||||
// Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker?
|
||||
PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required.");
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,158 +0,0 @@
|
|||
#include "UIGridPoint.h"
|
||||
|
||||
UIGridPoint::UIGridPoint()
|
||||
: color(1.0f, 1.0f, 1.0f), color_overlay(0.0f, 0.0f, 0.0f), walkable(false), transparent(false),
|
||||
tilesprite(-1), tile_overlay(-1), uisprite(-1)
|
||||
{}
|
||||
|
||||
// Utility function to convert sf::Color to PyObject*
|
||||
PyObject* sfColor_to_PyObject(sf::Color color) {
|
||||
return Py_BuildValue("(iiii)", color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
// Utility function to convert PyObject* to sf::Color
|
||||
sf::Color PyObject_to_sfColor(PyObject* obj) {
|
||||
int r, g, b, a = 255; // Default alpha to fully opaque if not specified
|
||||
if (!PyArg_ParseTuple(obj, "iii|i", &r, &g, &b, &a)) {
|
||||
return sf::Color(); // Return default color on parse error
|
||||
}
|
||||
return sf::Color(r, g, b, a);
|
||||
}
|
||||
|
||||
PyObject* UIGridPoint::get_color(PyUIGridPointObject* self, void* closure) {
|
||||
if (reinterpret_cast<long>(closure) == 0) { // color
|
||||
return sfColor_to_PyObject(self->data->color);
|
||||
} else { // color_overlay
|
||||
return sfColor_to_PyObject(self->data->color_overlay);
|
||||
}
|
||||
}
|
||||
|
||||
int UIGridPoint::set_color(PyUIGridPointObject* self, PyObject* value, void* closure) {
|
||||
sf::Color color = PyObject_to_sfColor(value);
|
||||
if (reinterpret_cast<long>(closure) == 0) { // color
|
||||
self->data->color = color;
|
||||
} else { // color_overlay
|
||||
self->data->color_overlay = color;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* UIGridPoint::get_bool_member(PyUIGridPointObject* self, void* closure) {
|
||||
if (reinterpret_cast<long>(closure) == 0) { // walkable
|
||||
return PyBool_FromLong(self->data->walkable);
|
||||
} else { // transparent
|
||||
return PyBool_FromLong(self->data->transparent);
|
||||
}
|
||||
}
|
||||
|
||||
int UIGridPoint::set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure) {
|
||||
if (value == Py_True) {
|
||||
if (reinterpret_cast<long>(closure) == 0) { // walkable
|
||||
self->data->walkable = true;
|
||||
} else { // transparent
|
||||
self->data->transparent = true;
|
||||
}
|
||||
} else if (value == Py_False) {
|
||||
if (reinterpret_cast<long>(closure) == 0) { // walkable
|
||||
self->data->walkable = false;
|
||||
} else { // transparent
|
||||
self->data->transparent = false;
|
||||
}
|
||||
} else {
|
||||
PyErr_SetString(PyExc_ValueError, "Expected a boolean value");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* UIGridPoint::get_int_member(PyUIGridPointObject* self, void* closure) {
|
||||
switch(reinterpret_cast<long>(closure)) {
|
||||
case 0: return PyLong_FromLong(self->data->tilesprite);
|
||||
case 1: return PyLong_FromLong(self->data->tile_overlay);
|
||||
case 2: return PyLong_FromLong(self->data->uisprite);
|
||||
default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int UIGridPoint::set_int_member(PyUIGridPointObject* self, PyObject* value, void* closure) {
|
||||
long val = PyLong_AsLong(value);
|
||||
if (PyErr_Occurred()) return -1;
|
||||
|
||||
switch(reinterpret_cast<long>(closure)) {
|
||||
case 0: self->data->tilesprite = val; break;
|
||||
case 1: self->data->tile_overlay = val; break;
|
||||
case 2: self->data->uisprite = val; break;
|
||||
default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyGetSetDef UIGridPoint::getsetters[] = {
|
||||
{"color", (getter)UIGridPoint::get_color, (setter)UIGridPoint::set_color, "GridPoint color", (void*)0},
|
||||
{"color_overlay", (getter)UIGridPoint::get_color, (setter)UIGridPoint::set_color, "GridPoint color overlay", (void*)1},
|
||||
{"walkable", (getter)UIGridPoint::get_bool_member, (setter)UIGridPoint::set_bool_member, "Is the GridPoint walkable", (void*)0},
|
||||
{"transparent", (getter)UIGridPoint::get_bool_member, (setter)UIGridPoint::set_bool_member, "Is the GridPoint transparent", (void*)1},
|
||||
{"tilesprite", (getter)UIGridPoint::get_int_member, (setter)UIGridPoint::set_int_member, "Tile sprite index", (void*)0},
|
||||
{"tile_overlay", (getter)UIGridPoint::get_int_member, (setter)UIGridPoint::set_int_member, "Tile overlay sprite index", (void*)1},
|
||||
{"uisprite", (getter)UIGridPoint::get_int_member, (setter)UIGridPoint::set_int_member, "UI sprite index", (void*)2},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
PyObject* UIGridPoint::repr(PyUIGridPointObject* self) {
|
||||
std::ostringstream ss;
|
||||
if (!self->data) ss << "<GridPoint (invalid internal object)>";
|
||||
else {
|
||||
auto gp = self->data;
|
||||
ss << "<GridPoint (walkable=" << (gp->walkable ? "True" : "False") << ", transparent=" << (gp->transparent ? "True" : "False") <<
|
||||
", tilesprite=" << gp->tilesprite << ", tile_overlay=" << gp->tile_overlay << ", uisprite=" << gp->uisprite <<
|
||||
")>";
|
||||
}
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
||||
|
||||
PyObject* UIGridPointState::get_bool_member(PyUIGridPointStateObject* self, void* closure) {
|
||||
if (reinterpret_cast<long>(closure) == 0) { // visible
|
||||
return PyBool_FromLong(self->data->visible);
|
||||
} else { // discovered
|
||||
return PyBool_FromLong(self->data->discovered);
|
||||
}
|
||||
}
|
||||
|
||||
int UIGridPointState::set_bool_member(PyUIGridPointStateObject* self, PyObject* value, void* closure) {
|
||||
if (!PyBool_Check(value)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Value must be a boolean");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int truthValue = PyObject_IsTrue(value);
|
||||
if (truthValue < 0) {
|
||||
return -1; // PyObject_IsTrue returns -1 on error
|
||||
}
|
||||
|
||||
if (reinterpret_cast<long>(closure) == 0) { // visible
|
||||
self->data->visible = truthValue;
|
||||
} else { // discovered
|
||||
self->data->discovered = truthValue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyGetSetDef UIGridPointState::getsetters[] = {
|
||||
{"visible", (getter)UIGridPointState::get_bool_member, (setter)UIGridPointState::set_bool_member, "Is the GridPointState visible", (void*)0},
|
||||
{"discovered", (getter)UIGridPointState::get_bool_member, (setter)UIGridPointState::set_bool_member, "Has the GridPointState been discovered", (void*)1},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
PyObject* UIGridPointState::repr(PyUIGridPointStateObject* self) {
|
||||
std::ostringstream ss;
|
||||
if (!self->data) ss << "<GridPointState (invalid internal object)>";
|
||||
else {
|
||||
auto gps = self->data;
|
||||
ss << "<GridPointState (visible=" << (gps->visible ? "True" : "False") << ", discovered=" << (gps->discovered ? "True" : "False") <<
|
||||
")>";
|
||||
}
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Python.h"
|
||||
#include "structmember.h"
|
||||
#include "IndexTexture.h"
|
||||
#include "Resources.h"
|
||||
#include <list>
|
||||
|
||||
#include "PyCallable.h"
|
||||
#include "PyTexture.h"
|
||||
#include "PyColor.h"
|
||||
#include "PyVector.h"
|
||||
#include "PyFont.h"
|
||||
|
||||
static PyObject* sfColor_to_PyObject(sf::Color color);
|
||||
static sf::Color PyObject_to_sfColor(PyObject* obj);
|
||||
|
||||
class UIGrid;
|
||||
class UIEntity;
|
||||
class UIGridPoint;
|
||||
class UIGridPointState;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
UIGridPoint* data;
|
||||
std::shared_ptr<UIGrid> grid;
|
||||
} PyUIGridPointObject;
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
UIGridPointState* data;
|
||||
std::shared_ptr<UIGrid> grid;
|
||||
std::shared_ptr<UIEntity> entity;
|
||||
} PyUIGridPointStateObject;
|
||||
|
||||
// UIGridPoint - revised grid data for each point
|
||||
class UIGridPoint
|
||||
{
|
||||
public:
|
||||
sf::Color color, color_overlay;
|
||||
bool walkable, transparent;
|
||||
int tilesprite, tile_overlay, uisprite;
|
||||
UIGridPoint();
|
||||
|
||||
static int set_int_member(PyUIGridPointObject* self, PyObject* value, void* closure);
|
||||
static PyGetSetDef getsetters[];
|
||||
static PyObject* get_color(PyUIGridPointObject* self, void* closure);
|
||||
static PyObject* get_int_member(PyUIGridPointObject* self, void* closure);
|
||||
static int set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure);
|
||||
static PyObject* get_bool_member(PyUIGridPointObject* self, void* closure);
|
||||
static int set_color(PyUIGridPointObject* self, PyObject* value, void* closure);
|
||||
static PyObject* repr(PyUIGridPointObject* self);
|
||||
};
|
||||
|
||||
// UIGridPointState - entity-specific info for each cell
|
||||
class UIGridPointState
|
||||
{
|
||||
public:
|
||||
bool visible, discovered;
|
||||
|
||||
static PyObject* get_bool_member(PyUIGridPointStateObject* self, void* closure);
|
||||
static int set_bool_member(PyUIGridPointStateObject* self, PyObject* value, void* closure);
|
||||
static PyGetSetDef getsetters[];
|
||||
static PyObject* repr(PyUIGridPointStateObject* self);
|
||||
};
|
||||
|
||||
namespace mcrfpydef {
|
||||
static PyTypeObject PyUIGridPointType = {
|
||||
//PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "mcrfpy.GridPoint",
|
||||
.tp_basicsize = sizeof(PyUIGridPointObject),
|
||||
.tp_itemsize = 0,
|
||||
.tp_repr = (reprfunc)UIGridPoint::repr,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = "UIGridPoint object",
|
||||
.tp_getset = UIGridPoint::getsetters,
|
||||
//.tp_init = (initproc)PyUIGridPoint_init, // TODO Define the init function
|
||||
.tp_new = PyType_GenericNew,
|
||||
};
|
||||
|
||||
static PyTypeObject PyUIGridPointStateType = {
|
||||
//PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "mcrfpy.GridPointState",
|
||||
.tp_basicsize = sizeof(PyUIGridPointStateObject),
|
||||
.tp_itemsize = 0,
|
||||
.tp_repr = (reprfunc)UIGridPointState::repr,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = "UIGridPointState object", // TODO: Add PyUIGridPointState tp_init
|
||||
.tp_getset = UIGridPointState::getsetters,
|
||||
.tp_new = PyType_GenericNew,
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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:
|
||||
|
||||
|
||||
|
||||
};
|
217
src/UISprite.cpp
217
src/UISprite.cpp
|
@ -1,217 +0,0 @@
|
|||
#include "UISprite.h"
|
||||
#include "GameEngine.h"
|
||||
|
||||
UIDrawable* UISprite::click_at(sf::Vector2f point)
|
||||
{
|
||||
if (click_callable)
|
||||
{
|
||||
if(sprite.getGlobalBounds().contains(point)) return this;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UISprite::UISprite() {}
|
||||
|
||||
UISprite::UISprite(std::shared_ptr<PyTexture> _ptex, int _sprite_index, sf::Vector2f _pos, float _scale)
|
||||
: ptex(_ptex), sprite_index(_sprite_index)
|
||||
{
|
||||
sprite = ptex->sprite(sprite_index, _pos, sf::Vector2f(_scale, _scale));
|
||||
}
|
||||
|
||||
/*
|
||||
void UISprite::render(sf::Vector2f offset)
|
||||
{
|
||||
sprite.move(offset);
|
||||
Resources::game->getWindow().draw(sprite);
|
||||
sprite.move(-offset);
|
||||
}
|
||||
*/
|
||||
|
||||
void UISprite::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||
{
|
||||
sprite.move(offset);
|
||||
target.draw(sprite);
|
||||
sprite.move(-offset);
|
||||
}
|
||||
|
||||
void UISprite::setPosition(sf::Vector2f pos)
|
||||
{
|
||||
sprite.setPosition(pos);
|
||||
}
|
||||
|
||||
void UISprite::setScale(sf::Vector2f s)
|
||||
{
|
||||
sprite.setScale(s);
|
||||
}
|
||||
|
||||
void UISprite::setTexture(std::shared_ptr<PyTexture> _ptex, int _sprite_index)
|
||||
{
|
||||
ptex = _ptex;
|
||||
if (_sprite_index != -1) // if you are changing textures, there's a good chance you need a new index too
|
||||
sprite_index = _sprite_index;
|
||||
sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale());
|
||||
}
|
||||
|
||||
void UISprite::setSpriteIndex(int _sprite_index)
|
||||
{
|
||||
sprite_index = _sprite_index;
|
||||
sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale());
|
||||
}
|
||||
|
||||
sf::Vector2f UISprite::getScale()
|
||||
{
|
||||
return sprite.getScale();
|
||||
}
|
||||
|
||||
sf::Vector2f UISprite::getPosition()
|
||||
{
|
||||
return sprite.getPosition();
|
||||
}
|
||||
|
||||
std::shared_ptr<PyTexture> UISprite::getTexture()
|
||||
{
|
||||
return ptex;
|
||||
}
|
||||
|
||||
int UISprite::getSpriteIndex()
|
||||
{
|
||||
return sprite_index;
|
||||
}
|
||||
|
||||
PyObjectsEnum UISprite::derived_type()
|
||||
{
|
||||
return PyObjectsEnum::UISPRITE;
|
||||
}
|
||||
|
||||
PyObject* UISprite::get_float_member(PyUISpriteObject* self, void* closure)
|
||||
{
|
||||
auto member_ptr = reinterpret_cast<long>(closure);
|
||||
if (member_ptr == 0)
|
||||
return PyFloat_FromDouble(self->data->getPosition().x);
|
||||
else if (member_ptr == 1)
|
||||
return PyFloat_FromDouble(self->data->getPosition().y);
|
||||
else if (member_ptr == 2)
|
||||
return PyFloat_FromDouble(self->data->getScale().x); // scale X and Y are identical, presently
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int UISprite::set_float_member(PyUISpriteObject* self, PyObject* value, void* closure)
|
||||
{
|
||||
float val;
|
||||
auto member_ptr = reinterpret_cast<long>(closure);
|
||||
if (PyFloat_Check(value))
|
||||
{
|
||||
val = PyFloat_AsDouble(value);
|
||||
}
|
||||
else if (PyLong_Check(value))
|
||||
{
|
||||
val = PyLong_AsLong(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "Value must be a floating point number.");
|
||||
return -1;
|
||||
}
|
||||
if (member_ptr == 0) //x
|
||||
self->data->setPosition(sf::Vector2f(val, self->data->getPosition().y));
|
||||
else if (member_ptr == 1) //y
|
||||
self->data->setPosition(sf::Vector2f(self->data->getPosition().x, val));
|
||||
else if (member_ptr == 2) // scale
|
||||
self->data->setScale(sf::Vector2f(val, val));
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* UISprite::get_int_member(PyUISpriteObject* self, void* closure)
|
||||
{
|
||||
auto member_ptr = reinterpret_cast<long>(closure);
|
||||
if (true) {}
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return PyLong_FromDouble(self->data->getSpriteIndex());
|
||||
}
|
||||
|
||||
int UISprite::set_int_member(PyUISpriteObject* self, PyObject* value, void* closure)
|
||||
{
|
||||
int val;
|
||||
auto member_ptr = reinterpret_cast<long>(closure);
|
||||
if (PyLong_Check(value))
|
||||
{
|
||||
val = PyLong_AsLong(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "Value must be an integer.");
|
||||
return -1;
|
||||
}
|
||||
self->data->setSpriteIndex(val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* UISprite::get_texture(PyUISpriteObject* self, void* closure)
|
||||
{
|
||||
return self->data->getTexture()->pyObject();
|
||||
}
|
||||
|
||||
int UISprite::set_texture(PyUISpriteObject* self, PyObject* value, void* closure)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyGetSetDef UISprite::getsetters[] = {
|
||||
{"x", (getter)UISprite::get_float_member, (setter)UISprite::set_float_member, "X coordinate of top-left corner", (void*)0},
|
||||
{"y", (getter)UISprite::get_float_member, (setter)UISprite::set_float_member, "Y coordinate of top-left corner", (void*)1},
|
||||
{"scale", (getter)UISprite::get_float_member, (setter)UISprite::set_float_member, "Size factor", (void*)2},
|
||||
{"sprite_number", (getter)UISprite::get_int_member, (setter)UISprite::set_int_member, "Which sprite on the texture is shown", NULL},
|
||||
{"texture", (getter)UISprite::get_texture, (setter)UISprite::set_texture, "Texture object", NULL},
|
||||
{"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UISPRITE},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyObject* UISprite::repr(PyUISpriteObject* self)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
if (!self->data) ss << "<Sprite (invalid internal object)>";
|
||||
else {
|
||||
//auto sprite = self->data->sprite;
|
||||
ss << "<Sprite (x=" << self->data->getPosition().x << ", y=" << self->data->getPosition().y << ", " <<
|
||||
"scale=" << self->data->getScale().x << ", " <<
|
||||
"sprite_number=" << self->data->getSpriteIndex() << ")>";
|
||||
}
|
||||
std::string repr_str = ss.str();
|
||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||
}
|
||||
|
||||
int UISprite::init(PyUISpriteObject* self, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
//std::cout << "Init called\n";
|
||||
static const char* keywords[] = { "x", "y", "texture", "sprite_index", "scale", nullptr };
|
||||
float x = 0.0f, y = 0.0f, scale = 1.0f;
|
||||
int sprite_index;
|
||||
PyObject* texture;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffOif",
|
||||
const_cast<char**>(keywords), &x, &y, &texture, &sprite_index, &scale))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// check types for texture
|
||||
//if (texture != NULL && !PyObject_IsInstance(texture, (PyObject*)&PyTextureType)){
|
||||
if (texture != NULL && !PyObject_IsInstance(texture, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))){
|
||||
PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance");
|
||||
return -1;
|
||||
}
|
||||
auto pytexture = (PyTextureObject*)texture;
|
||||
self->data = std::make_shared<UISprite>(pytexture->data, sprite_index, sf::Vector2f(x, y), scale);
|
||||
self->data->setPosition(sf::Vector2f(x, y));
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
#pragma once
|
||||
#include "Common.h"
|
||||
#include "Python.h"
|
||||
#include "structmember.h"
|
||||
#include "IndexTexture.h"
|
||||
#include "Resources.h"
|
||||
#include <list>
|
||||
|
||||
#include "PyCallable.h"
|
||||
#include "PyTexture.h"
|
||||
#include "PyColor.h"
|
||||
#include "PyVector.h"
|
||||
#include "PyFont.h"
|
||||
#include "UIDrawable.h"
|
||||
#include "UIBase.h"
|
||||
|
||||
class UISprite: public UIDrawable
|
||||
{
|
||||
private:
|
||||
int sprite_index;
|
||||
sf::Sprite sprite;
|
||||
protected:
|
||||
std::shared_ptr<PyTexture> ptex;
|
||||
public:
|
||||
UISprite();
|
||||
UISprite(std::shared_ptr<PyTexture>, int, sf::Vector2f, float);
|
||||
void update();
|
||||
void render(sf::Vector2f, sf::RenderTarget&) override final;
|
||||
virtual UIDrawable* click_at(sf::Vector2f point) override final;
|
||||
|
||||
//void render(sf::Vector2f, sf::RenderTexture&);
|
||||
|
||||
void setPosition(sf::Vector2f);
|
||||
sf::Vector2f getPosition();
|
||||
void setScale(sf::Vector2f);
|
||||
sf::Vector2f getScale();
|
||||
void setSpriteIndex(int);
|
||||
int getSpriteIndex();
|
||||
|
||||
void setTexture(std::shared_ptr<PyTexture> _ptex, int _sprite_index=-1);
|
||||
std::shared_ptr<PyTexture> getTexture();
|
||||
|
||||
PyObjectsEnum derived_type() override final;
|
||||
|
||||
|
||||
static PyObject* get_float_member(PyUISpriteObject* self, void* closure);
|
||||
static int set_float_member(PyUISpriteObject* self, PyObject* value, void* closure);
|
||||
static PyObject* get_int_member(PyUISpriteObject* self, void* closure);
|
||||
static int set_int_member(PyUISpriteObject* self, PyObject* value, void* closure);
|
||||
static PyObject* get_texture(PyUISpriteObject* self, void* closure);
|
||||
static int set_texture(PyUISpriteObject* self, PyObject* value, void* closure);
|
||||
static PyGetSetDef getsetters[];
|
||||
static PyObject* repr(PyUISpriteObject* self);
|
||||
static int init(PyUISpriteObject* self, PyObject* args, PyObject* kwds);
|
||||
|
||||
};
|
||||
|
||||
namespace mcrfpydef {
|
||||
static PyTypeObject PyUISpriteType = {
|
||||
//PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "mcrfpy.Sprite",
|
||||
.tp_basicsize = sizeof(PyUISpriteObject),
|
||||
.tp_itemsize = 0,
|
||||
.tp_dealloc = (destructor)[](PyObject* self)
|
||||
{
|
||||
PyUISpriteObject* obj = (PyUISpriteObject*)self;
|
||||
// release reference to font object
|
||||
//if (obj->texture) Py_DECREF(obj->texture);
|
||||
obj->data.reset();
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
},
|
||||
.tp_repr = (reprfunc)UISprite::repr,
|
||||
//.tp_hash = NULL,
|
||||
//.tp_iter
|
||||
//.tp_iternext
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = PyDoc_STR("docstring"),
|
||||
//.tp_methods = PyUIFrame_methods,
|
||||
//.tp_members = PyUIFrame_members,
|
||||
.tp_getset = UISprite::getsetters,
|
||||
//.tp_base = NULL,
|
||||
.tp_init = (initproc)UISprite::init,
|
||||
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
|
||||
{
|
||||
PyUISpriteObject* self = (PyUISpriteObject*)type->tp_alloc(type, 0);
|
||||
//if (self) self->data = std::make_shared<UICaption>();
|
||||
return (PyObject*)self;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -4,10 +4,8 @@
|
|||
|
||||
UITestScene::UITestScene(GameEngine* g) : Scene(g)
|
||||
{
|
||||
registerAction(ActionCode::KEY + sf::Keyboard::Grave, "debug_menu");
|
||||
|
||||
text.setFont(game->getFont());
|
||||
text.setString("UITest: surprised to be here? game.py raised an exception.");
|
||||
text.setString("Test Scene for UI elements");
|
||||
text.setCharacterSize(24);
|
||||
|
||||
|
||||
|
@ -61,15 +59,15 @@ UITestScene::UITestScene(GameEngine* g) : Scene(g)
|
|||
//ui_elements.push_back(&e1);
|
||||
//ui_elements.push_back(&e2);
|
||||
|
||||
//t.loadFromFile("./assets/kenney_tinydungeon.png");
|
||||
//t.setSmooth(false);
|
||||
//auto* indextex = new IndexTexture(t, 16, 12, 11);
|
||||
//Resources::game->textures.push_back(*indextex);
|
||||
t.loadFromFile("./assets/kenney_tinydungeon.png");
|
||||
t.setSmooth(false);
|
||||
auto* indextex = new IndexTexture(t, 16, 12, 11);
|
||||
Resources::game->textures.push_back(*indextex);
|
||||
|
||||
|
||||
auto ptex = std::make_shared<PyTexture>("./assets/kenney_tinydungeon.png", 16, 16);
|
||||
|
||||
//std::cout << Resources::game->textures.size() << " textures loaded.\n";
|
||||
auto e3 = std::make_shared<UISprite>(ptex, 84, sf::Vector2f(10, 10), 4.0);
|
||||
auto e3 = std::make_shared<UISprite>();
|
||||
|
||||
// Make UISprite more like IndexSprite: this is bad
|
||||
//e3->x = 10; e3->y = 10;
|
||||
|
@ -79,19 +77,19 @@ UITestScene::UITestScene(GameEngine* g) : Scene(g)
|
|||
//e3->update();
|
||||
|
||||
// This goes to show how inconvenient the default constructor is. It should be removed
|
||||
//e3->itex = &Resources::game->textures[0];
|
||||
//e3->sprite.setTexture(e3->itex->texture);
|
||||
//e3->sprite_index = 84;
|
||||
//e3->sprite.setTextureRect(e3->itex->spriteCoordinates(e3->sprite_index));
|
||||
//e3->setPosition(10, 10);
|
||||
//e3->setScale(4.0f);
|
||||
e3->itex = &Resources::game->textures[0];
|
||||
e3->sprite.setTexture(e3->itex->texture);
|
||||
e3->sprite_index = 84;
|
||||
e3->sprite.setTextureRect(e3->itex->spriteCoordinates(e3->sprite_index));
|
||||
e3->setPosition(10, 10);
|
||||
e3->setScale(4.0f);
|
||||
|
||||
e1aa->children->push_back(e3);
|
||||
|
||||
|
||||
|
||||
auto e4 = std::make_shared<UISprite>(
|
||||
ptex, //indextex, //&Resources::game->textures[0],
|
||||
indextex, //&Resources::game->textures[0],
|
||||
85, sf::Vector2f(90, 10), 4.0);
|
||||
e1aa->children->push_back(e4);
|
||||
//std::cout << "UISprite built: " << e4->sprite.getPosition().x << " " << e4->sprite.getPosition().y << " " << e4->sprite.getScale().x << " " <<
|
||||
|
@ -103,33 +101,6 @@ UITestScene::UITestScene(GameEngine* g) : Scene(g)
|
|||
if (ui)
|
||||
std::cout << "pointer to ui_elements now shows size=" << ui->size() << std::endl;
|
||||
*/
|
||||
|
||||
// UIGrid test: (in grid cells) ( in screen pixels )
|
||||
// constructor args: w h texture x y w h
|
||||
auto e5 = std::make_shared<UIGrid>(4, 4, ptex, sf::Vector2f(550, 150), sf::Vector2f(200, 200));
|
||||
e5->zoom=2.0;
|
||||
e5->points[0].color = sf::Color(255, 0, 0);
|
||||
e5->points[1].tilesprite = 1;
|
||||
e5->points[5].color = sf::Color(0, 255, 0);
|
||||
e5->points[6].tilesprite = 2;
|
||||
e5->points[10].color = sf::Color(0, 0, 255);
|
||||
e5->points[11].tilesprite = 3;
|
||||
e5->points[15].color = sf::Color(255, 255, 255);
|
||||
|
||||
ui_elements->push_back(e5);
|
||||
|
||||
//UIEntity test:
|
||||
// asdf
|
||||
// TODO - reimplement UISprite style rendering within UIEntity class. Entities don't have a screen pixel position, they have a grid position, and grid sets zoom when rendering them.
|
||||
auto e5a = std::make_shared<UIEntity>(*e5); // this basic constructor sucks: sprite position + zoom are irrelevant for UIEntity.
|
||||
e5a->grid = e5;
|
||||
//auto e5as = UISprite(indextex, 85, sf::Vector2f(0, 0), 1.0);
|
||||
//e5a->sprite = e5as; // will copy constructor even exist for UISprite...?
|
||||
e5a->sprite = UISprite(ptex, 85, sf::Vector2f(0, 0), 1.0);
|
||||
e5a->position = sf::Vector2f(1, 0);
|
||||
|
||||
e5->entities->push_back(e5a);
|
||||
|
||||
}
|
||||
|
||||
void UITestScene::update()
|
||||
|
@ -143,9 +114,6 @@ void UITestScene::doAction(std::string name, std::string type)
|
|||
//if (name.compare("start_game") == 0 and type.compare("start") == 0)
|
||||
if(ACTION("start_game", "start"))
|
||||
game->changeScene("py");
|
||||
else if ACTIONONCE("debug_menu") {
|
||||
McRFPy_API::REPL();
|
||||
}
|
||||
/*
|
||||
else if(ACTIONONCE("up"))
|
||||
game->getWindow().setSize(sf::Vector2u(1280, 800));
|
||||
|
@ -154,7 +122,7 @@ void UITestScene::doAction(std::string name, std::string type)
|
|||
*/
|
||||
}
|
||||
|
||||
void UITestScene::render()
|
||||
void UITestScene::sRender()
|
||||
{
|
||||
game->getWindow().clear();
|
||||
game->getWindow().draw(text);
|
||||
|
@ -177,5 +145,5 @@ void UITestScene::render()
|
|||
|
||||
game->getWindow().display();
|
||||
|
||||
//McRFPy_API::REPL();
|
||||
McRFPy_API::REPL();
|
||||
}
|
||||
|
|
|
@ -18,5 +18,5 @@ public:
|
|||
UITestScene(GameEngine*);
|
||||
void update() override final;
|
||||
void doAction(std::string, std::string) override final;
|
||||
void render() override final;
|
||||
void sRender() override final;
|
||||
};
|
||||
|
|
|
@ -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}>"
|
|
@ -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()
|
||||
|
|
@ -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()
|
|
@ -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>"
|
|
@ -1,393 +0,0 @@
|
|||
import mcrfpy
|
||||
import random
|
||||
#t = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
|
||||
t = mcrfpy.Texture("assets/kenney_TD_MR_IP.png", 16, 16)
|
||||
#def iterable_entities(grid):
|
||||
# """Workaround for UIEntityCollection bug; see issue #72"""
|
||||
# entities = []
|
||||
# for i in range(len(grid.entities)):
|
||||
# entities.append(grid.entities[i])
|
||||
# return entities
|
||||
|
||||
class COSEntity(): #mcrfpy.Entity): # Fake mcrfpy.Entity integration; engine bugs workarounds
|
||||
def __init__(self, g:mcrfpy.Grid, x=0, y=0, sprite_num=86, *, game):
|
||||
#self.e = mcrfpy.Entity((x, y), t, sprite_num)
|
||||
#super().__init__((x, y), t, sprite_num)
|
||||
self._entity = mcrfpy.Entity((x, y), t, sprite_num)
|
||||
#grid.entities.append(self.e)
|
||||
self.grid = g
|
||||
#g.entities.append(self._entity)
|
||||
self.game = game
|
||||
self.game.add_entity(self)
|
||||
|
||||
## Wrapping mcfrpy.Entity properties to emulate derived class... see issue #76
|
||||
@property
|
||||
def draw_pos(self):
|
||||
return self._entity.draw_pos
|
||||
|
||||
@draw_pos.setter
|
||||
def draw_pos(self, value):
|
||||
self._entity.draw_pos = value
|
||||
|
||||
@property
|
||||
def sprite_number(self):
|
||||
return self._entity.sprite_number
|
||||
|
||||
@sprite_number.setter
|
||||
def sprite_number(self, value):
|
||||
self._entity.sprite_number = value
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__name__} ({self.draw_pos})>"
|
||||
|
||||
def die(self):
|
||||
# ugly workaround! grid.entities isn't really iterable (segfaults)
|
||||
for i in range(len(self.grid.entities)):
|
||||
e = self.grid.entities[i]
|
||||
if e == self._entity:
|
||||
#if e == self:
|
||||
self.grid.entities.remove(i)
|
||||
break
|
||||
else:
|
||||
print(f"!!! {self!r} wasn't removed from grid on call to die()")
|
||||
|
||||
def bump(self, other, dx, dy, test=False):
|
||||
raise NotImplementedError
|
||||
|
||||
def do_move(self, tx, ty):
|
||||
"""Base class method to move this entity
|
||||
Assumes try_move succeeded, for everyone!
|
||||
from: self._entity.draw_pos
|
||||
to: (tx, ty)
|
||||
calls ev_exit for every entity at (draw_pos)
|
||||
calls ev_enter for every entity at (tx, ty)
|
||||
"""
|
||||
old_pos = self.draw_pos
|
||||
self.draw_pos = (tx, ty)
|
||||
for e in self.game.entities:
|
||||
if e is self: continue
|
||||
if e.draw_pos == old_pos: e.ev_exit(self)
|
||||
for e in self.game.entities:
|
||||
if e is self: continue
|
||||
if e.draw_pos == (tx, ty): e.ev_enter(self)
|
||||
|
||||
def act(self):
|
||||
pass
|
||||
|
||||
def ev_enter(self, other):
|
||||
pass
|
||||
|
||||
def ev_exit(self, other):
|
||||
pass
|
||||
|
||||
def try_move(self, dx, dy, test=False):
|
||||
x_max, y_max = self.grid.grid_size
|
||||
tx, ty = int(self.draw_pos[0] + dx), int(self.draw_pos[1] + dy)
|
||||
#for e in iterable_entities(self.grid):
|
||||
|
||||
# sorting entities to test against the boulder instead of the button when they overlap.
|
||||
for e in sorted(self.game.entities, key = lambda i: i.draw_order, reverse = True):
|
||||
if e.draw_pos == (tx, ty):
|
||||
#print(f"bumping {e}")
|
||||
return e.bump(self, dx, dy)
|
||||
|
||||
if tx < 0 or tx >= x_max:
|
||||
return False
|
||||
if ty < 0 or ty >= y_max:
|
||||
return False
|
||||
if self.grid.at((tx, ty)).walkable == True:
|
||||
if not test:
|
||||
#self.draw_pos = (tx, ty)
|
||||
self.do_move(tx, ty)
|
||||
return True
|
||||
else:
|
||||
#print("Bonk")
|
||||
return False
|
||||
|
||||
def _relative_move(self, dx, dy):
|
||||
tx, ty = int(self.draw_pos[0] + dx), int(self.draw_pos[1] + dy)
|
||||
#self.draw_pos = (tx, ty)
|
||||
self.do_move(tx, ty)
|
||||
|
||||
class Equippable:
|
||||
def __init__(self, hands = 0, hp_healing = 0, damage = 0, defense = 0, zap_damage = 1, zap_cooldown = 10, sprite = 129):
|
||||
self.hands = hands
|
||||
self.hp_healing = hp_healing
|
||||
self.damage = damage
|
||||
self.defense = defense
|
||||
self.zap_damage = zap_damage
|
||||
self.zap_cooldown = zap_cooldown
|
||||
self.zap_cooldown_remaining = 0
|
||||
self.sprite = self.sprite
|
||||
self.quality = 0
|
||||
|
||||
def tick(self):
|
||||
if self.zap_cooldown_remaining:
|
||||
self.zap_cooldown_remaining -= 1
|
||||
if self.zap_cooldown_remaining < 0: self.zap_cooldown_remaining = 0
|
||||
|
||||
def __repr__(self):
|
||||
cooldown_str = f'({self.zap_cooldown_remaining} rounds until ready)'
|
||||
return f"<Equippable hands={self.hands}, hp_healing={self.hp_healing}, damage={self.damage}, defense={self.defense}, zap_damage={self.zap_damage}, zap_cooldown={self.zap_cooldown}{cooldown_str if self.zap_cooldown_remaining else ''}, sprite={self.sprite}>"
|
||||
|
||||
def classify(self):
|
||||
categories = []
|
||||
if self.hands==0:
|
||||
categories.append("consumable")
|
||||
elif self.damage > 0:
|
||||
categories.append(f"{self.hands}-handed weapon")
|
||||
elif self.defense > 0:
|
||||
categories.append(f"defense")
|
||||
elif self.zap_damage > 0:
|
||||
categories.append("{self.hands}-handed magic weapon")
|
||||
if len(categories) == 0:
|
||||
return "unclassifiable"
|
||||
elif len(categories) == 1:
|
||||
return categories[0]
|
||||
else:
|
||||
return "Erratic: " + ', '.join(categories)
|
||||
|
||||
#def compare(self, other):
|
||||
# my_class = self.classify()
|
||||
# o_class = other.classify()
|
||||
# if my_class == "unclassifiable" or o_class == "unclassifiable":
|
||||
# return None
|
||||
# if my_class == "consumable":
|
||||
# return other.hp_healing - self.hp_healing
|
||||
|
||||
|
||||
class PlayerEntity(COSEntity):
|
||||
def __init__(self, *, game):
|
||||
#print(f"spawn at origin")
|
||||
self.draw_order = 10
|
||||
super().__init__(game.grid, 0, 0, sprite_num=84, game=game)
|
||||
self.hp = 10
|
||||
self.max_hp = 10
|
||||
self.base_damage = 1
|
||||
self.base_defense = 0
|
||||
self.luck = 0
|
||||
self.archetype = None
|
||||
self.equipped = []
|
||||
self.inventory = []
|
||||
|
||||
def tick(self):
|
||||
for i in self.equipped:
|
||||
i.tick()
|
||||
|
||||
def calc_damage(self):
|
||||
dmg = self.base_damage
|
||||
for i in self.equipped:
|
||||
dmg += i.damage
|
||||
return dmg
|
||||
|
||||
def calc_defense(self):
|
||||
defense = self.base_defense
|
||||
for i in self.equipped:
|
||||
defense += i.damage
|
||||
return defense
|
||||
|
||||
def do_zap(self):
|
||||
pass
|
||||
|
||||
def bump(self, other, dx, dy, test=False):
|
||||
if type(other) == BoulderEntity:
|
||||
print("Boulder hit w/ knockback!")
|
||||
return self.game.pull_boulder_move((-dx, -dy), other)
|
||||
print(f"oof, ouch, {other} bumped the player - {other.base_damage} damage from {other}")
|
||||
self.hp = max(self.hp - max(other.base_damage - self.calc_defense(), 0), 0)
|
||||
|
||||
def respawn(self, avoid=None):
|
||||
# find spawn point
|
||||
x_max, y_max = g.size
|
||||
spawn_points = []
|
||||
for x in range(x_max):
|
||||
for y in range(y_max):
|
||||
if g.at((x, y)).walkable:
|
||||
spawn_points.append((x, y))
|
||||
random.shuffle(spawn_points)
|
||||
## TODO - find other entities to avoid spawning on top of
|
||||
for spawn in spawn_points:
|
||||
for e in avoid or []:
|
||||
if e.draw_pos == spawn: break
|
||||
else:
|
||||
break
|
||||
self.draw_pos = spawn
|
||||
|
||||
def __repr__(self):
|
||||
return f"<PlayerEntity {self.draw_pos}, {self.grid}>"
|
||||
|
||||
|
||||
class BoulderEntity(COSEntity):
|
||||
def __init__(self, x, y, *, game):
|
||||
self.draw_order = 8
|
||||
super().__init__(game.grid, x, y, 66, game=game)
|
||||
|
||||
def bump(self, other, dx, dy, test=False):
|
||||
if type(other) == BoulderEntity:
|
||||
#print("Boulders can't push boulders")
|
||||
return False
|
||||
elif type(other) == EnemyEntity:
|
||||
if not other.can_push: return False
|
||||
#tx, ty = int(self.e.position[0] + dx), int(self.e.position[1] + dy)
|
||||
tx, ty = int(self.draw_pos[0] + dx), int(self.draw_pos[1] + dy)
|
||||
# Is the boulder blocked the same direction as the bumper? If not, let's both move
|
||||
old_pos = int(self.draw_pos[0]), int(self.draw_pos[1])
|
||||
if self.try_move(dx, dy, test=test):
|
||||
if not test:
|
||||
other.do_move(*old_pos)
|
||||
#other.draw_pos = old_pos
|
||||
return True
|
||||
|
||||
class ButtonEntity(COSEntity):
|
||||
def __init__(self, x, y, exit_entity, *, game):
|
||||
self.draw_order = 1
|
||||
super().__init__(game.grid, x, y, 250, game=game)
|
||||
self.exit = exit_entity
|
||||
|
||||
def ev_enter(self, other):
|
||||
print("Button makes a satisfying click!")
|
||||
self.exit.unlock()
|
||||
|
||||
def ev_exit(self, other):
|
||||
print("Button makes a disappointing click.")
|
||||
self.exit.lock()
|
||||
|
||||
def bump(self, other, dx, dy, test=False):
|
||||
#if type(other) == BoulderEntity:
|
||||
# self.exit.unlock()
|
||||
# TODO: unlock, and then lock again, when player steps on/off
|
||||
if not test:
|
||||
pos = int(self.draw_pos[0]), int(self.draw_pos[1])
|
||||
other.do_move(*pos)
|
||||
return True
|
||||
|
||||
class ExitEntity(COSEntity):
|
||||
def __init__(self, x, y, bx, by, *, game):
|
||||
self.draw_order = 2
|
||||
super().__init__(game.grid, x, y, 45, game=game)
|
||||
self.my_button = ButtonEntity(bx, by, self, game=game)
|
||||
self.unlocked = False
|
||||
#global cos_entities
|
||||
#cos_entities.append(self.my_button)
|
||||
|
||||
def unlock(self):
|
||||
self.sprite_number = 21
|
||||
self.unlocked = True
|
||||
|
||||
def lock(self):
|
||||
self.sprite_number = 45
|
||||
self.unlocked = False
|
||||
|
||||
def bump(self, other, dx, dy, test=False):
|
||||
if type(other) == BoulderEntity:
|
||||
return False
|
||||
if self.unlocked:
|
||||
if not test:
|
||||
other._relative_move(dx, dy)
|
||||
#TODO - player go down a level logic
|
||||
if type(other) == PlayerEntity:
|
||||
self.game.depth += 1
|
||||
print(f"welcome to level {self.game.depth}")
|
||||
self.game.create_level(self.game.depth)
|
||||
self.game.swap_level(self.game.level, self.game.spawn_point)
|
||||
|
||||
class EnemyEntity(COSEntity):
|
||||
def __init__(self, x, y, hp=2, base_damage=1, base_defense=0, sprite=123, can_push=False, crushable=True, sight=8, move_cooldown=1, *, game):
|
||||
self.draw_order = 7
|
||||
super().__init__(game.grid, x, y, sprite, game=game)
|
||||
self.hp = hp
|
||||
self.base_damage = base_damage
|
||||
self.base_defense = base_defense
|
||||
self.base_sprite = sprite
|
||||
self.can_push = can_push
|
||||
self.crushable = crushable
|
||||
self.sight = sight
|
||||
self.move_cooldown = move_cooldown
|
||||
self.moved_last = 0
|
||||
|
||||
def bump(self, other, dx, dy, test=False):
|
||||
if self.hp == 0:
|
||||
if not test:
|
||||
old_pos = int(self.draw_pos[0]), int(self.draw_pos[1])
|
||||
other.do_move(*old_pos)
|
||||
return True
|
||||
if type(other) == PlayerEntity:
|
||||
# TODO - get damage from player, take damage, decide to die or not
|
||||
d = other.calc_damage()
|
||||
self.hp -= d
|
||||
self.hp = max(self.hp, 0)
|
||||
if self.hp == 0:
|
||||
self._entity.sprite_number = self.base_sprite + 246
|
||||
self.draw_order = 1
|
||||
print(f"Player hit for {d}. HP = {self.hp}")
|
||||
#self.hp = 0
|
||||
return False
|
||||
elif type(other) == BoulderEntity:
|
||||
if not self.crushable and self.hp > 0:
|
||||
print("Uncrushable!")
|
||||
return False
|
||||
if self.hp > 0:
|
||||
print("Ouch, my entire body!!")
|
||||
self._entity.sprite_number = self.base_sprite + 246
|
||||
self.hp = 0
|
||||
old_pos = int(self.draw_pos[0]), int(self.draw_pos[1])
|
||||
if not test:
|
||||
other.do_move(*old_pos)
|
||||
return True
|
||||
|
||||
def act(self):
|
||||
if self.hp > 0:
|
||||
# if player nearby: attack
|
||||
x, y = self.draw_pos
|
||||
px, py = self.game.player.draw_pos
|
||||
for d in ((1, 0), (0, 1), (-1, 0), (1, 0)):
|
||||
if int(x + d[0]) == int(px) and int(y + d[1]) == int(py):
|
||||
self.try_move(*d)
|
||||
return
|
||||
|
||||
# slow movement (doesn't affect ability to attack)
|
||||
if self.moved_last < 0:
|
||||
self.moved_last -= 1
|
||||
return
|
||||
else:
|
||||
self.moved_last = self.move_cooldown
|
||||
|
||||
# if player is not nearby, wander
|
||||
if abs(x - px) + abs(y - py) > self.sight:
|
||||
d = random.choice(((1, 0), (0, 1), (-1, 0), (1, 0)))
|
||||
self.try_move(*d)
|
||||
|
||||
# if can_push and player in a line: KICK
|
||||
if self.can_push:
|
||||
if int(x) == int(px):
|
||||
pass # vertical kick
|
||||
elif int(y) == int(py):
|
||||
pass # horizontal kick
|
||||
|
||||
# else, nearby pursue
|
||||
towards = []
|
||||
dist = lambda dx, dy: abs(px - (x + dx)) + abs(py - (y + dy))
|
||||
current_dist = dist(0, 0)
|
||||
for d in ((1, 0), (0, 1), (-1, 0), (1, 0)):
|
||||
if dist(*d) <= current_dist + 0.75: towards.append(d)
|
||||
print(current_dist, towards)
|
||||
target_dir = random.choice(towards)
|
||||
self.try_move(*target_dir)
|
||||
|
||||
|
||||
class TreasureEntity(COSEntity):
|
||||
def __init__(self, x, y, treasure_table=None, *, game):
|
||||
self.draw_order = 6
|
||||
super().__init__(game.grid, x, y, 89, game=game)
|
||||
self.popped = False
|
||||
|
||||
def bump(self, other, dx, dy, test=False):
|
||||
if type(other) != PlayerEntity:
|
||||
return False
|
||||
if self.popped:
|
||||
print("It's already open.")
|
||||
return
|
||||
print("Take me, I'm yours!")
|
||||
self._entity.sprite_number = 91
|
||||
self.popped = True
|
||||
|
|
@ -1,285 +0,0 @@
|
|||
import random
|
||||
import mcrfpy
|
||||
import cos_tiles as ct
|
||||
|
||||
t = mcrfpy.Texture("assets/kenney_TD_MR_IP.png", 16, 16)
|
||||
|
||||
def binary_space_partition(x, y, w, h):
|
||||
d = random.choices(["vert", "horiz"], weights=[w/(w+h), h/(w+h)])[0]
|
||||
split = random.randint(30, 70) / 100.0
|
||||
if d == "vert":
|
||||
coord = int(w * split)
|
||||
return (x, y, coord, h), (x+coord, y, w-coord, h)
|
||||
else: # horizontal
|
||||
coord = int(h * split)
|
||||
return (x, y, w, coord), (x, y+coord, w, h-coord)
|
||||
|
||||
room_area = lambda x, y, w, h: w * h
|
||||
|
||||
class BinaryRoomNode:
|
||||
def __init__(self, xywh):
|
||||
self.data = xywh
|
||||
self.left = None
|
||||
self.right = None
|
||||
|
||||
def __repr__(self):
|
||||
return f"<RoomNode {self.data}>"
|
||||
|
||||
def center(self):
|
||||
x, y, w, h = self.data
|
||||
return (x + w // 2, y + h // 2)
|
||||
|
||||
def split(self):
|
||||
new_data = binary_space_partition(*self.data)
|
||||
self.left = BinaryRoomNode(new_data[0])
|
||||
self.right = BinaryRoomNode(new_data[1])
|
||||
|
||||
def walk(self):
|
||||
if self.left and self.right:
|
||||
return self.left.walk() + self.right.walk()
|
||||
return [self]
|
||||
|
||||
def contains(self, pt):
|
||||
x, y, w, h = self.data
|
||||
tx, ty = pt
|
||||
return x <= tx <= x + w and y <= ty <= y + h
|
||||
|
||||
class RoomGraph:
|
||||
def __init__(self, xywh):
|
||||
self.root = BinaryRoomNode(xywh)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<RoomGraph, root={self.root}, {len(self.walk())} rooms>"
|
||||
|
||||
def walk(self):
|
||||
w = self.root.walk() if self.root else []
|
||||
#print(w)
|
||||
return w
|
||||
|
||||
def room_coord(room, margin=0):
|
||||
x, y, w, h = room.data
|
||||
#print(x,y,w,h, f'{margin=}', end=';')
|
||||
w -= 1
|
||||
h -= 1
|
||||
margin += 1
|
||||
x += margin
|
||||
y += margin
|
||||
w -= margin
|
||||
h -= margin
|
||||
if w < 0: w = 0
|
||||
if h < 0: h = 0
|
||||
#print(x,y,w,h, end=' -> ')
|
||||
tx = x if w==0 else random.randint(x, x+w)
|
||||
ty = y if h==0 else random.randint(y, y+h)
|
||||
#print((tx, ty))
|
||||
return (tx, ty)
|
||||
|
||||
def adjacent_rooms(r, rooms):
|
||||
x, y, w, h = r.data
|
||||
adjacents = {}
|
||||
|
||||
for i, other_r in enumerate(rooms):
|
||||
rx, ry, rw, rh = other_r.data
|
||||
if (rx, ry, rw, rh) == r:
|
||||
continue # Skip self
|
||||
|
||||
# Check vertical adjacency (above or below)
|
||||
if rx < x + w and x < rx + rw: # Overlapping width
|
||||
if ry + rh == y: # Above
|
||||
adjacents[i] = (x + w // 2, y - 1)
|
||||
elif y + h == ry: # Below
|
||||
adjacents[i] = (x + w // 2, y + h + 1)
|
||||
|
||||
# Check horizontal adjacency (left or right)
|
||||
if ry < y + h and y < ry + rh: # Overlapping height
|
||||
if rx + rw == x: # Left
|
||||
adjacents[i] = (x - 1, y + h // 2)
|
||||
elif x + w == rx: # Right
|
||||
adjacents[i] = (x + w + 1, y + h // 2)
|
||||
|
||||
return adjacents
|
||||
|
||||
class Level:
|
||||
def __init__(self, width, height):
|
||||
self.width = width
|
||||
self.height = height
|
||||
#self.graph = [(0, 0, width, height)]
|
||||
self.graph = RoomGraph( (0, 0, width, height) )
|
||||
self.grid = mcrfpy.Grid(width, height, t, (10, 10), (1014, 758))
|
||||
self.highlighted = -1 #debug view feature
|
||||
self.walled_rooms = [] # for tracking "hallway rooms" vs "walled rooms"
|
||||
|
||||
def fill(self, xywh, highlight = False):
|
||||
if highlight:
|
||||
ts = 0
|
||||
else:
|
||||
ts = room_area(*xywh) % 131
|
||||
X, Y, W, H = xywh
|
||||
for x in range(X, X+W):
|
||||
for y in range(Y, Y+H):
|
||||
self.grid.at((x, y)).tilesprite = ts
|
||||
|
||||
def highlight(self, delta):
|
||||
rooms = self.graph.walk()
|
||||
if self.highlighted < len(rooms):
|
||||
#print(f"reset {self.highlighted}")
|
||||
self.fill(rooms[self.highlighted].data) # reset
|
||||
self.highlighted += delta
|
||||
print(f"highlight {self.highlighted}")
|
||||
self.highlighted = self.highlighted % len(rooms)
|
||||
self.fill(rooms[self.highlighted].data, highlight = True)
|
||||
|
||||
def reset(self):
|
||||
self.graph = RoomGraph( (0, 0, self.width, self.height) )
|
||||
for x in range(self.width):
|
||||
for y in range(self.height):
|
||||
self.grid.at((x, y)).walkable = True
|
||||
self.grid.at((x, y)).transparent = True
|
||||
self.grid.at((x, y)).tilesprite = 0 #random.choice([40, 28])
|
||||
|
||||
def split(self, single=False):
|
||||
if single:
|
||||
areas = {g.data: room_area(*g.data) for g in self.graph.walk()}
|
||||
largest = sorted(self.graph.walk(), key=lambda g: areas[g.data])[-1]
|
||||
largest.split()
|
||||
else:
|
||||
for room in self.graph.walk(): room.split()
|
||||
self.fill_rooms()
|
||||
|
||||
def fill_rooms(self, features=None):
|
||||
rooms = self.graph.walk()
|
||||
#print(f"rooms: {len(rooms)}")
|
||||
for i, g in enumerate(rooms):
|
||||
X, Y, W, H = g.data
|
||||
#c = [random.randint(0, 255) for _ in range(3)]
|
||||
ts = room_area(*g.data) % 131 + i # modulo - consistent tile pick
|
||||
for x in range(X, X+W):
|
||||
for y in range(Y, Y+H):
|
||||
self.grid.at((x, y)).tilesprite = ts
|
||||
|
||||
def wall_rooms(self):
|
||||
self.walled_rooms = []
|
||||
rooms = self.graph.walk()
|
||||
for i, g in enumerate(rooms):
|
||||
# unwalled / hallways: not selected for small dungeons, first, last, and 65% of all other rooms
|
||||
if len(rooms) > 3 and i > 1 and i < len(rooms) - 2 and random.random() < 0.35:
|
||||
self.walled_rooms.append(False)
|
||||
continue
|
||||
self.walled_rooms.append(True)
|
||||
X, Y, W, H = g.data
|
||||
for x in range(X, X+W):
|
||||
self.grid.at((x, Y)).walkable = False
|
||||
#self.grid.at((x, Y+H-1)).walkable = False
|
||||
for y in range(Y, Y+H):
|
||||
self.grid.at((X, y)).walkable = False
|
||||
#self.grid.at((X+W-1, y)).walkable = False
|
||||
# boundary of entire level
|
||||
for x in range(0, self.width):
|
||||
# self.grid.at((x, 0)).walkable = False
|
||||
self.grid.at((x, self.height-1)).walkable = False
|
||||
for y in range(0, self.height):
|
||||
# self.grid.at((0, y)).walkable = False
|
||||
self.grid.at((self.width-1, y)).walkable = False
|
||||
|
||||
def dig_path(self, start:"Tuple[int, int]", end:"Tuple[int, int]", walkable=True, color=None, sprite=None):
|
||||
print(f"Digging: {start} -> {end}")
|
||||
# get x1,y1 and x2,y2 coordinates: top left and bottom right points on the rect formed by two random points, one from each of the 2 rooms
|
||||
x1 = min([start[0], end[0]])
|
||||
x2 = max([start[0], end[0]])
|
||||
dw = x2 - x1
|
||||
y1 = min([start[1], end[1]])
|
||||
y2 = max([start[1], end[1]])
|
||||
dh = y2 - y1
|
||||
|
||||
# random: top left or bottom right as the corner between the paths
|
||||
tx, ty = (x1, y1) if random.random() >= 0.5 else (x2, y2)
|
||||
|
||||
for x in range(x1, x1+dw):
|
||||
try:
|
||||
if walkable:
|
||||
self.grid.at((x, ty)).walkable = walkable
|
||||
if color:
|
||||
self.grid.at((x, ty)).color = color
|
||||
if sprite is not None:
|
||||
self.grid.at((x, ty)).tilesprite = sprite
|
||||
except:
|
||||
pass
|
||||
for y in range(y1, y1+dh):
|
||||
try:
|
||||
if walkable:
|
||||
self.grid.at((tx, y)).walkable = True
|
||||
if color:
|
||||
self.grid.at((tx, y)).color = color
|
||||
if sprite is not None:
|
||||
self.grid.at((tx, y)).tilesprite = sprite
|
||||
except:
|
||||
pass
|
||||
|
||||
def generate(self, level_plan): #target_rooms = 5, features=None):
|
||||
self.reset()
|
||||
target_rooms = len(level_plan)
|
||||
if type(level_plan) is set:
|
||||
level_plan = random.choice(list(level_plan))
|
||||
while len(self.graph.walk()) < target_rooms:
|
||||
self.split(single=len(self.graph.walk()) > target_rooms * .5)
|
||||
|
||||
# Player path planning
|
||||
#self.fill_rooms()
|
||||
self.wall_rooms()
|
||||
rooms = self.graph.walk()
|
||||
feature_coords = []
|
||||
prev_room = None
|
||||
print(level_plan)
|
||||
for room_num, room in enumerate(rooms):
|
||||
room_plan = level_plan[room_num]
|
||||
if type(room_plan) == str: room_plan = [room_plan] # single item plans became single-character plans...
|
||||
for f in room_plan:
|
||||
#feature_coords.append((f, room_coord(room, margin=4 if f in ("boulder",) else 1)))
|
||||
# boulders are breaking my brain. If I can't get boulders away from walls with margin, I'm just going to dig them out.
|
||||
if f == "boulder":
|
||||
x, y = room_coord(room, margin=0)
|
||||
if x < 2: x += 1
|
||||
if y < 2: y += 1
|
||||
if x > self.grid.grid_size[0] - 2: x -= 1
|
||||
if y > self.grid.grid_size[1] - 2: y -= 1
|
||||
for _x in (1, 0, -1):
|
||||
for _y in (1, 0, -1):
|
||||
self.grid.at((x + _x, y + _y)).walkable = True
|
||||
feature_coords.append((f, (x, y)))
|
||||
else:
|
||||
feature_coords.append((f, room_coord(room, margin=0)))
|
||||
print(feature_coords[-1])
|
||||
|
||||
## Hallway generation
|
||||
# plow an inelegant path
|
||||
if prev_room:
|
||||
start = room_coord(prev_room, margin=2)
|
||||
end = room_coord(room, margin=2)
|
||||
self.dig_path(start, end, color=(0, 64, 0))
|
||||
prev_room = room
|
||||
|
||||
# Tile painting
|
||||
possibilities = None
|
||||
while possibilities or possibilities is None:
|
||||
possibilities = ct.wfc_pass(self.grid, possibilities)
|
||||
|
||||
## "hallway room" repainting
|
||||
#for i, hall_room in enumerate(rooms):
|
||||
# if self.walled_rooms[i]:
|
||||
# print(f"walled room: {hall_room}")
|
||||
# continue
|
||||
# print(f"hall room: {hall_room}")
|
||||
# x, y, w, h = hall_room.data
|
||||
# for _x in range(x+1, x+w-1):
|
||||
# for _y in range(y+1, y+h-1):
|
||||
# self.grid.at((_x, _y)).walkable = False
|
||||
# self.grid.at((_x, _y)).tilesprite = -1
|
||||
# self.grid.at((_x, _y)).color = (0, 0, 0) # pit!
|
||||
# targets = adjacent_rooms(hall_room, rooms)
|
||||
# print(targets)
|
||||
# for k, v in targets.items():
|
||||
# self.dig_path(hall_room.center(), v, color=(64, 32, 32))
|
||||
# for _, p in feature_coords:
|
||||
# if hall_room.contains(p): self.dig_path(hall_room.center(), p, color=(92, 48, 48))
|
||||
|
||||
return feature_coords
|
|
@ -1,223 +0,0 @@
|
|||
tiles = {}
|
||||
deltas = [
|
||||
(-1, -1), ( 0, -1), (+1, -1),
|
||||
(-1, 0), ( 0, 0), (+1, 0),
|
||||
(-1, +1), ( 0, +1), (+1, +1)
|
||||
]
|
||||
|
||||
class TileInfo:
|
||||
GROUND, WALL, DONTCARE = True, False, None
|
||||
chars = {
|
||||
"X": WALL,
|
||||
"_": GROUND,
|
||||
"?": DONTCARE
|
||||
}
|
||||
symbols = {v: k for k, v in chars.items()}
|
||||
|
||||
def __init__(self, values:dict):
|
||||
self._values = values
|
||||
self.rules = []
|
||||
self.chance = 1.0
|
||||
|
||||
@staticmethod
|
||||
def from_grid(grid, xy:tuple):
|
||||
values = {}
|
||||
for d in deltas:
|
||||
tx, ty = d[0] + xy[0], d[1] + xy[1]
|
||||
try:
|
||||
values[d] = grid.at((tx, ty)).walkable
|
||||
except ValueError:
|
||||
values[d] = True
|
||||
return TileInfo(values)
|
||||
|
||||
@staticmethod
|
||||
def from_string(s):
|
||||
values = {}
|
||||
for d, c in zip(deltas, s):
|
||||
values[d] = TileInfo.chars[c]
|
||||
return TileInfo(values)
|
||||
|
||||
def __hash__(self):
|
||||
"""for use as a dictionary key"""
|
||||
return hash(tuple(self._values.items()))
|
||||
|
||||
def match(self, other:"TileInfo"):
|
||||
for d, rule in self._values.items():
|
||||
if rule is TileInfo.DONTCARE: continue
|
||||
if other._values[d] is TileInfo.DONTCARE: continue
|
||||
if rule != other._values[d]: return False
|
||||
return True
|
||||
|
||||
def show(self):
|
||||
nine = ['', '', '\n'] * 3
|
||||
for k, end in zip(deltas, nine):
|
||||
c = TileInfo.symbols[self._values[k]]
|
||||
print(c, end=end)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<TileInfo {self._values}>"
|
||||
|
||||
cardinal_directions = {
|
||||
"N": ( 0, -1),
|
||||
"S": ( 0, +1),
|
||||
"E": (-1, 0),
|
||||
"W": (+1, 0)
|
||||
}
|
||||
|
||||
def special_rule_verify(rule, grid, xy, unverified_tiles, pass_unverified=False):
|
||||
cardinal, allowed_tile = rule
|
||||
dxy = cardinal_directions[cardinal.upper()]
|
||||
tx, ty = xy[0] + dxy[0], xy[1] + dxy[1]
|
||||
#print(f"Special rule: {cardinal} {allowed_tile} {type(allowed_tile)} -> ({tx}, {ty}) [{grid.at((tx, ty)).tilesprite}]{'*' if (tx, ty) in unverified_tiles else ''}")
|
||||
if (tx, ty) in unverified_tiles and cardinal in "nsew": return pass_unverified
|
||||
try:
|
||||
return grid.at((tx, ty)).tilesprite == allowed_tile
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
import random
|
||||
tile_of_last_resort = 431
|
||||
|
||||
def find_possible_tiles(grid, x, y, unverified_tiles=None, pass_unverified=False):
|
||||
ti = TileInfo.from_grid(grid, (x, y))
|
||||
if unverified_tiles is None: unverified_tiles = []
|
||||
matches = [(k, v) for k, v in tiles.items() if k.match(ti)]
|
||||
if not matches:
|
||||
return []
|
||||
possible = []
|
||||
if not any([tileinfo.rules for tileinfo, _ in matches]):
|
||||
# make weighted choice, as the tile does not depend on verification
|
||||
wts = [k.chance for k, v in matches]
|
||||
tileinfo, tile = random.choices(matches, weights=wts)[0]
|
||||
return [tile]
|
||||
|
||||
for tileinfo, tile in matches:
|
||||
if not tileinfo.rules:
|
||||
possible.append(tile)
|
||||
continue
|
||||
for r in tileinfo.rules: #for r in ...: if ... continue == more readable than an "any" 1-liner
|
||||
p = special_rule_verify(r, grid, (x,y),
|
||||
unverified_tiles=unverified_tiles,
|
||||
pass_unverified = pass_unverified
|
||||
)
|
||||
if p:
|
||||
possible.append(tile)
|
||||
continue
|
||||
return list(set(list(possible)))
|
||||
|
||||
def wfc_first_pass(grid):
|
||||
w, h = grid.grid_size
|
||||
possibilities = {}
|
||||
for x in range(0, w):
|
||||
for y in range(0, h):
|
||||
matches = find_possible_tiles(grid, x, y, pass_unverified=True)
|
||||
if len(matches) == 0:
|
||||
grid.at((x, y)).tilesprite = tile_of_last_resort
|
||||
possibilities[(x,y)] = matches
|
||||
elif len(matches) == 1:
|
||||
grid.at((x, y)).tilesprite = matches[0]
|
||||
else:
|
||||
possibilities[(x,y)] = matches
|
||||
return possibilities
|
||||
|
||||
def wfc_pass(grid, possibilities=None):
|
||||
w, h = grid.grid_size
|
||||
if possibilities is None:
|
||||
#print("first pass results:")
|
||||
possibilities = wfc_first_pass(grid)
|
||||
counts = {}
|
||||
for v in possibilities.values():
|
||||
if len(v) in counts: counts[len(v)] += 1
|
||||
else: counts[len(v)] = 1
|
||||
#print(counts)
|
||||
return possibilities
|
||||
elif len(possibilities) == 0:
|
||||
print("We're done!")
|
||||
return
|
||||
old_possibilities = possibilities
|
||||
possibilities = {}
|
||||
for (x, y) in old_possibilities.keys():
|
||||
matches = find_possible_tiles(grid, x, y, unverified_tiles=old_possibilities.keys(), pass_unverified = True)
|
||||
if len(matches) == 0:
|
||||
print((x,y), matches)
|
||||
grid.at((x, y)).tilesprite = tile_of_last_resort
|
||||
possibilities[(x,y)] = matches
|
||||
elif len(matches) == 1:
|
||||
grid.at((x, y)).tilesprite = matches[0]
|
||||
else:
|
||||
grid.at((x, y)).tilesprite = -1
|
||||
grid.at((x, y)).color = (32 * len(matches), 32 * len(matches), 32 * len(matches))
|
||||
possibilities[(x,y)] = matches
|
||||
|
||||
if len(possibilities) == len(old_possibilities):
|
||||
#print("No more tiles could be solved without collapse")
|
||||
counts = {}
|
||||
for v in possibilities.values():
|
||||
if len(v) in counts: counts[len(v)] += 1
|
||||
else: counts[len(v)] = 1
|
||||
#print(counts)
|
||||
if 0 in counts: del counts[0]
|
||||
if len(counts) == 0:
|
||||
print("Contrats! You broke it! (insufficient tile defs to solve remaining tiles)")
|
||||
return []
|
||||
target = min(list(counts.keys()))
|
||||
while possibilities:
|
||||
for (x, y) in possibilities.keys():
|
||||
if len(possibilities[(x, y)]) != target:
|
||||
continue
|
||||
ti = TileInfo.from_grid(grid, (x, y))
|
||||
matches = [(k, v) for k, v in tiles.items() if k.match(ti)]
|
||||
verifiable_matches = find_possible_tiles(grid, x, y, unverified_tiles=possibilities.keys())
|
||||
if not verifiable_matches: continue
|
||||
#print(f"collapsing {(x, y)} ({target} choices)")
|
||||
matches = [(k, v) for k, v in matches if v in verifiable_matches]
|
||||
wts = [k.chance for k, v in matches]
|
||||
tileinfo, tile = random.choices(matches, weights=wts)[0]
|
||||
grid.at((x, y)).tilesprite = tile
|
||||
del possibilities[(x, y)]
|
||||
break
|
||||
else:
|
||||
selected = random.choice(list(possibilities.keys()))
|
||||
#print(f"No tiles have verifable solutions: QUANTUM -> {selected}")
|
||||
# sprinkle some quantumness on it
|
||||
ti = TileInfo.from_grid(grid, (x, y))
|
||||
matches = [(k, v) for k, v in tiles.items() if k.match(ti)]
|
||||
wts = [k.chance for k, v in matches]
|
||||
if not wts:
|
||||
print(f"This one: {(x,y)} {matches}\n{wts}")
|
||||
del possibilities[(x, y)]
|
||||
return possibilities
|
||||
tileinfo, tile = random.choices(matches, weights=wts)[0]
|
||||
grid.at((x, y)).tilesprite = tile
|
||||
del possibilities[(x, y)]
|
||||
|
||||
return possibilities
|
||||
|
||||
#with open("scripts/tile_def.txt", "r") as f:
|
||||
with open("scripts/simple_tiles.txt", "r") as f:
|
||||
for block in f.read().split('\n\n'):
|
||||
info, constraints = block.split('\n', 1)
|
||||
if '#' in info:
|
||||
info, comment = info.split('#', 1)
|
||||
rules = []
|
||||
if '@' in info:
|
||||
info, *block_rules = info.split('@')
|
||||
#print(block_rules)
|
||||
for r in block_rules:
|
||||
rules.append((r[0], int(r[1:])))
|
||||
#cardinal_dir = block_rules[0]
|
||||
#partner
|
||||
if ':' not in info:
|
||||
tile_id = int(info)
|
||||
chance = 1.0
|
||||
else:
|
||||
tile_id, chance = info.split(':')
|
||||
tile_id = int(tile_id)
|
||||
chance = float(chance.strip())
|
||||
constraints = constraints.replace('\n', '')
|
||||
k = TileInfo.from_string(constraints)
|
||||
k.rules = rules
|
||||
k.chance = chance
|
||||
tiles[k] = tile_id
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
print("[Python] Attempting import")
|
||||
import scriptable
|
||||
|
||||
print(f"[Python] calling fibonacci(8): {scriptable.fibonacci(8)}")
|
||||
print(f"[Python] calling fibonacci(15): {scriptable.fibonacci(15)}")
|
||||
|
||||
import venv
|
||||
print("[Python] Importing library installed with pip")
|
||||
import numpy
|
||||
|
|
@ -1,606 +0,0 @@
|
|||
import mcrfpy
|
||||
#t = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16) # 12, 11)
|
||||
t = mcrfpy.Texture("assets/kenney_TD_MR_IP.png", 16, 16) # 12, 11)
|
||||
btn_tex = mcrfpy.Texture("assets/48px_ui_icons-KenneyNL.png", 48, 48)
|
||||
font = mcrfpy.Font("assets/JetbrainsMono.ttf")
|
||||
|
||||
frame_color = (64, 64, 128)
|
||||
|
||||
import random
|
||||
import cos_entities as ce
|
||||
import cos_level as cl
|
||||
#import cos_tiles as ct
|
||||
|
||||
class Resources:
|
||||
def __init__(self):
|
||||
self.music_enabled = True
|
||||
self.music_volume = 40
|
||||
self.sfx_enabled = True
|
||||
self.sfx_volume = 100
|
||||
self.master_volume = 100
|
||||
|
||||
# load the music/sfx files here
|
||||
self.splats = []
|
||||
for i in range(1, 10):
|
||||
mcrfpy.createSoundBuffer(f"assets/sfx/splat{i}.ogg")
|
||||
|
||||
|
||||
def play_sfx(self, sfx_id):
|
||||
if self.sfx_enabled and self.sfx_volume and self.master_volume:
|
||||
mcrfpy.setSoundVolume(self.master_volume/100 * self.sfx_volume)
|
||||
mcrfpy.playSound(sfx_id)
|
||||
|
||||
def play_music(self, track_id):
|
||||
if self.music_enabled and self.music_volume and self.master_volume:
|
||||
mcrfpy.setMusicVolume(self.master_volume/100 * self.music_volume)
|
||||
mcrfpy.playMusic(...)
|
||||
|
||||
resources = Resources()
|
||||
|
||||
class Crypt:
|
||||
def __init__(self):
|
||||
mcrfpy.createScene("play")
|
||||
self.ui = mcrfpy.sceneUI("play")
|
||||
|
||||
entity_frame = mcrfpy.Frame(815, 10, 194, 595, fill_color = frame_color)
|
||||
inventory_frame = mcrfpy.Frame(10, 610, 800, 143, fill_color = frame_color)
|
||||
stats_frame = mcrfpy.Frame(815, 610, 194, 143, fill_color = frame_color)
|
||||
|
||||
#self.level = cl.Level(30, 23)
|
||||
self.entities = []
|
||||
self.depth=1
|
||||
self.stuck_btn = SweetButton(self.ui, (810, 700), "Stuck", icon=19, box_width=150, box_height = 60, click=self.stuck)
|
||||
|
||||
self.level_plan = {
|
||||
1: [("spawn", "button", "boulder"), ("exit")],
|
||||
2: [("spawn", "button", "boulder"), ("rat"), ("exit")],
|
||||
3: [("spawn", "button", "boulder"), ("rat"), ("exit")],
|
||||
4: [("spawn", "button", "rat"), ("boulder", "rat", "treasure"), ("exit")],
|
||||
5: [("spawn", "button", "rat"), ("boulder", "rat"), ("exit")],
|
||||
6: {(("spawn", "button"), ("boulder", "treasure", "exit")),
|
||||
(("spawn", "boulder"), ("button", "treasure", "exit"))},
|
||||
7: {(("spawn", "button"), ("boulder", "treasure", "exit")),
|
||||
(("spawn", "boulder"), ("button", "treasure", "exit"))},
|
||||
8: {(("spawn", "treasure", "button"), ("boulder", "treasure", "exit")),
|
||||
(("spawn", "treasure", "boulder"), ("button", "treasure", "exit"))}
|
||||
#9: self.lv_planner
|
||||
}
|
||||
|
||||
# empty void for the player to initialize into
|
||||
self.headsup = mcrfpy.Frame(10, 684, 320, 64, fill_color = (0, 0, 0, 0))
|
||||
self.sidebar = mcrfpy.Frame(860, 4, 160, 600, fill_color = (96, 96, 160))
|
||||
|
||||
# Heads Up (health bar, armor bar) config
|
||||
self.health_bar = [mcrfpy.Sprite(32*i, 2, t, 659, 2) for i in range(10)]
|
||||
[self.headsup.children.append(i) for i in self.health_bar]
|
||||
self.armor_bar = [mcrfpy.Sprite(32*i, 42, t, 659, 2) for i in range(10)]
|
||||
[self.headsup.children.append(i) for i in self.armor_bar]
|
||||
# (40, 3), caption, font, fill_color=font_color
|
||||
self.stat_captions = mcrfpy.Caption((325,0), "HP:10\nDef:0(+0)", font, fill_color=(255, 255, 255))
|
||||
self.stat_captions.outline = 3
|
||||
self.stat_captions.outline_color = (0, 0, 0)
|
||||
self.headsup.children.append(self.stat_captions)
|
||||
|
||||
# Side Bar (inventory, level info) config
|
||||
self.level_caption = mcrfpy.Caption((5,5), "Level: 1", font, fill_color=(255, 255, 255))
|
||||
self.level_caption.size = 26
|
||||
self.level_caption.outline = 3
|
||||
self.level_caption.outline_color = (0, 0, 0)
|
||||
self.sidebar.children.append(self.level_caption)
|
||||
self.inv_sprites = [mcrfpy.Sprite(15, 70 + 95*i, t, 659, 6.0) for i in range(5)]
|
||||
for i in self.inv_sprites:
|
||||
self.sidebar.children.append(i)
|
||||
self.key_captions = [
|
||||
mcrfpy.Sprite(75, 130 + (95*2) + 95 * i, t, 384 + i, 3.0) for i in range(3)
|
||||
]
|
||||
for i in self.key_captions:
|
||||
self.sidebar.children.append(i)
|
||||
self.inv_captions = [
|
||||
mcrfpy.Caption((25, 130 + 95 * i), "x", font, fill_color=(255, 255, 255)) for i in range(5)
|
||||
]
|
||||
for i in self.inv_captions:
|
||||
self.sidebar.children.append(i)
|
||||
|
||||
liminal_void = mcrfpy.Grid(1, 1, t, (0, 0), (16, 16))
|
||||
self.grid = liminal_void
|
||||
self.player = ce.PlayerEntity(game=self)
|
||||
self.spawn_point = (0, 0)
|
||||
|
||||
# level creation moves player to the game level at the generated spawn point
|
||||
self.create_level(self.depth)
|
||||
#self.grid = mcrfpy.Grid(20, 15, t, (10, 10), (1014, 758))
|
||||
self.swap_level(self.level, self.spawn_point)
|
||||
|
||||
# Test Entities
|
||||
#ce.BoulderEntity(9, 7, game=self)
|
||||
#ce.BoulderEntity(9, 8, game=self)
|
||||
#ce.ExitEntity(12, 6, 14, 4, game=self)
|
||||
# scene setup
|
||||
|
||||
## might be done by self.swap_level
|
||||
#[self.ui.append(e) for e in (self.grid, self.stuck_btn.base_frame)] # entity_frame, inventory_frame, stats_frame)]
|
||||
|
||||
self.possibilities = None # track WFC possibilities between rounds
|
||||
self.enemies = []
|
||||
#mcrfpy.setTimer("enemy_test", self.enemy_movement, 750)
|
||||
|
||||
#mcrfpy.Frame(x, y, box_width+shadow_offset, box_height, fill_color = (0, 0, 0, 255))
|
||||
#Sprite(0, 3, btn_tex, icon, icon_scale)
|
||||
|
||||
#def enemy_movement(self, *args):
|
||||
# for e in self.enemies: e.act()
|
||||
|
||||
#def spawn_test_rat(self):
|
||||
# success = False
|
||||
# while not success:
|
||||
# x, y = [random.randint(0, i-1) for i in self.grid.grid_size]
|
||||
# success = self.grid.at((x,y)).walkable
|
||||
# self.enemies.append(ce.EnemyEntity(x, y, game=self))
|
||||
|
||||
def gui_update(self):
|
||||
self.stat_captions.text = f"HP:{self.player.hp}\nDef:{self.player.calc_defense()}(+{self.player.calc_defense() - self.player.base_defense})"
|
||||
for i, hs in enumerate(self.health_bar):
|
||||
full_hearts = self.player.hp - (i*2)
|
||||
empty_hearts = self.player.max_hp - (i*2)
|
||||
hs.sprite_number = 659
|
||||
if empty_hearts >= 2:
|
||||
hs.sprite_number = 208
|
||||
if full_hearts >= 2:
|
||||
hs.sprite_number = 210
|
||||
elif full_hearts == 1:
|
||||
hs.sprite_number = 209
|
||||
for i, arm_s in enumerate(self.armor_bar):
|
||||
full_hearts = self.player.calc_defense() - (i*2)
|
||||
arm_s.sprite_number = 659
|
||||
if full_hearts >= 2:
|
||||
arm_s.sprite_number = 211
|
||||
elif full_hearts == 1:
|
||||
arm_s.sprite_number = 212
|
||||
|
||||
#items = self.player.equipped[:] + self.player.inventory[:]
|
||||
for i in range(5):
|
||||
if i == 0:
|
||||
item = self.player.equipped[0] if len(self.player.equipped) > 0 else None
|
||||
elif i == 1:
|
||||
item = self.player.equipped[1] if len(self.player.equipped) > 1 else None
|
||||
elif i == 2:
|
||||
item = self.player.inventory[0] if len(self.player.inventory) > 0 else None
|
||||
elif i == 3:
|
||||
item = self.player.inventory[1] if len(self.player.inventory) > 1 else None
|
||||
elif i == 4:
|
||||
item = self.player.inventory[2] if len(self.player.inventory) > 2 else None
|
||||
if item is None:
|
||||
self.inv_sprites[i].sprite_number = 659
|
||||
if i > 1: self.key_captions[i - 2].sprite_number = 659
|
||||
self.inv_captions[i].text = ""
|
||||
continue
|
||||
self.inv_sprites[i].sprite_number = item.sprite
|
||||
if i > 1:
|
||||
self.key_captions[i - 2].sprite_number = 384 + (i - 2)
|
||||
self.inv_captions[i].text = "Blah"
|
||||
|
||||
def lv_planner(self, target_level):
|
||||
"""Plan room sequence in levels > 9"""
|
||||
monsters = (target_level - 6) // 2
|
||||
target_rooms = min(int(target_level // 2), 6)
|
||||
target_treasure = min(int(target_level // 3), 4)
|
||||
rooms = []
|
||||
for i in range(target_rooms):
|
||||
rooms.append([])
|
||||
for o in ("spawn", "boulder", "button", "exit"):
|
||||
r = random.randint(0, target_rooms-1)
|
||||
rooms[r].append(o)
|
||||
monster_table = {
|
||||
"rat": int(monsters * 0.8) + 2,
|
||||
"big rat": max(int(monsters * 0.2) - 2, 0),
|
||||
"cyclops": max(int(monsters * 0.1) - 3, 0)
|
||||
}
|
||||
monster_table = {k: v for k, v in monster_table.items() if v > 0}
|
||||
monster_names = list(monster_table.keys())
|
||||
monster_weights = [monster_table[k] for k in monster_names]
|
||||
for m in range(monsters):
|
||||
r = random.randint(0, target_rooms - 1)
|
||||
rooms[r].append(random.choices(monster_names, weights = monster_weights)[0])
|
||||
|
||||
for t in range(target_treasure):
|
||||
r = random.randint(0, target_rooms - 1)
|
||||
rooms[r].append("treasure")
|
||||
|
||||
return rooms
|
||||
|
||||
def treasure_planner(self, treasure_level):
|
||||
"""Plan treasure contents at all levels"""
|
||||
item_minlv = {
|
||||
"buckler": 1,
|
||||
"shield": 2,
|
||||
"sword": 1,
|
||||
"sword2": 2,
|
||||
"sword3": 5,
|
||||
"axe": 1,
|
||||
"axe2": 2,
|
||||
"axe3": 5,
|
||||
"wand": 1,
|
||||
"staff": 2,
|
||||
"staff2": 5,
|
||||
"red_pot": 3,
|
||||
"blue_pot": 6,
|
||||
"green_pot": 6,
|
||||
"grey_pot": 6,
|
||||
"sm_grey_pot": 1
|
||||
}
|
||||
base_wts = {
|
||||
("buckler", "shield"): 0.25, # defensive items
|
||||
("sword", "sword2", "axe", "axe2"): 0.4, #1h weapons
|
||||
("sword3", "axe3"): 0.25, #2h weapons
|
||||
("wand", "staff", "staff2"): 0.15, #magic weapons
|
||||
("red_pot",): 0.25, #health
|
||||
("blue_pot", "green_pot", "grey_pot", "sm_grey_pot"): 0.2 #stat upgrade potions
|
||||
}
|
||||
|
||||
# find item name in base_wts key (base weight of the category)
|
||||
base_weight = lambda s: base_wts[list([t for t in base_wts.keys() if s in t])[0]]
|
||||
weights = {d[0]: base_weight(d[0]) for d in item_minlv.items() if treasure_level > d[1]}
|
||||
if self.player.archetype is None:
|
||||
prefs = []
|
||||
elif self.player.archetype == "viking":
|
||||
prefs = ["axe2", "axe3", "green_pot"]
|
||||
elif self.player.archetype == "knight":
|
||||
prefs = ["sword2", "shield", "grey_pot"]
|
||||
elif self.player.archetype == "wizard":
|
||||
prefs = ["staff", "staff2", "blue_pot"]
|
||||
for i in prefs:
|
||||
if i in weights: weights[i] *= 3
|
||||
|
||||
return weights
|
||||
|
||||
def start(self):
|
||||
resources.play_sfx(1)
|
||||
mcrfpy.setScene("play")
|
||||
mcrfpy.keypressScene(self.cos_keys)
|
||||
|
||||
def add_entity(self, e:ce.COSEntity):
|
||||
self.entities.append(e)
|
||||
self.entities.sort(key = lambda e: e.draw_order, reverse=False)
|
||||
# hack / workaround for grid.entities not interable
|
||||
while len(self.grid.entities): # while there are entities on the grid,
|
||||
self.grid.entities.remove(0) # remove the 1st ("0th")
|
||||
for e in self.entities:
|
||||
self.grid.entities.append(e._entity)
|
||||
|
||||
def create_level(self, depth, _luck = 0):
|
||||
#if depth < 3:
|
||||
# features = None
|
||||
self.level = cl.Level(20, 20)
|
||||
self.grid = self.level.grid
|
||||
if depth in self.level_plan:
|
||||
plan = self.level_plan[depth]
|
||||
else:
|
||||
plan = self.lv_planner(depth)
|
||||
coords = self.level.generate(plan)
|
||||
self.entities = []
|
||||
if self.player:
|
||||
luck = self.player.luck
|
||||
else:
|
||||
luck = 0
|
||||
buttons = []
|
||||
for k, v in sorted(coords, key=lambda i: i[0]): # "button" before "exit"; "button", "button", "door", "exit" -> alphabetical is correct sequence
|
||||
if k == "spawn":
|
||||
if self.player:
|
||||
self.add_entity(self.player)
|
||||
#self.player.draw_pos = v
|
||||
self.spawn_point = v
|
||||
elif k == "boulder":
|
||||
ce.BoulderEntity(v[0], v[1], game=self)
|
||||
elif k == "treasure":
|
||||
ce.TreasureEntity(v[0], v[1], treasure_table = self.treasure_planner(depth + luck), game=self)
|
||||
elif k == "button":
|
||||
buttons.append(v)
|
||||
elif k == "exit":
|
||||
btn = buttons.pop(0)
|
||||
ce.ExitEntity(v[0], v[1], btn[0], btn[1], game=self)
|
||||
elif k == "rat":
|
||||
ce.EnemyEntity(*v, game=self)
|
||||
elif k == "big rat":
|
||||
ce.EnemyEntity(*v, game=self, base_damage=2, hp=4, sprite=130)
|
||||
elif k == "cyclops":
|
||||
ce.EnemyEntity(*v, game=self, base_damage=3, hp=8, sprite=109, base_defense=2)
|
||||
|
||||
#if self.depth > 2:
|
||||
#for i in range(10):
|
||||
# self.spawn_test_rat()
|
||||
|
||||
def stuck(self, sweet_btn, args):
|
||||
if args[3] == "end": return
|
||||
self.create_level(self.depth)
|
||||
self.swap_level(self.level, self.spawn_point)
|
||||
|
||||
def cos_keys(self, key, state):
|
||||
d = None
|
||||
if state == "end": return
|
||||
elif key == "W": d = (0, -1)
|
||||
elif key == "A": d = (-1, 0)
|
||||
elif key == "S": d = (0, 1)
|
||||
elif key == "D": d = (1, 0)
|
||||
#elif key == "M": self.level.generate()
|
||||
#elif key == "R":
|
||||
# self.level.reset()
|
||||
# self.possibilities = None
|
||||
#elif key == "T":
|
||||
# self.level.split()
|
||||
# self.possibilities = None
|
||||
#elif key == "Y": self.level.split(single=True)
|
||||
#elif key == "U": self.level.highlight(+1)
|
||||
#elif key == "I": self.level.highlight(-1)
|
||||
#elif key == "O":
|
||||
# self.level.wall_rooms()
|
||||
# self.possibilities = None
|
||||
#elif key == "P": ct.format_tiles(self.grid)
|
||||
#elif key == "P":
|
||||
#self.possibilities = ct.wfc_pass(self.grid, self.possibilities)
|
||||
elif key == "P":
|
||||
self.depth += 1
|
||||
print(f"Descending: lv {self.depth}")
|
||||
self.stuck(None, [1,2,3,4])
|
||||
elif key == "Period":
|
||||
self.enemy_turn()
|
||||
elif key == "X":
|
||||
self.pull_boulder_search()
|
||||
#else:
|
||||
# print(key)
|
||||
if d:
|
||||
self.entities.sort(key = lambda e: e.draw_order, reverse=False)
|
||||
self.player.try_move(*d)
|
||||
self.enemy_turn()
|
||||
|
||||
def enemy_turn(self):
|
||||
self.entities.sort(key = lambda e: e.draw_order, reverse=False)
|
||||
for e in self.entities:
|
||||
e.act()
|
||||
self.gui_update()
|
||||
|
||||
def pull_boulder_search(self):
|
||||
for dx, dy in ( (0, -1), (-1, 0), (1, 0), (0, 1) ):
|
||||
for e in self.entities:
|
||||
if e.draw_pos != (self.player.draw_pos[0] + dx, self.player.draw_pos[1] + dy): continue
|
||||
if type(e) == ce.BoulderEntity:
|
||||
self.pull_boulder_move((dx, dy), e)
|
||||
return self.enemy_turn()
|
||||
else:
|
||||
print("No boulder found to pull.")
|
||||
|
||||
def pull_boulder_move(self, p, target_boulder):
|
||||
print(p, target_boulder)
|
||||
self.entities.sort(key = lambda e: e.draw_order, reverse=False)
|
||||
if self.player.try_move(-p[0], -p[1], test=True):
|
||||
old_pos = self.player.draw_pos
|
||||
self.player.try_move(-p[0], -p[1])
|
||||
target_boulder.do_move(*old_pos)
|
||||
|
||||
def swap_level(self, new_level, spawn_point):
|
||||
self.level = new_level
|
||||
self.grid = self.level.grid
|
||||
self.grid.zoom = 2.0
|
||||
# TODO, make an entity mover function
|
||||
#self.add_entity(self.player)
|
||||
self.player.grid = self.grid
|
||||
self.player.draw_pos = spawn_point
|
||||
#self.grid.entities.append(self.player._entity)
|
||||
|
||||
# reform UI (workaround to ui collection iterators crashing)
|
||||
while len(self.ui) > 0:
|
||||
try:
|
||||
self.ui.remove(0)
|
||||
except:
|
||||
pass
|
||||
self.ui.append(self.grid)
|
||||
self.ui.append(self.stuck_btn.base_frame)
|
||||
self.ui.append(self.headsup)
|
||||
|
||||
self.level_caption.text = f"Level: {self.depth}"
|
||||
self.ui.append(self.sidebar)
|
||||
self.gui_update()
|
||||
|
||||
class SweetButton:
|
||||
def __init__(self, ui:mcrfpy.UICollection,
|
||||
pos:"Tuple[int, int]",
|
||||
caption:str, font=font, font_size=24, font_color=(255,255,255), font_outline_color=(0, 0, 0), font_outline_width=2,
|
||||
shadow_offset = 8, box_width=200, box_height = 80, shadow_color=(64, 64, 86), box_color=(96, 96, 160),
|
||||
icon=4, icon_scale=1.75, click=lambda *args: None):
|
||||
self.ui = ui
|
||||
self.shadow_box = mcrfpy.Frame
|
||||
x, y = pos
|
||||
|
||||
# box w/ drop shadow
|
||||
self.shadow_offset = shadow_offset
|
||||
self.base_frame = mcrfpy.Frame(x, y, box_width+shadow_offset, box_height, fill_color = (0, 0, 0, 255))
|
||||
self.base_frame.click = self.do_click
|
||||
|
||||
# drop shadow won't need configured, append directly
|
||||
self.base_frame.children.append(mcrfpy.Frame(0, 0, box_width, box_height, fill_color = shadow_color))
|
||||
|
||||
# main button is where the content lives
|
||||
self.main_button = mcrfpy.Frame(shadow_offset, shadow_offset, box_width, box_height, fill_color = box_color)
|
||||
self.click = click
|
||||
self.base_frame.children.append(self.main_button)
|
||||
|
||||
# main button icon
|
||||
self.icon = mcrfpy.Sprite(0, 3, btn_tex, icon, icon_scale)
|
||||
self.main_button.children.append(self.icon)
|
||||
|
||||
# main button caption
|
||||
self.caption = mcrfpy.Caption((40, 3), caption, font, fill_color=font_color)
|
||||
self.caption.size = font_size
|
||||
self.caption.outline_color=font_outline_color
|
||||
self.caption.outline=font_outline_width
|
||||
self.main_button.children.append(self.caption)
|
||||
|
||||
def unpress(self):
|
||||
"""Helper func for when graphics changes or glitches make the button stuck down"""
|
||||
self.main_button.x, self.main_button.y = (self.shadow_offset, self.shadow_offset)
|
||||
|
||||
def do_click(self, x, y, mousebtn, event):
|
||||
if event == "start":
|
||||
self.main_button.x, self.main_button.y = (0, 0)
|
||||
elif event == "end":
|
||||
self.main_button.x, self.main_button.y = (self.shadow_offset, self.shadow_offset)
|
||||
result = self.click(self, (x, y, mousebtn, event))
|
||||
if result: # return True from event function to instantly un-pop
|
||||
self.main_button.x, self.main_button.y = (self.shadow_offset, self.shadow_offset)
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
return self.caption.text
|
||||
|
||||
@text.setter
|
||||
def text(self, value):
|
||||
self.caption.text = value
|
||||
|
||||
@property
|
||||
def sprite_number(self):
|
||||
return self.icon.sprite_number
|
||||
|
||||
@sprite_number.setter
|
||||
def sprite_number(self, value):
|
||||
self.icon.sprite_number = value
|
||||
|
||||
class MainMenu:
|
||||
def __init__(self):
|
||||
mcrfpy.createScene("menu")
|
||||
self.ui = mcrfpy.sceneUI("menu")
|
||||
mcrfpy.setScene("menu")
|
||||
self.crypt = None
|
||||
|
||||
components = []
|
||||
# demo grid
|
||||
#components.append(
|
||||
# )
|
||||
|
||||
# title text
|
||||
drop_shadow = mcrfpy.Caption((150, 10), "Crypt Of Sokoban", font, fill_color=(96, 96, 96), outline_color=(192, 0, 0))
|
||||
drop_shadow.outline = 3
|
||||
drop_shadow.size = 64
|
||||
components.append(
|
||||
drop_shadow
|
||||
)
|
||||
|
||||
title_txt = mcrfpy.Caption((158, 18), "Crypt Of Sokoban", font, fill_color=(255, 255, 255))
|
||||
title_txt.size = 64
|
||||
components.append(
|
||||
title_txt
|
||||
)
|
||||
|
||||
# toast: text over the demo grid that fades out on a timer
|
||||
self.toast = mcrfpy.Caption((150, 400), "", font, fill_color=(0, 0, 0))
|
||||
self.toast.size = 28
|
||||
self.toast.outline = 2
|
||||
self.toast.outline_color = (255, 255, 255)
|
||||
self.toast_event = None
|
||||
components.append(self.toast)
|
||||
|
||||
# button - PLAY
|
||||
#playbtn = mcrfpy.Frame(284, 548, 456, 120, fill_color =
|
||||
play_btn = SweetButton(self.ui, (284, 548), "PLAY", box_width=456, box_height=110, icon=1, icon_scale=2.0, click=self.play)
|
||||
components.append(play_btn.base_frame)
|
||||
|
||||
# button - config menu pane
|
||||
#self.config = lambda self, sweet_btn, *args: print(f"boop, sweet button {sweet_btn} config {args}")
|
||||
config_btn = SweetButton(self.ui, (10, 678), "Settings", icon=2, click=self.show_config)
|
||||
components.append(config_btn.base_frame)
|
||||
|
||||
# button - insta-1080p scaling
|
||||
scale_btn = SweetButton(self.ui, (10+256, 678), "Scale up\nto 1080p", icon=15, click=self.scale)
|
||||
self.scaled = False
|
||||
components.append(scale_btn.base_frame)
|
||||
|
||||
# button - music toggle
|
||||
music_btn = SweetButton(self.ui, (10+256*2, 678), "Music\nON", icon=12, click=self.music_toggle)
|
||||
self.music_enabled = True
|
||||
self.music_volume = 40
|
||||
components.append(music_btn.base_frame)
|
||||
|
||||
# button - sfx toggle
|
||||
sfx_btn = SweetButton(self.ui, (10+256*3, 678), "SFX\nON", icon=0, click=self.sfx_toggle)
|
||||
self.sfx_enabled = True
|
||||
self.sfx_volume = 40
|
||||
components.append(sfx_btn.base_frame)
|
||||
|
||||
[self.ui.append(e) for e in components]
|
||||
|
||||
def toast_say(self, txt, delay=10):
|
||||
"kick off a toast event"
|
||||
if self.toast_event is not None:
|
||||
mcrfpy.delTimer("toast_timer")
|
||||
self.toast.text = txt
|
||||
self.toast_event = 350
|
||||
self.toast.fill_color = (255, 255, 255, 255)
|
||||
self.toast.outline = 2
|
||||
self.toast.outline_color = (0, 0, 0, 255)
|
||||
mcrfpy.setTimer("toast_timer", self.toast_callback, 100)
|
||||
|
||||
def toast_callback(self, *args):
|
||||
"fade out the toast text"
|
||||
self.toast_event -= 5
|
||||
if self.toast_event < 0:
|
||||
self.toast_event = None
|
||||
mcrfpy.delTimer("toast_timer")
|
||||
mcrfpy.text = ""
|
||||
return
|
||||
a = min(self.toast_event, 255)
|
||||
self.toast.fill_color = (255, 255, 255, a)
|
||||
self.toast.outline_color = (0, 0, 0, a)
|
||||
|
||||
def show_config(self, sweet_btn, args):
|
||||
self.toast_say("Beep, Boop! Configurations will go here.")
|
||||
|
||||
def play(self, sweet_btn, args):
|
||||
#if args[3] == "start": return # DRAMATIC on release action!
|
||||
if args[3] == "end": return
|
||||
self.crypt = Crypt()
|
||||
#mcrfpy.setScene("play")
|
||||
self.crypt.start()
|
||||
|
||||
def scale(self, sweet_btn, args, window_scale=None):
|
||||
if args[3] == "end": return
|
||||
if not window_scale:
|
||||
self.scaled = not self.scaled
|
||||
window_scale = 1.3
|
||||
else:
|
||||
self.scaled = True
|
||||
sweet_btn.unpress()
|
||||
if self.scaled:
|
||||
self.toast_say("Windowed mode only, sorry!\nCheck Settings for for fine-tuned controls.")
|
||||
mcrfpy.setScale(window_scale)
|
||||
sweet_btn.text = "Scale down\n to 1.0x"
|
||||
else:
|
||||
mcrfpy.setScale(1.0)
|
||||
sweet_btn.text = "Scale up\nto 1080p"
|
||||
|
||||
def music_toggle(self, sweet_btn, args):
|
||||
if args[3] == "end": return
|
||||
self.music_enabled = not self.music_enabled
|
||||
print(f"music: {self.music_enabled}")
|
||||
if self.music_enabled:
|
||||
mcrfpy.setMusicVolume(self.music_volume)
|
||||
sweet_btn.text = "Music is ON"
|
||||
sweet_btn.sprite_number = 12
|
||||
else:
|
||||
self.toast_say("Use your volume keys or\nlook in Settings for a volume meter.")
|
||||
mcrfpy.setMusicVolume(0)
|
||||
sweet_btn.text = "Music is OFF"
|
||||
sweet_btn.sprite_number = 17
|
||||
|
||||
def sfx_toggle(self, sweet_btn, args):
|
||||
if args[3] == "end": return
|
||||
self.sfx_enabled = not self.sfx_enabled
|
||||
print(f"sfx: {self.sfx_enabled}")
|
||||
if self.sfx_enabled:
|
||||
mcrfpy.setSoundVolume(self.sfx_volume)
|
||||
sweet_btn.text = "SFX are ON"
|
||||
sweet_btn.sprite_number = 0
|
||||
else:
|
||||
self.toast_say("Use your volume keys or\nlook in Settings for a volume meter.")
|
||||
mcrfpy.setSoundVolume(0)
|
||||
sweet_btn.text = "SFX are OFF"
|
||||
sweet_btn.sprite_number = 17
|
||||
|
||||
mainmenu = MainMenu()
|
|
@ -1,144 +0,0 @@
|
|||
145# open space
|
||||
???
|
||||
?_?
|
||||
???
|
||||
|
||||
184:0.03# open space variant
|
||||
???
|
||||
?_?
|
||||
???
|
||||
|
||||
146# lone wall / pillar
|
||||
___
|
||||
_X_
|
||||
___
|
||||
|
||||
132# top left corner
|
||||
?_?
|
||||
_XX
|
||||
?X?
|
||||
|
||||
133# plain horizontal wall
|
||||
???
|
||||
XXX
|
||||
?_?
|
||||
|
||||
182:0.04# plain horizontal wall variant
|
||||
???
|
||||
XXX
|
||||
?_?
|
||||
|
||||
183:0.04# plain horizontal wall variant
|
||||
???
|
||||
XXX
|
||||
?_?
|
||||
|
||||
157:0.01# plain horizontal wall variant
|
||||
???
|
||||
XXX
|
||||
?_?
|
||||
|
||||
135# top right corner
|
||||
?_?
|
||||
XX_
|
||||
?X?
|
||||
|
||||
144@N132@s144@n144@n192@s192@S156@n171@s169@n180# Left side wall. Space on both sides rule may make the dungeon less robust (no double-walls allowed)
|
||||
?X?
|
||||
?X_
|
||||
?X?
|
||||
|
||||
147@N135@s147@n147@n193@s193@S159@n170@s168@n181# Right side wall
|
||||
?X?
|
||||
_X?
|
||||
?X?
|
||||
|
||||
156# bottom left corner
|
||||
?X?
|
||||
_XX
|
||||
?_?
|
||||
|
||||
159# bottom right corner
|
||||
?X?
|
||||
XX_
|
||||
?_?
|
||||
|
||||
192@n144@s144@s169# vertical T, left wall
|
||||
?X?
|
||||
?XX
|
||||
?X?
|
||||
|
||||
193@n147@s147@s168# vertical T, right wall
|
||||
?X?
|
||||
XX?
|
||||
?X?
|
||||
|
||||
180@s144@s144@s169# horizontal T, left wall
|
||||
???
|
||||
XXX
|
||||
?X?
|
||||
|
||||
181@s147@s147@s168# horizontal T, right wall
|
||||
???
|
||||
XXX
|
||||
?X?
|
||||
|
||||
195@W133@W182@W183@W157# wall for edge of a gap
|
||||
??_
|
||||
XX_
|
||||
?__
|
||||
|
||||
195
|
||||
?__
|
||||
XX_
|
||||
?__
|
||||
|
||||
194@E133@E182@E183@E157# wall for edge of a gap (R)
|
||||
_??
|
||||
_XX
|
||||
__?
|
||||
|
||||
194
|
||||
__?
|
||||
_XX
|
||||
__?
|
||||
|
||||
195@W133@W182@W183@W157# wall for edge of a gap
|
||||
?__
|
||||
XX_
|
||||
??_
|
||||
|
||||
194@E133@E182@E183@E157# wall for edge of a gap (R)
|
||||
__?
|
||||
_XX
|
||||
_??
|
||||
|
||||
195@W133@W182@W183@W157# wall for edge of a gap
|
||||
??_
|
||||
XX_
|
||||
?__
|
||||
|
||||
194@E133@E182@E183@E157# wall for edge of a gap (R)
|
||||
_??
|
||||
_XX
|
||||
__?
|
||||
|
||||
168@n147@n170@n135@n181@n193# right vertical wall, gap below
|
||||
?X?
|
||||
_X_
|
||||
?_?
|
||||
|
||||
169@n144@n171@n132@n192@n180# left vertical wall, gap below
|
||||
?X?
|
||||
_X_
|
||||
?_?
|
||||
|
||||
170@s147@s168@s133@s182@s183@s157@s193@s181# right vertical wall, gap above
|
||||
?_?
|
||||
_X_
|
||||
?X?
|
||||
|
||||
171@s144@s169@s133@s182@s183@s157@s171@s180# left vertical wall, gap above
|
||||
?_?
|
||||
_X_
|
||||
?X?
|
|
@ -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)
|
|
@ -1,168 +0,0 @@
|
|||
Red #ED0A3F
|
||||
Maroon #C32148
|
||||
Scarlet #FD0E35
|
||||
Brick Red #C62D42
|
||||
English Vermilion #CC474B
|
||||
Madder Lake #CC3336
|
||||
Permanent Geranium Lake #E12C2C
|
||||
Maximum Red #D92121
|
||||
Chestnut #B94E48
|
||||
Orange-Red #FF5349
|
||||
Sunset Orange #FE4C40
|
||||
Bittersweet #FE6F5E
|
||||
Dark Venetian Red #B33B24
|
||||
Venetian Red #CC553D
|
||||
Light Venetian Red #E6735C
|
||||
Vivid Tangerine #FF9980
|
||||
Middle Red #E58E73
|
||||
Burnt Orange #FF7034
|
||||
Red-Orange #FF3F34
|
||||
Orange #FF8833
|
||||
Macaroni and Cheese #FFB97B
|
||||
Middle Yellow Red #ECAC76
|
||||
Mango Tango #E77200
|
||||
Yellow-Orange #FFAE42
|
||||
Maximum Yellow Red #F2BA49
|
||||
Banana Mania #FBE7B2
|
||||
Maize #F2C649
|
||||
Orange-Yellow #F8D568
|
||||
Goldenrod #FCD667
|
||||
Dandelion #FED85D
|
||||
Yellow #FBE870
|
||||
Green-Yellow #F1E788
|
||||
Middle Yellow #FFEB00
|
||||
Olive Green #B5B35C
|
||||
Spring Green #ECEBBD
|
||||
Maximum Yellow #FAFA37
|
||||
Canary #FFFF99
|
||||
Lemon Yellow #FFFF9F
|
||||
Maximum Green Yellow #D9E650
|
||||
Middle Green Yellow #ACBF60
|
||||
Inchworm #B0E313
|
||||
Light Chrome Green #BEE64B
|
||||
Yellow-Green #C5E17A
|
||||
Maximum Green #5E8C31
|
||||
Asparagus #7BA05B
|
||||
Granny Smith Apple #9DE093
|
||||
Fern #63B76C
|
||||
Middle Green #4D8C57
|
||||
Green #01A638
|
||||
Medium Chrome Green #6CA67C
|
||||
Forest Green #5FA777
|
||||
Sea Green #93DFB8
|
||||
Shamrock #33CC99
|
||||
Mountain Meadow #1AB385
|
||||
Jungle Green #29AB87
|
||||
Caribbean Green #00CC99
|
||||
Tropical Rain Forest #00755E
|
||||
Middle Blue Green #8DD9CC
|
||||
Pine Green #01796F
|
||||
Maximum Blue Green #30BFBF
|
||||
Robin's Egg Blue #00CCCC
|
||||
Teal Blue #008080
|
||||
Light Blue #8FD8D8
|
||||
Aquamarine #458B74
|
||||
Turquoise Blue #6CDAE7
|
||||
Outer Space #2D383A
|
||||
Sky Blue #76D7EA
|
||||
Middle Blue #7ED4E6
|
||||
Blue-Green #0095B7
|
||||
Pacific Blue #009DC4
|
||||
Cerulean #02A4D3
|
||||
Maximum Blue #47ABCC
|
||||
Blue (I) #2EB4E6
|
||||
Cerulean Blue #339ACC
|
||||
Cornflower #93CCEA
|
||||
Green-Blue #2887C8
|
||||
Midnight Blue #003366
|
||||
Navy Blue #0066CC
|
||||
Denim #1560BD
|
||||
Blue (III) #0066FF
|
||||
Cadet Blue #A9B2C3
|
||||
Periwinkle #C3CDE6
|
||||
Blue (II) #4570E6
|
||||
Bluetiful #3C69E7
|
||||
Wild Blue Yonder #7A89B8
|
||||
Indigo #4F69C6
|
||||
Manatee #8D90A1
|
||||
Cobalt Blue #8C90C8
|
||||
Celestial Blue #7070CC
|
||||
Blue Bell #9999CC
|
||||
Maximum Blue Purple #ACACE6
|
||||
Violet-Blue #766EC8
|
||||
Blue-Violet #6456B7
|
||||
Ultramarine Blue #3F26BF
|
||||
Middle Blue Purple #8B72BE
|
||||
Purple Heart #652DC1
|
||||
Royal Purple #6B3FA0
|
||||
Violet (II) #8359A3
|
||||
Medium Violet #8F47B3
|
||||
Wisteria #C9A0DC
|
||||
Lavender (I) #BF8FCC
|
||||
Vivid Violet #803790
|
||||
Maximum Purple #733380
|
||||
Purple Mountains' Majesty #D6AEDD
|
||||
Fuchsia #C154C1
|
||||
Pink Flamingo #F2583E
|
||||
Violet (I) #732E6C
|
||||
Brilliant Rose #E667CE
|
||||
Orchid #E29CD2
|
||||
Plum #843179
|
||||
Medium Rose #D96CBE
|
||||
Thistle #D8BFD8
|
||||
Mulberry #C8509B
|
||||
Red-Violet #BB3385
|
||||
Middle Purple #D982B5
|
||||
Maximum Red Purple #A63A79
|
||||
Jazzberry Jam #A50B5E
|
||||
Eggplant #614051
|
||||
Magenta #F653A6
|
||||
Cerise #DA3287
|
||||
Wild Strawberry #FF3399
|
||||
Lavender (II) #FBAED2
|
||||
Cotton Candy #FFB7D5
|
||||
Carnation Pink #FFA6C9
|
||||
Violet-Red #F7468A
|
||||
Razzmatazz #E30B5C
|
||||
Piggy Pink #FDD7E4
|
||||
Carmine #E62E6B
|
||||
Blush #DB5079
|
||||
Tickle Me Pink #FC80A5
|
||||
Mauvelous #F091A9
|
||||
Salmon #FF91A4
|
||||
Middle Red Purple #A55353
|
||||
Mahogany #CA3435
|
||||
Melon #FEBAAD
|
||||
Pink Sherbert #F7A38E
|
||||
Burnt Sienna #E97451
|
||||
Brown #AF593E
|
||||
Sepia #9E5B40
|
||||
Fuzzy Wuzzy #87421F
|
||||
Beaver #926F5B
|
||||
Tumbleweed #DEA681
|
||||
Raw Sienna #D27D46
|
||||
Van Dyke Brown #664228
|
||||
Tan #FA9D5A
|
||||
Desert Sand #EDC9AF
|
||||
Peach #FFCBA4
|
||||
Burnt Umber #805533
|
||||
Apricot #FDD5B1
|
||||
Almond #EED9C4
|
||||
Raw Umber #665233
|
||||
Shadow #837050
|
||||
Raw Sienna (I) #E6BC5C
|
||||
Gold (I) #92926E
|
||||
Gold (II) #E6BE8A
|
||||
Silver #C9C0BB
|
||||
Copper #DA8A67
|
||||
Antique Brass #C88A65
|
||||
Black #000000
|
||||
Charcoal Gray #736A62
|
||||
Gray #8B8680
|
||||
Blue-Gray #C8C8CD
|
||||
Timberwolf #D9D6CF
|
||||
White #FFFFFF
|
||||
Crayellow #F1D651[6]
|
||||
Cool Mint #DDEBEC[6]
|
||||
Oatmeal #D9DAD2[6]
|
||||
Powder Blue #C0D5F0[6]
|
949
xkcd_colors.txt
949
xkcd_colors.txt
|
@ -1,949 +0,0 @@
|
|||
cloudy blue #acc2d9
|
||||
dark pastel green #56ae57
|
||||
dust #b2996e
|
||||
electric lime #a8ff04
|
||||
fresh green #69d84f
|
||||
light eggplant #894585
|
||||
nasty green #70b23f
|
||||
really light blue #d4ffff
|
||||
tea #65ab7c
|
||||
warm purple #952e8f
|
||||
yellowish tan #fcfc81
|
||||
cement #a5a391
|
||||
dark grass green #388004
|
||||
dusty teal #4c9085
|
||||
grey teal #5e9b8a
|
||||
macaroni and cheese #efb435
|
||||
pinkish tan #d99b82
|
||||
spruce #0a5f38
|
||||
strong blue #0c06f7
|
||||
toxic green #61de2a
|
||||
windows blue #3778bf
|
||||
blue blue #2242c7
|
||||
blue with a hint of purple #533cc6
|
||||
booger #9bb53c
|
||||
bright sea green #05ffa6
|
||||
dark green blue #1f6357
|
||||
deep turquoise #017374
|
||||
green teal #0cb577
|
||||
strong pink #ff0789
|
||||
bland #afa88b
|
||||
deep aqua #08787f
|
||||
lavender pink #dd85d7
|
||||
light moss green #a6c875
|
||||
light seafoam green #a7ffb5
|
||||
olive yellow #c2b709
|
||||
pig pink #e78ea5
|
||||
deep lilac #966ebd
|
||||
desert #ccad60
|
||||
dusty lavender #ac86a8
|
||||
purpley grey #947e94
|
||||
purply #983fb2
|
||||
candy pink #ff63e9
|
||||
light pastel green #b2fba5
|
||||
boring green #63b365
|
||||
kiwi green #8ee53f
|
||||
light grey green #b7e1a1
|
||||
orange pink #ff6f52
|
||||
tea green #bdf8a3
|
||||
very light brown #d3b683
|
||||
egg shell #fffcc4
|
||||
eggplant purple #430541
|
||||
powder pink #ffb2d0
|
||||
reddish grey #997570
|
||||
baby shit brown #ad900d
|
||||
liliac #c48efd
|
||||
stormy blue #507b9c
|
||||
ugly brown #7d7103
|
||||
custard #fffd78
|
||||
darkish pink #da467d
|
||||
deep brown #410200
|
||||
greenish beige #c9d179
|
||||
manilla #fffa86
|
||||
off blue #5684ae
|
||||
battleship grey #6b7c85
|
||||
browny green #6f6c0a
|
||||
bruise #7e4071
|
||||
kelley green #009337
|
||||
sickly yellow #d0e429
|
||||
sunny yellow #fff917
|
||||
azul #1d5dec
|
||||
darkgreen #054907
|
||||
green/yellow #b5ce08
|
||||
lichen #8fb67b
|
||||
light light green #c8ffb0
|
||||
pale gold #fdde6c
|
||||
sun yellow #ffdf22
|
||||
tan green #a9be70
|
||||
burple #6832e3
|
||||
butterscotch #fdb147
|
||||
toupe #c7ac7d
|
||||
dark cream #fff39a
|
||||
indian red #850e04
|
||||
light lavendar #efc0fe
|
||||
poison green #40fd14
|
||||
baby puke green #b6c406
|
||||
bright yellow green #9dff00
|
||||
charcoal grey #3c4142
|
||||
squash #f2ab15
|
||||
cinnamon #ac4f06
|
||||
light pea green #c4fe82
|
||||
radioactive green #2cfa1f
|
||||
raw sienna #9a6200
|
||||
baby purple #ca9bf7
|
||||
cocoa #875f42
|
||||
light royal blue #3a2efe
|
||||
orangeish #fd8d49
|
||||
rust brown #8b3103
|
||||
sand brown #cba560
|
||||
swamp #698339
|
||||
tealish green #0cdc73
|
||||
burnt siena #b75203
|
||||
camo #7f8f4e
|
||||
dusk blue #26538d
|
||||
fern #63a950
|
||||
old rose #c87f89
|
||||
pale light green #b1fc99
|
||||
peachy pink #ff9a8a
|
||||
rosy pink #f6688e
|
||||
light bluish green #76fda8
|
||||
light bright green #53fe5c
|
||||
light neon green #4efd54
|
||||
light seafoam #a0febf
|
||||
tiffany blue #7bf2da
|
||||
washed out green #bcf5a6
|
||||
browny orange #ca6b02
|
||||
nice blue #107ab0
|
||||
sapphire #2138ab
|
||||
greyish teal #719f91
|
||||
orangey yellow #fdb915
|
||||
parchment #fefcaf
|
||||
straw #fcf679
|
||||
very dark brown #1d0200
|
||||
terracota #cb6843
|
||||
ugly blue #31668a
|
||||
clear blue #247afd
|
||||
creme #ffffb6
|
||||
foam green #90fda9
|
||||
grey/green #86a17d
|
||||
light gold #fddc5c
|
||||
seafoam blue #78d1b6
|
||||
topaz #13bbaf
|
||||
violet pink #fb5ffc
|
||||
wintergreen #20f986
|
||||
yellow tan #ffe36e
|
||||
dark fuchsia #9d0759
|
||||
indigo blue #3a18b1
|
||||
light yellowish green #c2ff89
|
||||
pale magenta #d767ad
|
||||
rich purple #720058
|
||||
sunflower yellow #ffda03
|
||||
green/blue #01c08d
|
||||
leather #ac7434
|
||||
racing green #014600
|
||||
vivid purple #9900fa
|
||||
dark royal blue #02066f
|
||||
hazel #8e7618
|
||||
muted pink #d1768f
|
||||
booger green #96b403
|
||||
canary #fdff63
|
||||
cool grey #95a3a6
|
||||
dark taupe #7f684e
|
||||
darkish purple #751973
|
||||
true green #089404
|
||||
coral pink #ff6163
|
||||
dark sage #598556
|
||||
dark slate blue #214761
|
||||
flat blue #3c73a8
|
||||
mushroom #ba9e88
|
||||
rich blue #021bf9
|
||||
dirty purple #734a65
|
||||
greenblue #23c48b
|
||||
icky green #8fae22
|
||||
light khaki #e6f2a2
|
||||
warm blue #4b57db
|
||||
dark hot pink #d90166
|
||||
deep sea blue #015482
|
||||
carmine #9d0216
|
||||
dark yellow green #728f02
|
||||
pale peach #ffe5ad
|
||||
plum purple #4e0550
|
||||
golden rod #f9bc08
|
||||
neon red #ff073a
|
||||
old pink #c77986
|
||||
very pale blue #d6fffe
|
||||
blood orange #fe4b03
|
||||
grapefruit #fd5956
|
||||
sand yellow #fce166
|
||||
clay brown #b2713d
|
||||
dark blue grey #1f3b4d
|
||||
flat green #699d4c
|
||||
light green blue #56fca2
|
||||
warm pink #fb5581
|
||||
dodger blue #3e82fc
|
||||
gross green #a0bf16
|
||||
ice #d6fffa
|
||||
metallic blue #4f738e
|
||||
pale salmon #ffb19a
|
||||
sap green #5c8b15
|
||||
algae #54ac68
|
||||
bluey grey #89a0b0
|
||||
greeny grey #7ea07a
|
||||
highlighter green #1bfc06
|
||||
light light blue #cafffb
|
||||
light mint #b6ffbb
|
||||
raw umber #a75e09
|
||||
vivid blue #152eff
|
||||
deep lavender #8d5eb7
|
||||
dull teal #5f9e8f
|
||||
light greenish blue #63f7b4
|
||||
mud green #606602
|
||||
pinky #fc86aa
|
||||
red wine #8c0034
|
||||
shit green #758000
|
||||
tan brown #ab7e4c
|
||||
darkblue #030764
|
||||
rosa #fe86a4
|
||||
lipstick #d5174e
|
||||
pale mauve #fed0fc
|
||||
claret #680018
|
||||
dandelion #fedf08
|
||||
orangered #fe420f
|
||||
poop green #6f7c00
|
||||
ruby #ca0147
|
||||
dark #1b2431
|
||||
greenish turquoise #00fbb0
|
||||
pastel red #db5856
|
||||
piss yellow #ddd618
|
||||
bright cyan #41fdfe
|
||||
dark coral #cf524e
|
||||
algae green #21c36f
|
||||
darkish red #a90308
|
||||
reddy brown #6e1005
|
||||
blush pink #fe828c
|
||||
camouflage green #4b6113
|
||||
lawn green #4da409
|
||||
putty #beae8a
|
||||
vibrant blue #0339f8
|
||||
dark sand #a88f59
|
||||
purple/blue #5d21d0
|
||||
saffron #feb209
|
||||
twilight #4e518b
|
||||
warm brown #964e02
|
||||
bluegrey #85a3b2
|
||||
bubble gum pink #ff69af
|
||||
duck egg blue #c3fbf4
|
||||
greenish cyan #2afeb7
|
||||
petrol #005f6a
|
||||
royal #0c1793
|
||||
butter #ffff81
|
||||
dusty orange #f0833a
|
||||
off yellow #f1f33f
|
||||
pale olive green #b1d27b
|
||||
orangish #fc824a
|
||||
leaf #71aa34
|
||||
light blue grey #b7c9e2
|
||||
dried blood #4b0101
|
||||
lightish purple #a552e6
|
||||
rusty red #af2f0d
|
||||
lavender blue #8b88f8
|
||||
light grass green #9af764
|
||||
light mint green #a6fbb2
|
||||
sunflower #ffc512
|
||||
velvet #750851
|
||||
brick orange #c14a09
|
||||
lightish red #fe2f4a
|
||||
pure blue #0203e2
|
||||
twilight blue #0a437a
|
||||
violet red #a50055
|
||||
yellowy brown #ae8b0c
|
||||
carnation #fd798f
|
||||
muddy yellow #bfac05
|
||||
dark seafoam green #3eaf76
|
||||
deep rose #c74767
|
||||
dusty red #b9484e
|
||||
grey/blue #647d8e
|
||||
lemon lime #bffe28
|
||||
purple/pink #d725de
|
||||
brown yellow #b29705
|
||||
purple brown #673a3f
|
||||
wisteria #a87dc2
|
||||
banana yellow #fafe4b
|
||||
lipstick red #c0022f
|
||||
water blue #0e87cc
|
||||
brown grey #8d8468
|
||||
vibrant purple #ad03de
|
||||
baby green #8cff9e
|
||||
barf green #94ac02
|
||||
eggshell blue #c4fff7
|
||||
sandy yellow #fdee73
|
||||
cool green #33b864
|
||||
pale #fff9d0
|
||||
blue/grey #758da3
|
||||
hot magenta #f504c9
|
||||
greyblue #77a1b5
|
||||
purpley #8756e4
|
||||
baby shit green #889717
|
||||
brownish pink #c27e79
|
||||
dark aquamarine #017371
|
||||
diarrhea #9f8303
|
||||
light mustard #f7d560
|
||||
pale sky blue #bdf6fe
|
||||
turtle green #75b84f
|
||||
bright olive #9cbb04
|
||||
dark grey blue #29465b
|
||||
greeny brown #696006
|
||||
lemon green #adf802
|
||||
light periwinkle #c1c6fc
|
||||
seaweed green #35ad6b
|
||||
sunshine yellow #fffd37
|
||||
ugly purple #a442a0
|
||||
medium pink #f36196
|
||||
puke brown #947706
|
||||
very light pink #fff4f2
|
||||
viridian #1e9167
|
||||
bile #b5c306
|
||||
faded yellow #feff7f
|
||||
very pale green #cffdbc
|
||||
vibrant green #0add08
|
||||
bright lime #87fd05
|
||||
spearmint #1ef876
|
||||
light aquamarine #7bfdc7
|
||||
light sage #bcecac
|
||||
yellowgreen #bbf90f
|
||||
baby poo #ab9004
|
||||
dark seafoam #1fb57a
|
||||
deep teal #00555a
|
||||
heather #a484ac
|
||||
rust orange #c45508
|
||||
dirty blue #3f829d
|
||||
fern green #548d44
|
||||
bright lilac #c95efb
|
||||
weird green #3ae57f
|
||||
peacock blue #016795
|
||||
avocado green #87a922
|
||||
faded orange #f0944d
|
||||
grape purple #5d1451
|
||||
hot green #25ff29
|
||||
lime yellow #d0fe1d
|
||||
mango #ffa62b
|
||||
shamrock #01b44c
|
||||
bubblegum #ff6cb5
|
||||
purplish brown #6b4247
|
||||
vomit yellow #c7c10c
|
||||
pale cyan #b7fffa
|
||||
key lime #aeff6e
|
||||
tomato red #ec2d01
|
||||
lightgreen #76ff7b
|
||||
merlot #730039
|
||||
night blue #040348
|
||||
purpleish pink #df4ec8
|
||||
apple #6ecb3c
|
||||
baby poop green #8f9805
|
||||
green apple #5edc1f
|
||||
heliotrope #d94ff5
|
||||
yellow/green #c8fd3d
|
||||
almost black #070d0d
|
||||
cool blue #4984b8
|
||||
leafy green #51b73b
|
||||
mustard brown #ac7e04
|
||||
dusk #4e5481
|
||||
dull brown #876e4b
|
||||
frog green #58bc08
|
||||
vivid green #2fef10
|
||||
bright light green #2dfe54
|
||||
fluro green #0aff02
|
||||
kiwi #9cef43
|
||||
seaweed #18d17b
|
||||
navy green #35530a
|
||||
ultramarine blue #1805db
|
||||
iris #6258c4
|
||||
pastel orange #ff964f
|
||||
yellowish orange #ffab0f
|
||||
perrywinkle #8f8ce7
|
||||
tealish #24bca8
|
||||
dark plum #3f012c
|
||||
pear #cbf85f
|
||||
pinkish orange #ff724c
|
||||
midnight purple #280137
|
||||
light urple #b36ff6
|
||||
dark mint #48c072
|
||||
greenish tan #bccb7a
|
||||
light burgundy #a8415b
|
||||
turquoise blue #06b1c4
|
||||
ugly pink #cd7584
|
||||
sandy #f1da7a
|
||||
electric pink #ff0490
|
||||
muted purple #805b87
|
||||
mid green #50a747
|
||||
greyish #a8a495
|
||||
neon yellow #cfff04
|
||||
banana #ffff7e
|
||||
carnation pink #ff7fa7
|
||||
tomato #ef4026
|
||||
sea #3c9992
|
||||
muddy brown #886806
|
||||
turquoise green #04f489
|
||||
buff #fef69e
|
||||
fawn #cfaf7b
|
||||
muted blue #3b719f
|
||||
pale rose #fdc1c5
|
||||
dark mint green #20c073
|
||||
amethyst #9b5fc0
|
||||
blue/green #0f9b8e
|
||||
chestnut #742802
|
||||
sick green #9db92c
|
||||
pea #a4bf20
|
||||
rusty orange #cd5909
|
||||
stone #ada587
|
||||
rose red #be013c
|
||||
pale aqua #b8ffeb
|
||||
deep orange #dc4d01
|
||||
earth #a2653e
|
||||
mossy green #638b27
|
||||
grassy green #419c03
|
||||
pale lime green #b1ff65
|
||||
light grey blue #9dbcd4
|
||||
pale grey #fdfdfe
|
||||
asparagus #77ab56
|
||||
blueberry #464196
|
||||
purple red #990147
|
||||
pale lime #befd73
|
||||
greenish teal #32bf84
|
||||
caramel #af6f09
|
||||
deep magenta #a0025c
|
||||
light peach #ffd8b1
|
||||
milk chocolate #7f4e1e
|
||||
ocher #bf9b0c
|
||||
off green #6ba353
|
||||
purply pink #f075e6
|
||||
lightblue #7bc8f6
|
||||
dusky blue #475f94
|
||||
golden #f5bf03
|
||||
light beige #fffeb6
|
||||
butter yellow #fffd74
|
||||
dusky purple #895b7b
|
||||
french blue #436bad
|
||||
ugly yellow #d0c101
|
||||
greeny yellow #c6f808
|
||||
orangish red #f43605
|
||||
shamrock green #02c14d
|
||||
orangish brown #b25f03
|
||||
tree green #2a7e19
|
||||
deep violet #490648
|
||||
gunmetal #536267
|
||||
blue/purple #5a06ef
|
||||
cherry #cf0234
|
||||
sandy brown #c4a661
|
||||
warm grey #978a84
|
||||
dark indigo #1f0954
|
||||
midnight #03012d
|
||||
bluey green #2bb179
|
||||
grey pink #c3909b
|
||||
soft purple #a66fb5
|
||||
blood #770001
|
||||
brown red #922b05
|
||||
medium grey #7d7f7c
|
||||
berry #990f4b
|
||||
poo #8f7303
|
||||
purpley pink #c83cb9
|
||||
light salmon #fea993
|
||||
snot #acbb0d
|
||||
easter purple #c071fe
|
||||
light yellow green #ccfd7f
|
||||
dark navy blue #00022e
|
||||
drab #828344
|
||||
light rose #ffc5cb
|
||||
rouge #ab1239
|
||||
purplish red #b0054b
|
||||
slime green #99cc04
|
||||
baby poop #937c00
|
||||
irish green #019529
|
||||
pink/purple #ef1de7
|
||||
dark navy #000435
|
||||
greeny blue #42b395
|
||||
light plum #9d5783
|
||||
pinkish grey #c8aca9
|
||||
dirty orange #c87606
|
||||
rust red #aa2704
|
||||
pale lilac #e4cbff
|
||||
orangey red #fa4224
|
||||
primary blue #0804f9
|
||||
kermit green #5cb200
|
||||
brownish purple #76424e
|
||||
murky green #6c7a0e
|
||||
wheat #fbdd7e
|
||||
very dark purple #2a0134
|
||||
bottle green #044a05
|
||||
watermelon #fd4659
|
||||
deep sky blue #0d75f8
|
||||
fire engine red #fe0002
|
||||
yellow ochre #cb9d06
|
||||
pumpkin orange #fb7d07
|
||||
pale olive #b9cc81
|
||||
light lilac #edc8ff
|
||||
lightish green #61e160
|
||||
carolina blue #8ab8fe
|
||||
mulberry #920a4e
|
||||
shocking pink #fe02a2
|
||||
auburn #9a3001
|
||||
bright lime green #65fe08
|
||||
celadon #befdb7
|
||||
pinkish brown #b17261
|
||||
poo brown #885f01
|
||||
bright sky blue #02ccfe
|
||||
celery #c1fd95
|
||||
dirt brown #836539
|
||||
strawberry #fb2943
|
||||
dark lime #84b701
|
||||
copper #b66325
|
||||
medium brown #7f5112
|
||||
muted green #5fa052
|
||||
robin's egg #6dedfd
|
||||
bright aqua #0bf9ea
|
||||
bright lavender #c760ff
|
||||
ivory #ffffcb
|
||||
very light purple #f6cefc
|
||||
light navy #155084
|
||||
pink red #f5054f
|
||||
olive brown #645403
|
||||
poop brown #7a5901
|
||||
mustard green #a8b504
|
||||
ocean green #3d9973
|
||||
very dark blue #000133
|
||||
dusty green #76a973
|
||||
light navy blue #2e5a88
|
||||
minty green #0bf77d
|
||||
adobe #bd6c48
|
||||
barney #ac1db8
|
||||
jade green #2baf6a
|
||||
bright light blue #26f7fd
|
||||
light lime #aefd6c
|
||||
dark khaki #9b8f55
|
||||
orange yellow #ffad01
|
||||
ocre #c69c04
|
||||
maize #f4d054
|
||||
faded pink #de9dac
|
||||
british racing green #05480d
|
||||
sandstone #c9ae74
|
||||
mud brown #60460f
|
||||
light sea green #98f6b0
|
||||
robin egg blue #8af1fe
|
||||
aqua marine #2ee8bb
|
||||
dark sea green #11875d
|
||||
soft pink #fdb0c0
|
||||
orangey brown #b16002
|
||||
cherry red #f7022a
|
||||
burnt yellow #d5ab09
|
||||
brownish grey #86775f
|
||||
camel #c69f59
|
||||
purplish grey #7a687f
|
||||
marine #042e60
|
||||
greyish pink #c88d94
|
||||
pale turquoise #a5fbd5
|
||||
pastel yellow #fffe71
|
||||
bluey purple #6241c7
|
||||
canary yellow #fffe40
|
||||
faded red #d3494e
|
||||
sepia #985e2b
|
||||
coffee #a6814c
|
||||
bright magenta #ff08e8
|
||||
mocha #9d7651
|
||||
ecru #feffca
|
||||
purpleish #98568d
|
||||
cranberry #9e003a
|
||||
darkish green #287c37
|
||||
brown orange #b96902
|
||||
dusky rose #ba6873
|
||||
melon #ff7855
|
||||
sickly green #94b21c
|
||||
silver #c5c9c7
|
||||
purply blue #661aee
|
||||
purpleish blue #6140ef
|
||||
hospital green #9be5aa
|
||||
shit brown #7b5804
|
||||
mid blue #276ab3
|
||||
amber #feb308
|
||||
easter green #8cfd7e
|
||||
soft blue #6488ea
|
||||
cerulean blue #056eee
|
||||
golden brown #b27a01
|
||||
bright turquoise #0ffef9
|
||||
red pink #fa2a55
|
||||
red purple #820747
|
||||
greyish brown #7a6a4f
|
||||
vermillion #f4320c
|
||||
russet #a13905
|
||||
steel grey #6f828a
|
||||
lighter purple #a55af4
|
||||
bright violet #ad0afd
|
||||
prussian blue #004577
|
||||
slate green #658d6d
|
||||
dirty pink #ca7b80
|
||||
dark blue green #005249
|
||||
pine #2b5d34
|
||||
yellowy green #bff128
|
||||
dark gold #b59410
|
||||
bluish #2976bb
|
||||
darkish blue #014182
|
||||
dull red #bb3f3f
|
||||
pinky red #fc2647
|
||||
bronze #a87900
|
||||
pale teal #82cbb2
|
||||
military green #667c3e
|
||||
barbie pink #fe46a5
|
||||
bubblegum pink #fe83cc
|
||||
pea soup green #94a617
|
||||
dark mustard #a88905
|
||||
shit #7f5f00
|
||||
medium purple #9e43a2
|
||||
very dark green #062e03
|
||||
dirt #8a6e45
|
||||
dusky pink #cc7a8b
|
||||
red violet #9e0168
|
||||
lemon yellow #fdff38
|
||||
pistachio #c0fa8b
|
||||
dull yellow #eedc5b
|
||||
dark lime green #7ebd01
|
||||
denim blue #3b5b92
|
||||
teal blue #01889f
|
||||
lightish blue #3d7afd
|
||||
purpley blue #5f34e7
|
||||
light indigo #6d5acf
|
||||
swamp green #748500
|
||||
brown green #706c11
|
||||
dark maroon #3c0008
|
||||
hot purple #cb00f5
|
||||
dark forest green #002d04
|
||||
faded blue #658cbb
|
||||
drab green #749551
|
||||
light lime green #b9ff66
|
||||
snot green #9dc100
|
||||
yellowish #faee66
|
||||
light blue green #7efbb3
|
||||
bordeaux #7b002c
|
||||
light mauve #c292a1
|
||||
ocean #017b92
|
||||
marigold #fcc006
|
||||
muddy green #657432
|
||||
dull orange #d8863b
|
||||
steel #738595
|
||||
electric purple #aa23ff
|
||||
fluorescent green #08ff08
|
||||
yellowish brown #9b7a01
|
||||
blush #f29e8e
|
||||
soft green #6fc276
|
||||
bright orange #ff5b00
|
||||
lemon #fdff52
|
||||
purple grey #866f85
|
||||
acid green #8ffe09
|
||||
pale lavender #eecffe
|
||||
violet blue #510ac9
|
||||
light forest green #4f9153
|
||||
burnt red #9f2305
|
||||
khaki green #728639
|
||||
cerise #de0c62
|
||||
faded purple #916e99
|
||||
apricot #ffb16d
|
||||
dark olive green #3c4d03
|
||||
grey brown #7f7053
|
||||
green grey #77926f
|
||||
true blue #010fcc
|
||||
pale violet #ceaefa
|
||||
periwinkle blue #8f99fb
|
||||
light sky blue #c6fcff
|
||||
blurple #5539cc
|
||||
green brown #544e03
|
||||
bluegreen #017a79
|
||||
bright teal #01f9c6
|
||||
brownish yellow #c9b003
|
||||
pea soup #929901
|
||||
forest #0b5509
|
||||
barney purple #a00498
|
||||
ultramarine #2000b1
|
||||
purplish #94568c
|
||||
puke yellow #c2be0e
|
||||
bluish grey #748b97
|
||||
dark periwinkle #665fd1
|
||||
dark lilac #9c6da5
|
||||
reddish #c44240
|
||||
light maroon #a24857
|
||||
dusty purple #825f87
|
||||
terra cotta #c9643b
|
||||
avocado #90b134
|
||||
marine blue #01386a
|
||||
teal green #25a36f
|
||||
slate grey #59656d
|
||||
lighter green #75fd63
|
||||
electric green #21fc0d
|
||||
dusty blue #5a86ad
|
||||
golden yellow #fec615
|
||||
bright yellow #fffd01
|
||||
light lavender #dfc5fe
|
||||
umber #b26400
|
||||
poop #7f5e00
|
||||
dark peach #de7e5d
|
||||
jungle green #048243
|
||||
eggshell #ffffd4
|
||||
denim #3b638c
|
||||
yellow brown #b79400
|
||||
dull purple #84597e
|
||||
chocolate brown #411900
|
||||
wine red #7b0323
|
||||
neon blue #04d9ff
|
||||
dirty green #667e2c
|
||||
light tan #fbeeac
|
||||
ice blue #d7fffe
|
||||
cadet blue #4e7496
|
||||
dark mauve #874c62
|
||||
very light blue #d5ffff
|
||||
grey purple #826d8c
|
||||
pastel pink #ffbacd
|
||||
very light green #d1ffbd
|
||||
dark sky blue #448ee4
|
||||
evergreen #05472a
|
||||
dull pink #d5869d
|
||||
aubergine #3d0734
|
||||
mahogany #4a0100
|
||||
reddish orange #f8481c
|
||||
deep green #02590f
|
||||
vomit green #89a203
|
||||
purple pink #e03fd8
|
||||
dusty pink #d58a94
|
||||
faded green #7bb274
|
||||
camo green #526525
|
||||
pinky purple #c94cbe
|
||||
pink purple #db4bda
|
||||
brownish red #9e3623
|
||||
dark rose #b5485d
|
||||
mud #735c12
|
||||
brownish #9c6d57
|
||||
emerald green #028f1e
|
||||
pale brown #b1916e
|
||||
dull blue #49759c
|
||||
burnt umber #a0450e
|
||||
medium green #39ad48
|
||||
clay #b66a50
|
||||
light aqua #8cffdb
|
||||
light olive green #a4be5c
|
||||
brownish orange #cb7723
|
||||
dark aqua #05696b
|
||||
purplish pink #ce5dae
|
||||
dark salmon #c85a53
|
||||
greenish grey #96ae8d
|
||||
jade #1fa774
|
||||
ugly green #7a9703
|
||||
dark beige #ac9362
|
||||
emerald #01a049
|
||||
pale red #d9544d
|
||||
light magenta #fa5ff7
|
||||
sky #82cafc
|
||||
light cyan #acfffc
|
||||
yellow orange #fcb001
|
||||
reddish purple #910951
|
||||
reddish pink #fe2c54
|
||||
orchid #c875c4
|
||||
dirty yellow #cdc50a
|
||||
orange red #fd411e
|
||||
deep red #9a0200
|
||||
orange brown #be6400
|
||||
cobalt blue #030aa7
|
||||
neon pink #fe019a
|
||||
rose pink #f7879a
|
||||
greyish purple #887191
|
||||
raspberry #b00149
|
||||
aqua green #12e193
|
||||
salmon pink #fe7b7c
|
||||
tangerine #ff9408
|
||||
brownish green #6a6e09
|
||||
red brown #8b2e16
|
||||
greenish brown #696112
|
||||
pumpkin #e17701
|
||||
pine green #0a481e
|
||||
charcoal #343837
|
||||
baby pink #ffb7ce
|
||||
cornflower #6a79f7
|
||||
blue violet #5d06e9
|
||||
chocolate #3d1c02
|
||||
greyish green #82a67d
|
||||
scarlet #be0119
|
||||
green yellow #c9ff27
|
||||
dark olive #373e02
|
||||
sienna #a9561e
|
||||
pastel purple #caa0ff
|
||||
terracotta #ca6641
|
||||
aqua blue #02d8e9
|
||||
sage green #88b378
|
||||
blood red #980002
|
||||
deep pink #cb0162
|
||||
grass #5cac2d
|
||||
moss #769958
|
||||
pastel blue #a2bffe
|
||||
bluish green #10a674
|
||||
green blue #06b48b
|
||||
dark tan #af884a
|
||||
greenish blue #0b8b87
|
||||
pale orange #ffa756
|
||||
vomit #a2a415
|
||||
forrest green #154406
|
||||
dark lavender #856798
|
||||
dark violet #34013f
|
||||
purple blue #632de9
|
||||
dark cyan #0a888a
|
||||
olive drab #6f7632
|
||||
pinkish #d46a7e
|
||||
cobalt #1e488f
|
||||
neon purple #bc13fe
|
||||
light turquoise #7ef4cc
|
||||
apple green #76cd26
|
||||
dull green #74a662
|
||||
wine #80013f
|
||||
powder blue #b1d1fc
|
||||
off white #ffffe4
|
||||
electric blue #0652ff
|
||||
dark turquoise #045c5a
|
||||
blue purple #5729ce
|
||||
azure #069af3
|
||||
bright red #ff000d
|
||||
pinkish red #f10c45
|
||||
cornflower blue #5170d7
|
||||
light olive #acbf69
|
||||
grape #6c3461
|
||||
greyish blue #5e819d
|
||||
purplish blue #601ef9
|
||||
yellowish green #b0dd16
|
||||
greenish yellow #cdfd02
|
||||
medium blue #2c6fbb
|
||||
dusty rose #c0737a
|
||||
light violet #d6b4fc
|
||||
midnight blue #020035
|
||||
bluish purple #703be7
|
||||
red orange #fd3c06
|
||||
dark magenta #960056
|
||||
greenish #40a368
|
||||
ocean blue #03719c
|
||||
coral #fc5a50
|
||||
cream #ffffc2
|
||||
reddish brown #7f2b0a
|
||||
burnt sienna #b04e0f
|
||||
brick #a03623
|
||||
sage #87ae73
|
||||
grey green #789b73
|
||||
white #ffffff
|
||||
robin's egg blue #98eff9
|
||||
moss green #658b38
|
||||
steel blue #5a7d9a
|
||||
eggplant #380835
|
||||
light yellow #fffe7a
|
||||
leaf green #5ca904
|
||||
light grey #d8dcd6
|
||||
puke #a5a502
|
||||
pinkish purple #d648d7
|
||||
sea blue #047495
|
||||
pale purple #b790d4
|
||||
slate blue #5b7c99
|
||||
blue grey #607c8e
|
||||
hunter green #0b4008
|
||||
fuchsia #ed0dd9
|
||||
crimson #8c000f
|
||||
pale yellow #ffff84
|
||||
ochre #bf9005
|
||||
mustard yellow #d2bd0a
|
||||
light red #ff474c
|
||||
cerulean #0485d1
|
||||
pale pink #ffcfdc
|
||||
deep blue #040273
|
||||
rust #a83c09
|
||||
light teal #90e4c1
|
||||
slate #516572
|
||||
goldenrod #fac205
|
||||
dark yellow #d5b60a
|
||||
dark grey #363737
|
||||
army green #4b5d16
|
||||
grey blue #6b8ba4
|
||||
seafoam #80f9ad
|
||||
puce #a57e52
|
||||
spring green #a9f971
|
||||
dark orange #c65102
|
||||
sand #e2ca76
|
||||
pastel green #b0ff9d
|
||||
mint #9ffeb0
|
||||
light orange #fdaa48
|
||||
bright pink #fe01b1
|
||||
chartreuse #c1f80a
|
||||
deep purple #36013f
|
||||
dark brown #341c02
|
||||
taupe #b9a281
|
||||
pea green #8eab12
|
||||
puke green #9aae07
|
||||
kelly green #02ab2e
|
||||
seafoam green #7af9ab
|
||||
blue green #137e6d
|
||||
khaki #aaa662
|
||||
burgundy #610023
|
||||
dark teal #014d4e
|
||||
brick red #8f1402
|
||||
royal purple #4b006e
|
||||
plum #580f41
|
||||
mint green #8fff9f
|
||||
gold #dbb40c
|
||||
baby blue #a2cffe
|
||||
yellow green #c0fb2d
|
||||
bright purple #be03fd
|
||||
dark red #840000
|
||||
pale blue #d0fefe
|
||||
grass green #3f9b0b
|
||||
navy #01153e
|
||||
aquamarine #04d8b2
|
||||
burnt orange #c04e01
|
||||
neon green #0cff0c
|
||||
bright blue #0165fc
|
||||
rose #cf6275
|
||||
light pink #ffd1df
|
||||
mustard #ceb301
|
||||
indigo #380282
|
||||
lime #aaff32
|
||||
sea green #53fca1
|
||||
periwinkle #8e82fe
|
||||
dark pink #cb416b
|
||||
olive green #677a04
|
||||
peach #ffb07c
|
||||
pale green #c7fdb5
|
||||
light brown #ad8150
|
||||
hot pink #ff028d
|
||||
black #000000
|
||||
lilac #cea2fd
|
||||
navy blue #001146
|
||||
royal blue #0504aa
|
||||
beige #e6daa6
|
||||
salmon #ff796c
|
||||
olive #6e750e
|
||||
maroon #650021
|
||||
bright green #01ff07
|
||||
dark purple #35063e
|
||||
mauve #ae7181
|
||||
forest green #06470c
|
||||
aqua #13eac9
|
||||
cyan #00ffff
|
||||
tan #d1b26f
|
||||
dark blue #00035b
|
||||
lavender #c79fef
|
||||
turquoise #06c2ac
|
||||
dark green #033500
|
||||
violet #9a0eea
|
||||
light purple #bf77f6
|
||||
lime green #89fe05
|
||||
grey #929591
|
||||
sky blue #75bbfd
|
||||
yellow #ffff14
|
||||
magenta #c20078
|
||||
light green #96f97b
|
||||
orange #f97306
|
||||
teal #029386
|
||||
light blue #95d0fc
|
||||
red #e50000
|
||||
brown #653700
|
||||
pink #ff81c0
|
||||
blue #0343df
|
||||
green #15b01a
|
||||
purple #7e1e9c
|
Loading…
Reference in New Issue