Commit 22c0d51b authored by sm's avatar sm

New version 0.8

- Fix theme
- Improve RandomEvent (support permutations of a range)
- Minor changes and bug fixes
parent 968ec9f0
stmm-games (0.8) unstable; urgency=low
* Fix theme
* Improve RandomEvent (support permutations of a range)
* Increase minor version
-- Stefano Marsili <efanomars@gmx.ch> Mon, 08 Apr 2019 19:29:39 +0100
stmm-games (0.7) unstable; urgency=low
* Fix TileCoords and Coords bugs
......
......@@ -144,5 +144,5 @@ install(TARGETS stmm-games-fake LIBRARY DESTINATION "lib" ARCHIVE DESTINATION "
install(FILES ${STMMI_HEADERS} DESTINATION "include/stmm-games-fake")
if (PKG_CONFIG_FOUND)
install(FILES ${PROJECT_BINARY_DIR}/stmm-games-fake.pc DESTINATION "lib/pkgconfig")
install(FILES "${PROJECT_BINARY_DIR}/stmm-games-fake.pc" DESTINATION "lib/pkgconfig")
endif()
......@@ -20,12 +20,12 @@
# MINOR is REVISION (implementation of interface)
# AGE is always 0
set(STMM_GAMES_FAKE_MAJOR_VERSION 0)
set(STMM_GAMES_FAKE_MINOR_VERSION 7)
set(STMM_GAMES_FAKE_MINOR_VERSION 8)
set(STMM_GAMES_FAKE_VERSION "${STMM_GAMES_FAKE_MAJOR_VERSION}.${STMM_GAMES_FAKE_MINOR_VERSION}.0")
# required stmm-games version
set(STMM_GAMES_FAKE_REQ_STMM_GAMES_MAJOR_VERSION 0)
set(STMM_GAMES_FAKE_REQ_STMM_GAMES_MINOR_VERSION 7)
set(STMM_GAMES_FAKE_REQ_STMM_GAMES_MINOR_VERSION 8)
set(STMM_GAMES_FAKE_REQ_STMM_GAMES_VERSION "${STMM_GAMES_FAKE_REQ_STMM_GAMES_MAJOR_VERSION}.${STMM_GAMES_FAKE_REQ_STMM_GAMES_MINOR_VERSION}")
include("${PROJECT_SOURCE_DIR}/../libstmm-games/stmm-games-defs.cmake")
......
......@@ -262,4 +262,4 @@ install(FILES ${STMMI_HEADERS_ANIMATIONS} DESTINATION "include/stmm-games-gtk/an
install(FILES ${STMMI_HEADERS_MODIFIERS} DESTINATION "include/stmm-games-gtk/modifiers")
install(FILES ${STMMI_HEADERS_WIDGETS} DESTINATION "include/stmm-games-gtk/widgets")
install(FILES ${PROJECT_BINARY_DIR}/stmm-games-gtk.pc DESTINATION "lib/pkgconfig")
install(FILES "${PROJECT_BINARY_DIR}/stmm-games-gtk.pc" DESTINATION "lib/pkgconfig")
......@@ -20,11 +20,11 @@
# MINOR is REVISION (implementation of interface)
# AGE is always 0
set(STMM_GAMES_GTK_MAJOR_VERSION 0)
set(STMM_GAMES_GTK_MINOR_VERSION 7)
set(STMM_GAMES_GTK_MINOR_VERSION 8)
set(STMM_GAMES_GTK_VERSION "${STMM_GAMES_GTK_MAJOR_VERSION}.${STMM_GAMES_GTK_MINOR_VERSION}.0")
set(STMM_GAMES_GTK_REQ_STMM_GAMES_MAJOR_VERSION 0)
set(STMM_GAMES_GTK_REQ_STMM_GAMES_MINOR_VERSION 7)
set(STMM_GAMES_GTK_REQ_STMM_GAMES_MINOR_VERSION 8)
set(STMM_GAMES_GTK_REQ_STMM_GAMES_VERSION "${STMM_GAMES_GTK_REQ_STMM_GAMES_MAJOR_VERSION}.${STMM_GAMES_GTK_REQ_STMM_GAMES_MINOR_VERSION}")
set(STMM_GAMES_GTK_REQ_STMM_INPUT_GTK_MAJOR_VERSION 0)
......
......@@ -229,6 +229,7 @@ set(STMMI_SOURCES
set(STMMI_DATA_DIR "${PROJECT_SOURCE_DIR}/data")
# Data files
set(STMMI_DATA_FILES
"${STMMI_DATA_DIR}/themes/common/images/background0.svg"
"${STMMI_DATA_DIR}/themes/common/images/background1.svg"
"${STMMI_DATA_DIR}/themes/common/images/background2.svg"
"${STMMI_DATA_DIR}/themes/common/images/background3.svg"
......@@ -350,7 +351,7 @@ install(TARGETS stmm-games-xml LIBRARY DESTINATION "lib" ARCHIVE DESTINATION "l
install(FILES ${STMMI_HEADERS} DESTINATION "include/stmm-games-xml")
install(FILES ${PROJECT_BINARY_DIR}/stmm-games-xml.pc DESTINATION "lib/pkgconfig")
install(FILES "${PROJECT_BINARY_DIR}/stmm-games-xml.pc" DESTINATION "lib/pkgconfig")
foreach (STMMI_DATA_CUR_FILE ${STMMI_DATA_FILES})
file(RELATIVE_PATH STMMI_DATA_CUR_REL_FILE "${STMMI_DATA_DIR}" "${STMMI_DATA_CUR_FILE}")
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
viewBox="0 0 6400 6400"
version="1.0"
id="background1"
sodipodi:docname="background1.svg"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<metadata
id="metadata888">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<cc:license
rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
<dc:title />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
<cc:permits
rdf:resource="http://web.resource.org/cc/Reproduction" />
<cc:permits
rdf:resource="http://web.resource.org/cc/Distribution" />
<cc:requires
rdf:resource="http://web.resource.org/cc/Notice" />
<cc:permits
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
<cc:requires
rdf:resource="http://web.resource.org/cc/ShareAlike" />
<cc:requires
rdf:resource="http://web.resource.org/cc/SourceCode" />
</cc:License>
</rdf:RDF>
</metadata>
<defs>
<radialGradient id="RadialGradient" gradientUnits="userSpaceOnUse"
cx="3200" cy="3200" r="4500" fx="3200" fy="3200">
<stop offset="0" stop-color="white" stop-opacity="1" />
<stop offset="0.60" stop-color="white" stop-opacity="0.9" />
<stop offset="0.70" stop-color="white" stop-opacity="0.8" />
<stop offset="1" stop-color="white" stop-opacity="0" />
</radialGradient>
<mask id="RadialMask" maskUnits="userSpaceOnUse"
x="0" y="0" width="6400" height="6400">
<rect x="0" y="0" width="6400" height="6400" fill="url(#RadialGradient)"/>
</mask>
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,0)"
style="display:inline">
<rect x="0" y="0" width="6400" height="6400" style="fill:#88AAFF"/>
</g>
<g
inkscape:label="Layer 2"
inkscape:groupmode="layer"
id="layer2"
transform="translate(0,0)"
style="display:inline"
mask="url(#RadialMask)"
description="mask=url(#RadialMask)">
<rect x="0" y="0" width="6400" height="6400" style="fill:#000000"/>
</g>
</svg>
......@@ -25,6 +25,7 @@
<DefaultFont define="Arial Bold"/>
</Fonts>
<Images>
<Image imgId="Background0" imgFile="background0.svg"/>
<Image imgId="Background1" imgFile="background1.svg"/>
<Image imgId="Background2" imgFile="background2.svg"/>
<Image imgId="Background3" imgFile="background3.svg"/>
......
......@@ -404,6 +404,19 @@ public:
if (bDefined) {
oFrom = std::move(oTempFrom);
oTo = std::move(oTempTo);
} else {
if (bMin && (oFrom < oMin)) {
oFrom = oMin;
if (oTo < oFrom) {
oTo = oFrom;
}
}
if (bMax && (oTo > oMax)) {
oTo = oMax;
if (oFrom > oTo) {
oFrom = oTo;
}
}
}
return bDefined;
}
......
......@@ -127,9 +127,29 @@ protected:
* @return The added event. Is always refEvent.get().
*/
Event* parseBaseAndAddEvent(GameCtx& oCtx, unique_ptr<Event> refEvent, const xmlpp::Element* p0Element);
/** Helper function for the 'repeat' attribute.
* Expects the value to be \> 0 or -1 (infinite). Default is -1.
* @param oCtx The context.
* @param p0EventElement The event element. Cannot be null.
* @return The repeat value.
* @throws std::runtime_exception.
*/
int32_t parseEventAttrRepeat(GameCtx& oCtx, const xmlpp::Element* p0EventElement);
/** Helper function for the 'step' attribute.
* Expects the value to be \> 0. Default is 1.
* @param oCtx The context.
* @param p0EventElement The event element. Cannot be null.
* @return The step value.
* @throws std::runtime_exception.
*/
int32_t parseEventAttrStep(GameCtx& oCtx, const xmlpp::Element* p0EventElement);
/** Helper function for the 'prob' attribute.
* Expects the value to be \> 0. Default is 1.
* @param oCtx The context.
* @param p0EventElement The event element. Cannot be null.
* @return The prob value.
* @throws std::runtime_exception.
*/
int32_t parseEventAttrRandomProb(GameCtx& oCtx, const xmlpp::Element* p0EventElement);
bool isReservedChildElementOfEvent(const std::string& sElementName) const;
......
......@@ -96,6 +96,12 @@ unique_ptr<Event> XmlPositionerEventParser::parseEventPositioner(GameCtx& oCtx,
oInit.m_nCheckEachTicks = strToNumber<int32_t>(oCtx, p0Element, s_sEventPositionerCheckEachTicksAttr, sCheckEachTicks, false
, true, 1, false, -1);
}
const auto oPairTransitionTicks = getXmlConditionalParser().getAttributeValue(oCtx, p0Element, s_sEventPositionerTransitionTicksAttr);
if (oPairTransitionTicks.first) {
const std::string& sTransitionTicks = oPairTransitionTicks.second;
oInit.m_nTransitionTicks = strToNumber<int32_t>(oCtx, p0Element, s_sEventPositionerTransitionTicksAttr, sTransitionTicks, false
, true, 1, false, -1);
}
return std::make_unique<PositionerEvent>(std::move(oInit));
}
int32_t XmlPositionerEventParser::parseEventMsgName(GameCtx& oCtx, const xmlpp::Element* p0Element, const std::string& sAttr
......
......@@ -33,6 +33,7 @@ static const std::string s_sEventRandomFromAttr = "from";
static const std::string s_sEventRandomToAttr = "to";
static const std::string s_sEventRandomSharedNameAttr = "sharedName";
static const std::string s_sEventRandomBufferSizeAttr = "bufferSize";
static const std::string s_sEventRandomPermutationsAttr = "permutations";
XmlRandomEventParser::XmlRandomEventParser()
: XmlEventParser(s_sEventRandomNodeName)
......@@ -45,15 +46,27 @@ Event* XmlRandomEventParser::parseEvent(GameCtx& oCtx, const xmlpp::Element* p0E
}
unique_ptr<Event> XmlRandomEventParser::parseEventRandom(GameCtx& oCtx, const xmlpp::Element* p0Element)
{
//std::cout << "XmlGameParser::parseEventRandom" << '\n';
//std::cout << "XmlRandomEventParser::parseEventRandom" << '\n';
oCtx.addChecker(p0Element);
RandomEvent::Init oRInit;
oRInit.m_p0Level = &oCtx.level();
const auto oPairPermutations = getXmlConditionalParser().getAttributeValue(oCtx, p0Element, s_sEventRandomPermutationsAttr);
if (oPairPermutations.first) {
oRInit.m_bPermutations = XmlCommonParser::strToBool(oCtx, p0Element, s_sEventRandomPermutationsAttr, oPairPermutations.second);
}
int32_t nMinValue = std::numeric_limits<int32_t>::min();
int32_t nMaxValue = std::numeric_limits<int32_t>::max();
if (oRInit.m_bPermutations) {
nMinValue = RandomEvent::s_nPermutationMinValue;
nMaxValue = RandomEvent::s_nPermutationMaxValue;
}
getXmlConditionalParser().parseAttributeFromTo<int32_t>(oCtx, p0Element, "</>" // impossible attribute name
, s_sEventRandomFromAttr, s_sEventRandomToAttr
, false, false, -1, false, -1, oRInit.m_nFrom, oRInit.m_nTo);
, false, true, nMinValue, true, nMaxValue, oRInit.m_nFrom, oRInit.m_nTo);
const auto oPairSharedName = getXmlConditionalParser().getAttributeValue(oCtx, p0Element, s_sEventRandomSharedNameAttr);
if (oPairSharedName.first) {
......
......@@ -79,7 +79,8 @@ static const std::string s_sEventFilterInputNewValueModAttr = "valueMod";
//static const std::string s_sEventFilterInputNewValueFloorModAttr = "valueFloorMod";
static const std::string s_sEventFilterInputNewValueMinAttr = "valueMin";
static const std::string s_sEventFilterInputNewValueMaxAttr = "valueMax";
static const std::string s_sEventFilterInputNewValuePercAttr = "valuePerc";
static const std::string s_sEventFilterInputNewValuePercAttr = "valuePercent";
static const std::string s_sEventFilterInputNewValuePermillAttr = "valuePermill";
static const std::string s_sEventListenerNodeName = "Listener";
static const std::string s_sEventListenerEventAttr = "event";
static const std::string s_sEventListenerGroupAttr = "group";
......@@ -559,9 +560,20 @@ void XmlGameParser::parseFilterInput(GameCtx& oCtx, Event* p0Event, const xmlpp:
const std::string& sPercValue = oPairPercValue.second;
nOperand = XmlCommonParser::strToNumber<int32_t>(oCtx, p0Element, s_sEventFilterInputNewValuePercAttr, sPercValue, false
, false, -1, false, -1);
eOp = Event::MSG_FILTER_VALUE_OP_PERC_ADD;
eOp = Event::MSG_FILTER_VALUE_OP_PERCENT_ADD;
sOpAttr = s_sEventFilterInputNewValuePercAttr;
}
const auto oPairPermillValue = m_oXmlConditionalParser.getAttributeValue(oCtx, p0Element, s_sEventFilterInputNewValuePermillAttr);
if (oPairPermillValue.first) {
if (eOp != Event::MSG_FILTER_VALUE_OP_UNCHANGED) {
throw XmlCommonErrors::errorAttrAlreadyDefinedByAnother(oCtx, p0Element, s_sEventFilterInputNewValuePermillAttr, sOpAttr);
}
const std::string& sPermillValue = oPairPermillValue.second;
nOperand = XmlCommonParser::strToNumber<int32_t>(oCtx, p0Element, s_sEventFilterInputNewValuePermillAttr, sPermillValue, false
, false, -1, false, -1);
eOp = Event::MSG_FILTER_VALUE_OP_PERMILL_ADD;
sOpAttr = s_sEventFilterInputNewValuePermillAttr;
}
const auto oPairMinValue = m_oXmlConditionalParser.getAttributeValue(oCtx, p0Element, s_sEventFilterInputNewValueMinAttr);
if (oPairMinValue.first) {
if (eOp != Event::MSG_FILTER_VALUE_OP_UNCHANGED) {
......
......@@ -20,12 +20,12 @@
# MINOR is REVISION (implementation of interface)
# AGE is always 0
set(STMM_GAMES_XML_MAJOR_VERSION 0)
set(STMM_GAMES_XML_MINOR_VERSION 7)
set(STMM_GAMES_XML_MINOR_VERSION 8)
set(STMM_GAMES_XML_VERSION "${STMM_GAMES_XML_MAJOR_VERSION}.${STMM_GAMES_XML_MINOR_VERSION}.0")
# required stmm-games-gtk version
set(STMM_GAMES_XML_REQ_STMM_GAMES_GTK_MAJOR_VERSION 0)
set(STMM_GAMES_XML_REQ_STMM_GAMES_GTK_MINOR_VERSION 7)
set(STMM_GAMES_XML_REQ_STMM_GAMES_GTK_MINOR_VERSION 8)
set(STMM_GAMES_XML_REQ_STMM_GAMES_GTK_VERSION "${STMM_GAMES_XML_REQ_STMM_GAMES_GTK_MAJOR_VERSION}.${STMM_GAMES_XML_REQ_STMM_GAMES_GTK_MINOR_VERSION}")
# required libxml++-2.6 version
......
......@@ -278,5 +278,5 @@ install(FILES ${STMMI_HEADERS_EVENTS} DESTINATION "include/stmm-games/events
install(FILES ${STMMI_HEADERS_UTIL} DESTINATION "include/stmm-games/util")
install(FILES ${STMMI_HEADERS_WIDGETS} DESTINATION "include/stmm-games/widgets")
if (PKG_CONFIG_FOUND)
install(FILES ${PROJECT_BINARY_DIR}/stmm-games.pc DESTINATION "lib/pkgconfig")
install(FILES "${PROJECT_BINARY_DIR}/stmm-games.pc" DESTINATION "lib/pkgconfig")
endif()
......@@ -61,14 +61,15 @@ public:
, MSG_FILTER_VALUE_OP_SET = 1 /**< The value is set. */
, MSG_FILTER_VALUE_OP_MULT_ADD = 2 /**< The value is multiplied. */
, MSG_FILTER_VALUE_OP_DIV_ADD = 3 /**< The value is divided */
, MSG_FILTER_VALUE_OP_PERC_ADD = 4 /**< The value is "percentaged". */
, MSG_FILTER_VALUE_OP_MOD_ADD = 5 /**< The value is "moduloed". */
, MSG_FILTER_VALUE_OP_MIN_ADD = 6 /**< The value is minimized. */
, MSG_FILTER_VALUE_OP_MAX_ADD = 7 /**< The value is maximized. */
, MSG_FILTER_VALUE_OP_PERCENT_ADD = 4 /**< The value is "percentaged". */
, MSG_FILTER_VALUE_OP_PERMILL_ADD = 5 /**< The value is "permilled". */
, MSG_FILTER_VALUE_OP_MOD_ADD = 6 /**< The value is "moduloed". */
, MSG_FILTER_VALUE_OP_MIN_ADD = 7 /**< The value is minimized. */
, MSG_FILTER_VALUE_OP_MAX_ADD = 8 /**< The value is maximized. */
//, MSG_FILTER_VALUE_OP_MULT_INTERVAL_ADD = 8 /**< The value is multiplied by current game interval and operand. */
//, MSG_FILTER_VALUE_OP_DIV_INTERVAL_ADD = 9 /**< The value is divided by current game interval and operand. */
//, MSG_FILTER_VALUE_OP_FLOORMOD_ADD = 10 /**< The value is "moduloed". */
, MSG_FILTER_VALUE_OP_LAST = 7
, MSG_FILTER_VALUE_OP_LAST = 8
};
/** Add a message filter.
* All messages should be numbers \>= -1.
......
......@@ -45,7 +45,7 @@ public:
{
NRect m_oTrackingRect; /**< The tracking rectangle. */
int32_t m_nCheckEachTicks = 1; /**< Check position in game ticks. Default is 1. */
int32_t m_nTransitionTicks = 1; /**< The number of game ticks it takes to get to a targetcposition. */
int32_t m_nTransitionTicks = 1; /**< The number of game ticks it takes to get to a target position. */
};
struct Init : public Event::Init, public LocalInit
{
......
......@@ -32,21 +32,41 @@
namespace stmg
{
class GameProxy;
using std::shared_ptr;
using std::weak_ptr;
/** Random number generator event.
* Permutation example: range (m_nFrom = 0, m_nTo = 2100), s_nMaxPermutationSize = 1000
* m_nTotPartitions = ceil(2101 / 1000) = 3
* m_nPartitionMinSize = 2101 / 3 = 700
* Permutation sizes: 701, 700, 700
* First permutation emitted (unshuffled): 0,3,6,9,12, .. 2097,2100
* Second permutation emitted (unshuffled): 1,4,7,10,13, .. 2098
* Third permutation emitted (unshuffled): 2,5,8,11,14, .. 2099
* Fourth permutation emitted (unshuffled): 0,3,6,9,12, .. 2097,2100
* Fifth permutation emitted (unshuffled): 1,4,7,10,13, .. 2098
* And so on
*/
class RandomEvent : public Event
{
public:
static constexpr int32_t s_nDefaultSharedBufferSize = 1000;
static constexpr int32_t s_nDefaultSharedBufferSize = 1000; /**< The default shared buffer size. */
static constexpr int32_t s_nMaxPermutationSize = 200; /**< The maximum size of a permutation of the range. */
static constexpr int32_t s_nPermutationMinValue = -32000; /**< The minimum for the Init::m_nFrom value if Init::m_bPermutations is true. */
static constexpr int32_t s_nPermutationMaxValue = +32000; /**< The minimum for the Init::m_nFrom value if Init::m_bPermutations is true. */
struct LocalInit
{
int32_t m_nFrom = std::numeric_limits<int32_t>::min(); /**< The random number lowest value. Default is std::numeric_limits<int32_t>::min(). */
int32_t m_nTo = std::numeric_limits<int32_t>::max(); /**< The random number highest value. Default is std::numeric_limits<int32_t>::max(). */
std::string m_sSharedName; /**< The shared name. If empty random sequence not shared. */
int32_t m_nBufferSize = s_nDefaultSharedBufferSize; /**< The maximal shared buffer size. Ignored if sSharedName is empty. If \<= 0 s_nDefaultSharedBufferSize is used. */
std::string m_sSharedName; /**< The shared name. For two RandomEvent instances to share the same sequence the also
* must have same (m_nFrom, m_nTo) range. If empty random sequence not shared. */
int32_t m_nBufferSize = s_nDefaultSharedBufferSize; /**< The maximal shared buffer size. Ignored if sSharedName is empty.
* If \<= 0 s_nDefaultSharedBufferSize is used. Default is s_nDefaultSharedBufferSize. */
bool m_bPermutations = false; /**< Whether output sequence is permutations of the range [m_nFrom, m_nTo].
* If the range is bigger than s_nMaxPermutationSize then subsets of the range are permutated in a cyclic way. */
};
struct Init : public Event::Init, public LocalInit
{
......@@ -77,19 +97,34 @@ public:
private:
struct RandomSequence
{
RandomSequence()
: m_oBuffer(s_nDefaultSharedBufferSize)
{
}
explicit RandomSequence(int32_t nSize)
: m_oBuffer(nSize)
//
void initPartitions();
void createPermutation(GameProxy& oGame);
int32_t nextRandom(GameProxy& oGame);
int32_t m_nFromValue = 0;
int32_t m_nToValue = 0;
int32_t m_nTotPartitions = 0;
int32_t m_nPartitionMinSize = 0;
int32_t m_nTotPartitionsWithRest = 0;
//
int32_t m_nPartitionCount = 0;
int32_t m_nPermutationCount = 0;
std::vector<int32_t> m_aPermutation;
};
struct SharedRandomSequence : public RandomSequence
{
}
SharedRandomSequence();
explicit SharedRandomSequence(int32_t nSize);
CircularBuffer<int32_t> m_oBuffer;
int32_t m_nTotConsumed = 0;
};
static shared_ptr<RandomSequence> getRandomSequence(int64_t nGameId, const std::string& sSharedName, int32_t nBufferSize);
private:
void reInitCommon();
shared_ptr<RandomSequence> getSharedRandomSequence(int64_t nGameId, const std::string& sSharedName, int32_t nBufferSize);
shared_ptr<RandomSequence> getRandomSequence();
private:
LocalInit m_oData;
......@@ -101,7 +136,9 @@ private:
{
int64_t m_nGameId;
std::string m_sSharedName;
weak_ptr<RandomSequence> m_refRandomSequence;
weak_ptr<SharedRandomSequence> m_refSharedRandomSequence;
int32_t m_nFrom;
int32_t m_nTo;
};
static std::vector<SharedRandom> m_aSharedRandoms;
......
......@@ -128,14 +128,26 @@ private:
};
friend class const_iterator;
public:
/** TileCoords iterator.
*/
class const_iterator : Coords::const_iterator
{
public:
using Coords::const_iterator::x;
using Coords::const_iterator::y;
using Coords::const_iterator::point;
using Coords::const_iterator::next;
/** Tells whether the iterator point to the same position as another.
* @param it The other iterator.
* @return Whether same position within Coords.
*/
inline bool operator==(const const_iterator& it) const { return Coords::const_iterator::operator==(it); };
/** Opposite of const_iterator::operator==().
*/
inline bool operator!=(const const_iterator& it) const { return Coords::const_iterator::operator!=(it); };
/** Get the current tile.
* @return The tile.
*/
const Tile& getTile() const
{
const int32_t nIdx = get();
......
......@@ -68,6 +68,29 @@ struct NRect
return !((oR1.m_nX + oR1.m_nW <= oR2.m_nX) || (oR2.m_nX + oR2.m_nW <= oR1.m_nX)
|| (oR1.m_nY + oR1.m_nH <= oR2.m_nY) || (oR2.m_nY + oR2.m_nH <= oR1.m_nY));
}
/** The maximal rectangle contained by two rectangles.
* @param oR1 Must have positive size.
* @param oR2 Must have positive size.
* @return Intersection rectangle or 0 size rectangle if rectangles don't intersect.
*/
static NRect intersectionRect(const NRect& oR1, const NRect& oR2)
{
assert((oR1.m_nW >= 0) && (oR1.m_nH >= 0) && (oR2.m_nW >= 0) && (oR2.m_nH >= 0));
NRect oRr;
oRr.m_nX = std::max(oR1.m_nX, oR2.m_nX);
const int32_t nToX = std::min(oR1.m_nX + oR1.m_nW - 1, oR2.m_nX + oR2.m_nW - 1);
if (oRr.m_nX > nToX) {
return NRect{};
}
oRr.m_nY = std::max(oR1.m_nY, oR2.m_nY);
const int32_t nToY = std::min(oR1.m_nY + oR1.m_nH - 1, oR2.m_nY + oR2.m_nH - 1);
if (oRr.m_nY > nToY) {
return NRect{};
}
oRr.m_nW = nToX - oRr.m_nX + 1;
oRr.m_nH = nToY - oRr.m_nY + 1;
return oRr;
}
/** The minimal rectangle containing two rectangles.
* @param oR1 Must have positive size.
* @param oR2 Must have positive size.
......
......@@ -135,14 +135,33 @@ public:
public:
friend class const_iterator;
/** Coords iterator.
*/
class const_iterator
{
public:
/** The current x position.
* @return The x.
*/
inline int32_t x() const { return Util::unpackPointFromInt64(m_it->first).m_nX; };
/** The current y position.
* @return The y.
*/
inline int32_t y() const { return Util::unpackPointFromInt64(m_it->first).m_nY; };
/** The current position.
* @return The position.
*/
inline NPoint point() const { return Util::unpackPointFromInt64(m_it->first); };
/** Move the iterator to the next coord.
*/
inline void next() { ++m_it; };
/** Tells whether the iterator point to the same position as another.
* @param it The other iterator.
* @return Whether same position within Coords.
*/
inline bool operator==(const const_iterator& it) const { return m_it == it.m_it; };
/** Opposite of const_iterator::operator==().
*/
inline bool operator!=(const const_iterator& it) const { return !(m_it == it.m_it); };
protected:
inline int32_t get() const { return m_it->second; }
......
......@@ -165,11 +165,16 @@ bool Event::filterMsg(int32_t& nMsg, int32_t& nValue)
nValue = clampToInt(fCheck);
}
} break;
case MSG_FILTER_VALUE_OP_PERC_ADD:
case MSG_FILTER_VALUE_OP_PERCENT_ADD:
{
const double fCheck = 1.0 * nValue * oMfOut.m_nOutValueOperand / 100 + oMfOut.m_nOutValueAdd;
nValue = clampToInt(fCheck);
} break;
case MSG_FILTER_VALUE_OP_PERMILL_ADD:
{
const double fCheck = 1.0 * nValue * oMfOut.m_nOutValueOperand / 1000 + oMfOut.m_nOutValueAdd;
nValue = clampToInt(fCheck);
} break;
case MSG_FILTER_VALUE_OP_MOD_ADD:
{
if (oMfOut.m_nOutValueOperand == 0) {
......
</
......@@ -23,6 +23,8 @@
#include <cassert>
#include <iostream>
#include <cmath>
#include <numeric>
namespace stmg
......@@ -30,67 +32,147 @@ namespace stmg
std::vector<RandomEvent::SharedRandom> RandomEvent::m_aSharedRandoms{};
void RandomEvent::RandomSequence::initPartitions()
{
const int32_t nTotValues = m_nToValue - m_nFromValue + 1;
m_nTotPartitions = std::ceil(1.0 * nTotValues / s_nMaxPermutationSize);
m_nPartitionMinSize = nTotValues / m_nTotPartitions;
m_nTotPartitionsWithRest = nTotValues % m_nTotPartitions;
m_nPartitionCount = 0;
m_nPermutationCount = 0;
m_aPermutation.reserve(m_nPartitionMinSize + 1);
}
void RandomEvent::RandomSequence::createPermutation(GameProxy& oGame)
{
const int32_t nPartitionSize = m_nPartitionMinSize + ((m_nPartitionCount < m_nTotPartitionsWithRest) ? 1 : 0);
m_aPermutation.resize(nPartitionSize);
int32_t nCurValue = m_nFromValue + m_nPermutationCount - m_nTotPartitions;
std::generate(m_aPermutation.begin(), m_aPermutation.end(), [&]()
{
nCurValue += m_nTotPartitions;
return nCurValue;
});
// copied from deprecated std::random_shuffle
for (int32_t nIdx = nPartitionSize - 1; nIdx > 0; --nIdx) {
const int32_t nOtherIdx = oGame.random(0, nIdx);
std::swap(m_aPermutation[nIdx], m_aPermutation[nOtherIdx]);
}
}
int32_t RandomEvent::RandomSequence::nextRandom(GameProxy& oGame)
{
if (m_nTotPartitions == 0) {
return oGame.random(m_nFromValue, m_nToValue); //-----------------------
}
const int32_t nRand = m_aPermutation[m_nPermutationCount];
++m_nPermutationCount;
if (m_nPermutationCount >= static_cast<int32_t>(m_aPermutation.size())) {
m_nPermutationCount = 0;
++m_nPartitionCount;
if (m_nPartitionCount == m_nTotPartitions) {
m_nPartitionCount = 0;
}
createPermutation(oGame);
}
return nRand;
}
RandomEvent::SharedRandomSequence::SharedRandomSequence()
: m_oBuffer(s_nDefaultSharedBufferSize)
{
}
RandomEvent::SharedRandomSequence::SharedRandomSequence(int32_t nSize)
: m_oBuffer(nSize)
{
}
RandomEvent::RandomEvent(Init&& oInit)
: Event(std::move(oInit))
, m_oData(std::move(oInit))
, m_bShared(!m_oData.m_sSharedName.empty())
, m_nTotRead(0)
{
reInitCommon();
}
void RandomEvent::reInit(Init&& oInit)
{
Event::reInit(std::move(oInit));
m_oData = std::move(oInit);
m_bShared = !m_oData.m_sSharedName.empty();
m_refRandomSequence.reset();
reInitCommon();
}
void RandomEvent::reInitCommon()
{
assert(m_oData.m_nFrom <= m_oData.m_nTo);
if (! m_oData.m_sSharedName.empty()) {
if (m_oData.m_nBufferSize <= 0) {
m_oData.m_nBufferSize = s_nDefaultSharedBufferSize;
}
}
if (m_oData.m_bPermutations) {
assert(m_oData.m_nFrom >= s_nPermutationMinValue);
assert(m_oData.m_nTo <= s_nPermutationMaxValue);
}
m_bShared = ! m_oData.m_sSharedName.empty();
m_nTotRead = 0;
}
void RandomEvent::trigger(int32_t nMsg, int32_t /*nValue*/, Event* p0TriggeringEvent)
{
//std::cout << "RandomEvent::trigger" << '\n';
if (p0TriggeringEvent == nullptr) {
return;
}
if (nMsg != MESSAGE_GENERATE) {
return; //--------------------------------------------------------------
}
Level& oLevel = level();
auto& oGame = oLevel.game();
if (m_bShared && (! m_refRandomSequence)) {
m_refRandomSequence = getRandomSequence(oGame.getGameId(), m_oData.m_sSharedName, m_oData.m_nBufferSize);
if (! m_refRandomSequence) {
if (m_bShared) {
m_refRandomSequence = getSharedRandomSequence(oGame.getGameId(), m_oData.m_sSharedName, m_oData.m_nBufferSize);
} else {
m_refRandomSequence = getRandomSequence();
}
if (m_oData.m_bPermutations) {
m_refRandomSequence->initPartitions();
}
if (nMsg != MESSAGE_GENERATE) {
return; //--------------------------------------------------------------
}