diff --git a/source/Entity/Player.cpp b/source/Entity/Player.cpp index c4b00c55..c0131946 100644 --- a/source/Entity/Player.cpp +++ b/source/Entity/Player.cpp @@ -1580,9 +1580,232 @@ void CPlayer::SetColorB(Int32 g) const (~(0xFF) & _Func->GetPlayerColour(m_ID))); } +// ------------------------------------------------------------------------------------------------ +static inline bool SqCanBeInteger(HSQUIRRELVM vm, Int32 idx) +{ + switch (sq_gettype(vm, idx)) + { + case OT_NULL: + case OT_INTEGER: + case OT_FLOAT: + case OT_BOOL: + return true; + default: + return false; + } +} + +// ------------------------------------------------------------------------------------------------ +static SQRESULT SqGrabColor(HSQUIRRELVM vm, Int32 idx, Uint32 & color, Int32 & msgidx) +{ + const Int32 top = sq_gettop(vm); + // Is the color argument a Color3/Color4 instance? + if (sq_gettype(vm, idx) == OT_INSTANCE) + { + // Whether it failed to retrieve a Color3 value + bool failed = false; + // Attempt to extract a Color3 value + try + { + color = (Var< Color3 >(vm, idx).value.GetRGBA() | 0xFF); + } + catch (...) + { + failed = true; + } + // Did we failed to retrieve a Color3 instance? + if (failed) + { + // Attempt to extract a Color4 value + try + { + color = Var< Color4 >(vm, idx).value.GetRGBA(); + } + catch (const Sqrat::Exception & e) + { + return sq_throwerror(vm, e.Message().c_str()); + } + } + // The message starts right after the color + msgidx += 1; + } + // Is the color argument an unpacked RGBA color? + else if ((top - msgidx) >= 4 && SqCanBeInteger(vm, idx) + && SqCanBeInteger(vm, idx+1) + && SqCanBeInteger(vm, idx+2) + && SqCanBeInteger(vm, idx+3)) + { + color = SQMOD_PACK_RGBA(ConvTo< Uint8 >::From(PopStackInteger(vm, idx)), + ConvTo< Uint8 >::From(PopStackInteger(vm, idx+1)), + ConvTo< Uint8 >::From(PopStackInteger(vm, idx+2)), + ConvTo< Uint8 >::From(PopStackInteger(vm, idx+3))); + // The message starts right after the color + msgidx += 4; + } + // Is the color argument an unpacked RGB color? + else if ((top - msgidx) >= 3 && SqCanBeInteger(vm, idx) + && SqCanBeInteger(vm, idx+1) + && SqCanBeInteger(vm, idx+2)) + { + color = SQMOD_PACK_RGBA(ConvTo< Uint8 >::From(PopStackInteger(vm, idx)), + ConvTo< Uint8 >::From(PopStackInteger(vm, idx+1)), + ConvTo< Uint8 >::From(PopStackInteger(vm, idx+2)), + 0xFF); + // The message starts right after the color + msgidx += 3; + } + // Is the color argument an packed RGBA color? + else if (SqCanBeInteger(vm, idx)) + { + color = static_cast< Uint32 >(PopStackInteger(vm, idx)); + // The message starts right after the color + msgidx += 1; + } + // Is the first argument a string which can be interpreted as a color? + else if (sq_gettype(vm, idx) == OT_STRING) + { + CSStr str = nullptr; + SQInteger len = 0; + // Attempt to retrieve the color argument as a string + if (SQ_FAILED(sq_getstringandsize(vm, idx, &str, &len))) + { + return sq_throwerror(vm, "Cannot retrieve the color string"); + } + // Do we even have any color? + else if (!str || *str == '\0') + { + return sq_throwerror(vm, "Invalid or empty color string"); + } + // Skip white space at the beginning + while (std::isspace(*str) != 0) + { + ++str, --len; + } + // Is the first character a number sign? + if (*str == '#') + { + // Attempt to treat the value as a hex color + color = ConvTo< Uint32 >::From(std::strtoull(++str, nullptr, 16)); + // Adjust the color if necessary + switch (ClampMin(--len, 0)) + { + case 0: + { + color = 0x000000FF; + } break; + case 1: + { + color <<= 28; + color |= 0x00000FF; + } break; + case 2: + { + color <<= 24; + color |= 0x0000FF; + } break; + case 3: + { + color <<= 20; + color |= 0x000FF; + } break; + case 4: + { + color <<= 16; + color |= 0x00FF; + } break; + case 5: + { + color <<= 12; + color |= 0x0FF; + } break; + case 6: + { + color <<= 8; + color |= 0xFF; + } break; + case 7: + { + color <<= 4; + color |= 0x0; + } break; + } + } + // Are the first characters 0x so we can treat this as hex? + else if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X')) + { + // Attempt to treat the value as a hex color + color = ConvTo< Uint32 >::From(std::strtoull(str, nullptr, 16)); + // Adjust the color if necessary + switch (ClampMin(len-2, 0)) + { + case 0: + { + color = 0x000000FF; + } break; + case 1: + { + color <<= 28; + color |= 0x00000FF; + } break; + case 2: + { + color <<= 24; + color |= 0x0000FF; + } break; + case 3: + { + color <<= 20; + color |= 0x000FF; + } break; + case 4: + { + color <<= 16; + color |= 0x00FF; + } break; + case 5: + { + color <<= 12; + color |= 0x0FF; + } break; + case 6: + { + color <<= 8; + color |= 0xFF; + } break; + case 7: + { + color <<= 4; + color |= 0x0; + } break; + } + } + else + { + // Attempt to treat the value as a color name + try + { + color = (::SqMod::GetColor(str).GetRGBA() | 0xFF); + } + catch (...) + { + return sq_throwerror(vm, "Cannot identify color string"); + } + } + // The message starts right after the color + msgidx += 1; + } + else + { + return sq_throwerror(vm, "Unable to identify a valid color"); + } + // At this point we've extracted the color + return SQ_OK; +} + // ------------------------------------------------------------------------------------------------ SQInteger CPlayer::Msg(HSQUIRRELVM vm) { + // The function needs at least 2 arguments const Int32 top = sq_gettop(vm); // Was the message color specified? if (top <= 1) @@ -1594,6 +1817,7 @@ SQInteger CPlayer::Msg(HSQUIRRELVM vm) { return sq_throwerror(vm, "Missing message value"); } + // The player instance CPlayer * player; // Attempt to extract the argument values @@ -1606,60 +1830,49 @@ SQInteger CPlayer::Msg(HSQUIRRELVM vm) // Propagate the error return sq_throwerror(vm, e.Message().c_str()); } - // Failed to retrieve a Color4 value - bool failed = false; - // The message color - Color4 color; - // Attempt to extract the argument values - try - { - color = Var< Color4 >(vm, 2).value; - } - catch (...) - { - failed = true; - } - // Did we failed to retrieve a Color4 instance? - if (failed) - { - // Attempt to extract the argument values - try - { - color = Var< Color3 >(vm, 2).value; - } - catch (const Sqrat::Exception & e) - { - // Propagate the error - return sq_throwerror(vm, e.Message().c_str()); - } - } + // Do we have a valid player instance? if (!player) { - return sq_throwerror(vm, "Invalid player instance"); + return sq_throwerror(vm, "Invalid player instance "); } // Do we have a valid player identifier? else if (!player->IsActive()) { - return sq_throwerror(vm, "Invalid player reference"); + return sq_throwerror(vm, ToStrF("Invalid player reference [%s]", player->GetTag().c_str())); } + + // The index where the message should start + Int32 msgidx = 2; + // The message color + Uint32 color = 0; + // Attempt to identify and extract the color + const SQRESULT res = SqGrabColor(vm, 2, color, msgidx); + // Did we fail to identify a color? + if (SQ_FAILED(res)) + { + return res; // Propagate the error! + } + // Attempt to generate the string value - StackStrF val(vm, 3); + StackStrF val(vm, msgidx); // Have we failed to retrieve the string? if (SQ_FAILED(val.mRes)) { return val.mRes; // Propagate the error! } + // Send the resulted message string - const vcmpError result = _Func->SendClientMessage(player->GetID(), color.GetRGBA(), + const vcmpError result = _Func->SendClientMessage(player->GetID(), color, "%s%s%s", player->mMessagePrefix.c_str(), val.mPtr, player->mMessagePostfix.c_str()); + // Check the result if (result == vcmpErrorTooLargeInput) { - return sq_throwerror(vm, "Client message too big"); + return sq_throwerror(vm, ToStrF("Client message too big [%s]", player->GetTag().c_str())); } // This function does not return a value return 0; @@ -1679,21 +1892,19 @@ SQInteger CPlayer::MsgP(HSQUIRRELVM vm) { return sq_throwerror(vm, "Missing message value"); } + // The player instance CPlayer * player = nullptr; - // The prefix index - Uint32 index; // Attempt to extract the argument values try { player = Var< CPlayer * >(vm, 1).value; - index = Var< Uint32 >(vm, 2).value; } catch (const Sqrat::Exception & e) { - // Propagate the error return sq_throwerror(vm, e.Message().c_str()); } + // Do we have a valid player instance? if (!player) { @@ -1702,14 +1913,28 @@ SQInteger CPlayer::MsgP(HSQUIRRELVM vm) // Do we have a valid player identifier? else if (!player->IsActive()) { - return sq_throwerror(vm, "Invalid player reference"); + return sq_throwerror(vm, ToStrF("Invalid player reference [%s]", player->GetTag().c_str())); } - // Perform a range check on the specified prefix index - else if (index > SQMOD_PLAYER_MSG_PREFIXES) + + // The prefix index + Uint32 index = 0; + // Attempt to extract the argument values + try { - return sq_throwerror(vm, ToStrF("Prefix index is out of range: %u > %u", + index = ConvTo< Uint32 >::From(Var< SQInteger >(vm, 2).value); + } + catch (const Sqrat::Exception & e) + { + return sq_throwerror(vm, e.Message().c_str()); + } + + // Perform a range check on the specified prefix index + if (index >= SQMOD_PLAYER_MSG_PREFIXES) + { + return sq_throwerror(vm, ToStrF("Prefix index is out of range: %u >= %u", index, SQMOD_PLAYER_MSG_PREFIXES)); } + // Attempt to generate the string value StackStrF val(vm, 3); // Have we failed to retrieve the string? @@ -1717,6 +1942,7 @@ SQInteger CPlayer::MsgP(HSQUIRRELVM vm) { return val.mRes; // Propagate the error! } + vcmpError result = vcmpErrorNone; // Send the resulted message string if (player->mLimitPrefixPostfixMessage) @@ -1731,10 +1957,11 @@ SQInteger CPlayer::MsgP(HSQUIRRELVM vm) player->mMessagePrefixes[index].c_str(), val.mPtr, player->mMessagePostfix.c_str()); } + // Check the result if (result == vcmpErrorTooLargeInput) { - return sq_throwerror(vm, "Client message too big"); + return sq_throwerror(vm, ToStrF("Client message too big [%s]", player->GetTag().c_str())); } // This function does not return a value return 0; @@ -1744,34 +1971,34 @@ SQInteger CPlayer::MsgP(HSQUIRRELVM vm) SQInteger CPlayer::MsgEx(HSQUIRRELVM vm) { const Int32 top = sq_gettop(vm); + // Was the index of the message prefix specified? + if (top <= 1) + { + return sq_throwerror(vm, "Missing prefix index"); + } // Was the message color specified? - if (top <= 4) + else if (top <= 2) { return sq_throwerror(vm, "Missing message color"); } // Was the message value specified? - else if (top <= 5) + else if (top <= 3) { return sq_throwerror(vm, "Missing message value"); } + // The player instance CPlayer * player = nullptr; - // The message color - Uint32 color; // Attempt to extract the argument values try { player = Var< CPlayer * >(vm, 1).value; - color = SQMOD_PACK_RGBA(Var< Uint8 >(vm, 2).value, - Var< Uint8 >(vm, 3).value, - Var< Uint8 >(vm, 4).value, - Var< Uint8 >(vm, 5).value); } catch (const Sqrat::Exception & e) { - // Propagate the error return sq_throwerror(vm, e.Message().c_str()); } + // Do we have a valid player instance? if (!player) { @@ -1780,26 +2007,68 @@ SQInteger CPlayer::MsgEx(HSQUIRRELVM vm) // Do we have a valid player identifier? else if (!player->IsActive()) { - return sq_throwerror(vm, "Invalid player reference"); + return sq_throwerror(vm, ToStrF("Invalid player reference [%s]", player->GetTag().c_str())); } + + // The prefix index + Uint32 index = 0; + // Attempt to extract the argument values + try + { + index = ConvTo< Uint32 >::From(Var< SQInteger >(vm, 2).value); + } + catch (const Sqrat::Exception & e) + { + return sq_throwerror(vm, e.Message().c_str()); + } + + // Perform a range check on the specified prefix index + if (index >= SQMOD_PLAYER_MSG_PREFIXES) + { + return sq_throwerror(vm, ToStrF("Prefix index is out of range: %u >= %u", + index, SQMOD_PLAYER_MSG_PREFIXES)); + } + + // The index where the message should start + Int32 msgidx = 3; + // The message color + Uint32 color = 0; + // Attempt to identify and extract the color + const SQRESULT res = SqGrabColor(vm, 3, color, msgidx); + // Did we fail to identify a color? + if (SQ_FAILED(res)) + { + return res; // Propagate the error! + } + // Attempt to generate the string value - StackStrF val(vm, 6); + StackStrF val(vm, msgidx); // Have we failed to retrieve the string? if (SQ_FAILED(val.mRes)) { return val.mRes; // Propagate the error! } + + vcmpError result = vcmpErrorNone; // Send the resulted message string - const vcmpError result = _Func->SendClientMessage(player->GetID(), color, - "%s%s%s", - player->mMessagePrefix.c_str(), - val.mPtr, - player->mMessagePostfix.c_str()); + if (player->mLimitPrefixPostfixMessage) + { + result = _Func->SendClientMessage(player->GetID(), color, "%s%s", + player->mMessagePrefixes[index].c_str(), val.mPtr); + } + else + { + result = _Func->SendClientMessage(player->GetID(), color, "%s%s%s%s", + player->mMessagePrefix.c_str(), + player->mMessagePrefixes[index].c_str(), val.mPtr, + player->mMessagePostfix.c_str()); + } // Check the result if (result == vcmpErrorTooLargeInput) { - return sq_throwerror(vm, "Client message too big"); + return sq_throwerror(vm, ToStrF("Client message too big [%s]", player->GetTag().c_str())); } + // This function does not return a value return 0; } @@ -1813,6 +2082,7 @@ SQInteger CPlayer::Message(HSQUIRRELVM vm) { return sq_throwerror(vm, "Missing message value"); } + // The player instance CPlayer * player = nullptr; // Attempt to extract the argument values @@ -1822,9 +2092,9 @@ SQInteger CPlayer::Message(HSQUIRRELVM vm) } catch (const Sqrat::Exception & e) { - // Propagate the error return sq_throwerror(vm, e.Message().c_str()); } + // Do we have a valid player instance? if (!player) { @@ -1833,8 +2103,9 @@ SQInteger CPlayer::Message(HSQUIRRELVM vm) // Do we have a valid player identifier? else if (!player->IsActive()) { - return sq_throwerror(vm, "Invalid player reference"); + return sq_throwerror(vm, ToStrF("Invalid player reference [%s]", player->GetTag().c_str())); } + // Attempt to generate the string value StackStrF val(vm, 2); // Have we failed to retrieve the string? @@ -1842,6 +2113,7 @@ SQInteger CPlayer::Message(HSQUIRRELVM vm) { return val.mRes; // Propagate the error! } + // Send the resulted message string const vcmpError result = _Func->SendClientMessage(player->GetID(), player->mMessageColor, "%s%s%s", @@ -1851,8 +2123,9 @@ SQInteger CPlayer::Message(HSQUIRRELVM vm) // Check the result if (result == vcmpErrorTooLargeInput) { - return sq_throwerror(vm, "Client message too big"); + return sq_throwerror(vm, ToStrF("Client message too big [%s]", player->GetTag().c_str())); } + // This function does not return a value return 0; } @@ -1866,6 +2139,7 @@ SQInteger CPlayer::Announce(HSQUIRRELVM vm) { return sq_throwerror(vm, "Missing announcement value"); } + // The player instance CPlayer * player = nullptr; // Attempt to extract the argument values @@ -1875,9 +2149,9 @@ SQInteger CPlayer::Announce(HSQUIRRELVM vm) } catch (const Sqrat::Exception & e) { - // Propagate the error return sq_throwerror(vm, e.Message().c_str()); } + // Do we have a valid player instance? if (!player) { @@ -1886,8 +2160,9 @@ SQInteger CPlayer::Announce(HSQUIRRELVM vm) // Do we have a valid player identifier? else if (!player->IsActive()) { - return sq_throwerror(vm, "Invalid player reference"); + return sq_throwerror(vm, ToStrF("Invalid player reference [%s]", player->GetTag().c_str())); } + // Attempt to generate the string value StackStrF val(vm, 2); // Have we failed to retrieve the string? @@ -1895,6 +2170,7 @@ SQInteger CPlayer::Announce(HSQUIRRELVM vm) { return val.mRes; // Propagate the error! } + // Send the resulted announcement string const vcmpError result = _Func->SendGameMessage(player->GetID(), player->mAnnounceStyle, "%s%s%s", @@ -1904,12 +2180,14 @@ SQInteger CPlayer::Announce(HSQUIRRELVM vm) // Validate the result if (result == vcmpErrorArgumentOutOfBounds) { - return sq_throwerror(vm, "Invalid announcement style"); + return sq_throwerror(vm, ToStrF("Invalid announcement style %d [%s]", + player->mAnnounceStyle, player->GetTag().c_str())); } else if (result == vcmpErrorTooLargeInput) { - return sq_throwerror(vm, "Game message too big"); + return sq_throwerror(vm, ToStrF("Game message too big [%s]", player->GetTag().c_str())); } + // This function does not return a value return 0; } @@ -1928,6 +2206,7 @@ SQInteger CPlayer::AnnounceEx(HSQUIRRELVM vm) { return sq_throwerror(vm, "Missing announcement value"); } + // The player instance CPlayer * player = nullptr; // The announcement style @@ -1940,9 +2219,9 @@ SQInteger CPlayer::AnnounceEx(HSQUIRRELVM vm) } catch (const Sqrat::Exception & e) { - // Propagate the error return sq_throwerror(vm, e.Message().c_str()); } + // Do we have a valid player instance? if (!player) { @@ -1951,8 +2230,9 @@ SQInteger CPlayer::AnnounceEx(HSQUIRRELVM vm) // Do we have a valid player identifier? else if (!player->IsActive()) { - return sq_throwerror(vm, "Invalid player reference"); + return sq_throwerror(vm, ToStrF("Invalid player reference [%s]", player->GetTag().c_str())); } + // Attempt to generate the string value StackStrF val(vm, 3); // Have we failed to retrieve the string? @@ -1960,6 +2240,7 @@ SQInteger CPlayer::AnnounceEx(HSQUIRRELVM vm) { return val.mRes; // Propagate the error! } + // Send the resulted announcement string const vcmpError result = _Func->SendGameMessage(player->GetID(), style, "%s%s%s", player->mAnnouncePrefix.c_str(), @@ -1968,12 +2249,15 @@ SQInteger CPlayer::AnnounceEx(HSQUIRRELVM vm) // Validate the result if (result == vcmpErrorArgumentOutOfBounds) { - return sq_throwerror(vm, "Invalid announcement style"); + return sq_throwerror(vm, ""); + return sq_throwerror(vm, ToStrF("Invalid announcement style %d [%s]", + style, player->GetTag().c_str())); } else if (result == vcmpErrorTooLargeInput) { - return sq_throwerror(vm, "Game message too big"); + return sq_throwerror(vm, ToStrF("Game message too big [%s]", player->GetTag().c_str())); } + // This function does not return a value return 0; }