Many enhancements in client API

* Fix issues in item use
* Stack animated texts values
* Add utility functions for changing creature color and jumping
* Add some new extended functionality
* Improve map shader API
master
Eduardo Bart 11 years ago
parent aeb31f0669
commit cce2976156

@ -284,7 +284,7 @@ function onUseWith(clickedWidget, mousePosition)
if clickedWidget:getClassName() == 'UIMap' then
local tile = clickedWidget:getTile(mousePosition)
if tile then
g_game.useWith(selectedThing, tile:getTopMultiUseThing(false))
g_game.useWith(selectedThing, tile:getTopMultiUseThing())
end
elseif clickedWidget:getClassName() == 'UIItem' and not clickedWidget:isVirtual() then
g_game.useWith(selectedThing, clickedWidget:getItem())
@ -450,7 +450,7 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing)
menu:display(menuPosition)
end
function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, useThing, creatureThing, multiUseThing)
function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, useThing, creatureThing)
local keyboardModifiers = g_keyboard.getModifiers()
if not Options.getOption('classicControl') then
@ -482,20 +482,20 @@ function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, u
-- classic control
else
if multiUseThing and keyboardModifiers == KeyboardNoModifier and mouseButton == MouseRightButton and not g_mouse.isPressed(MouseLeftButton) then
if useThing and keyboardModifiers == KeyboardNoModifier and mouseButton == MouseRightButton and not g_mouse.isPressed(MouseLeftButton) then
local player = g_game.getLocalPlayer()
if creatureThing and creatureThing ~= player then
g_game.attack(creatureThing)
return true
elseif multiUseThing:isContainer() then
if multiUseThing:getParentContainer() then
g_game.open(multiUseThing, multiUseThing:getParentContainer())
elseif useThing:isContainer() then
if useThing:getParentContainer() then
g_game.open(useThing, useThing:getParentContainer())
return true
else
g_game.open(multiUseThing)
g_game.open(useThing)
return true
end
elseif multiUseThing:isMultiUse() then
elseif useThing:isMultiUse() then
startUseWith(useThing)
return true
else

@ -81,10 +81,9 @@ function UIGameMap:onMouseRelease(mousePosition, mouseButton)
lookThing = tile:getTopLookThing()
useThing = tile:getTopUseThing()
creatureThing = tile:getTopCreature()
multiUseThing = tile:getTopMultiUseThing()
end
local ret = modules.game_interface.processMouseAction(mousePosition, mouseButton, autoWalkPos, lookThing, useThing, creatureThing, multiUseThing)
local ret = modules.game_interface.processMouseAction(mousePosition, mouseButton, autoWalkPos, lookThing, useThing, creatureThing)
if ret then
self.cancelNextRelease = true
end

@ -22,6 +22,7 @@
#include "animatedtext.h"
#include "map.h"
#include "game.h"
#include <framework/core/clock.h>
#include <framework/core/eventdispatcher.h>
#include <framework/graphics/graphics.h>
@ -34,15 +35,32 @@ AnimatedText::AnimatedText()
void AnimatedText::drawText(const Point& dest, const Rect& visibleRect)
{
static float tf = Otc::ANIMATED_TEXT_DURATION;
static float tftf = Otc::ANIMATED_TEXT_DURATION * Otc::ANIMATED_TEXT_DURATION;
Point p = dest;
Size textSize = m_cachedText.getTextSize();
p.x += 20 - textSize.width() / 2;
p.y += (-20 * m_animationTimer.ticksElapsed()) / Otc::ANIMATED_TEXT_DURATION;
float t = m_animationTimer.ticksElapsed();
p.x += (24 - textSize.width() / 2);
if(g_game.getFeature(Otc::GameDiagonalAnimatedText)) {
p.x -= (4 * t / tf) + (8 * t * t / tftf);
}
p.y += 8 + (-48 * t) / tf;
p += m_offset;
Rect rect(p, textSize);
if(visibleRect.contains(rect)) {
//TODO: cache into a framebuffer
g_painter->setColor(m_color);
float t0 = tf / 1.2;
if(t > t0) {
Color color = m_color;
color.setAlpha((float)(1 - (t - t0) / (tf - t0)));
g_painter->setColor(color);
}
else
g_painter->setColor(m_color);
m_cachedText.draw(rect);
}
}
@ -65,3 +83,27 @@ void AnimatedText::setText(const std::string& text)
{
m_cachedText.setText(text);
}
bool AnimatedText::merge(const AnimatedTextPtr& other)
{
if(other->getColor() != m_color)
return false;
if(other->getCachedText().getFont() != m_cachedText.getFont())
return false;
if(m_animationTimer.ticksElapsed() > Otc::ANIMATED_TEXT_DURATION / 2.5)
return false;
try {
int number = stdext::safe_cast<int>(m_cachedText.getText());
int otherNumber = stdext::safe_cast<int>(other->getCachedText().getText());
std::string text = stdext::format("%d", number + otherNumber);
m_cachedText.setText(text);
return true;
}
catch(...) {
return false;
}
}

@ -38,6 +38,14 @@ public:
void setColor(int color);
void setText(const std::string& text);
void setOffset(const Point& offset) { m_offset = offset; }
Color getColor() { return m_color; }
const CachedText& getCachedText() const { return m_cachedText; }
Point getOffset() { return m_offset; }
Timer getTimer() { return m_animationTimer; }
bool merge(const AnimatedTextPtr& other);
AnimatedTextPtr asAnimatedText() { return static_self_cast<AnimatedText>(); }
bool isAnimatedText() { return true; }
@ -49,6 +57,7 @@ private:
Color m_color;
Timer m_animationTimer;
CachedText m_cachedText;
Point m_offset;
};
#endif

@ -37,6 +37,7 @@ void Client::init(std::vector<std::string>& args)
// register needed lua functions
registerLuaFunctions();
g_map.init();
g_game.init();
g_shaders.init();
g_things.init();

@ -32,15 +32,7 @@ namespace Otc
SEA_FLOOR = 7,
MAX_Z = 15,
UNDERGROUND_FLOOR = SEA_FLOOR+1,
VISIBLE_X_TILES = 15,
VISIBLE_Y_TILES = 11,
AWARE_UNDEGROUND_FLOOR_RANGE = 2,
AWARE_X_TILES = VISIBLE_X_TILES + 3,
AWARE_Y_TILES = VISIBLE_Y_TILES + 3,
AWARE_X_LEFT_TILES = AWARE_X_TILES/2 - 1,
AWARE_X_RIGHT_TILES = AWARE_X_TILES/2,
AWARE_Y_TOP_TILES = AWARE_Y_TILES/2 - 1,
AWARE_Y_BOTTOM_TILES = AWARE_Y_TILES/2,
INVISIBLE_TICKS_PER_FRAME = 500,
ITEM_TICKS_PER_FRAME = 500,
@ -343,9 +335,18 @@ namespace Otc
GameFormatCreatureName = 22,
GameSpellList = 23,
GameClientPing = 24,
GameLoginPending = 25,
GameNewSpeedLaw = 26,
// 23-50 unused yet
GameExtendedClientPing = 25,
GameUpdater = 26,
GameLoginLocale = 27,
GameDoubleHealth = 28,
GameDoubleSkills = 29,
GameChangeMapAwareRange = 30,
GameMapMovePosition = 31,
GameAttackSeq = 32,
GameBlueNpcNameColor = 33,
GameDiagonalAnimatedText = 34,
GameLoginPending = 35,
GameNewSpeedLaw = 36,
// 51-100 reserved to be defined in lua
LastGameFeature = 101
};

@ -58,6 +58,7 @@ Creature::Creature() : Thing()
m_nameCache.setAlign(Fw::AlignTopCenter);
m_footStep = 0;
m_speedFormula.fill(-1);
m_outfitColor = Color::white;
}
void Creature::draw(const Point& dest, float scaleFactor, bool animate, LightView *lightView)
@ -89,7 +90,7 @@ void Creature::draw(const Point& dest, float scaleFactor, bool animate, LightVie
// local player always have a minimum light in complete darkness
if(isLocalPlayer() && (g_map.getLight().intensity < 64 || m_position.z > Otc::SEA_FLOOR)) {
light.intensity = std::max<uint8>(light.intensity, 2);
light.intensity = std::max<uint8>(light.intensity, 3);
if(light.color == 0 || light.color > 215)
light.color = 215;
}
@ -101,6 +102,8 @@ void Creature::draw(const Point& dest, float scaleFactor, bool animate, LightVie
void Creature::internalDrawOutfit(Point dest, float scaleFactor, bool animateWalk, bool animateIdle, Otc::Direction direction, LightView *lightView)
{
g_painter->setColor(m_outfitColor);
// outfit is a real creature
if(m_outfit.getCategory() == ThingCategoryCreature) {
int animationPhase = animateWalk ? m_walkAnimationPhase : 0;
@ -128,6 +131,9 @@ void Creature::internalDrawOutfit(Point dest, float scaleFactor, bool animateWal
zPattern = 1;
}
PointF jumpOffset = m_jumpOffset * scaleFactor;
dest -= Point(stdext::round(jumpOffset.x), stdext::round(jumpOffset.y));
// yPattern => creature addon
for(int yPattern = 0; yPattern < getNumPatternY(); yPattern++) {
@ -181,6 +187,8 @@ void Creature::internalDrawOutfit(Point dest, float scaleFactor, bool animateWal
type->draw(dest - (getDisplacement() * scaleFactor), scaleFactor, 0, 0, 0, 0, animationPhase, lightView);
}
g_painter->resetColor();
}
void Creature::drawOutfit(const Rect& destRect, bool resize)
@ -250,6 +258,11 @@ void Creature::drawInformation(const Point& point, bool useGray, const Rect& par
g_painter->setColor(Color::black);
g_painter->drawFilledRect(backgroundRect);
if(g_game.getFeature(Otc::GameBlueNpcNameColor) && isNpc() && m_healthPercent == 100 && !useGray)
g_painter->setColor(Color(0x66, 0xcc, 0xff));
else
g_painter->setColor(fillColor);
g_painter->setColor(fillColor);
g_painter->drawFilledRect(healthRect);
@ -320,6 +333,58 @@ void Creature::stopWalk()
terminateWalk();
}
void Creature::jump(int height, int duration)
{
if(!m_jumpOffset.isNull())
return;
m_jumpTimer.restart();
m_jumpHeight = height;
m_jumpDuration = duration;
updateJump();
}
void Creature::updateJump()
{
int t = m_jumpTimer.ticksElapsed();
double a = -4 * m_jumpHeight / (m_jumpDuration * m_jumpDuration);
double b = +4 * m_jumpHeight / (m_jumpDuration);
double height = a*t*t + b*t;
int roundHeight = stdext::round(height);
int halfJumpDuration = m_jumpDuration / 2;
// schedules next update
if(m_jumpTimer.ticksElapsed() < m_jumpDuration) {
m_jumpOffset = PointF(height, height);
int diff = 0;
if(m_jumpTimer.ticksElapsed() < halfJumpDuration)
diff = 1;
else if(m_jumpTimer.ticksElapsed() > halfJumpDuration)
diff = -1;
int nextT, i = 1;
do {
nextT = stdext::round((-b + std::sqrt(std::max(b*b + 4*a*(roundHeight+diff*i), 0.0)) * diff) / (2*a));
++i;
if(nextT < halfJumpDuration)
diff = 1;
else if(nextT > halfJumpDuration)
diff = -1;
} while(nextT - m_jumpTimer.ticksElapsed() == 0 && i < 3);
auto self = static_self_cast<Creature>();
g_dispatcher.scheduleEvent([self] {
self->updateJump();
}, nextT - m_jumpTimer.ticksElapsed());
}
else
m_jumpOffset = PointF(0, 0);
}
void Creature::onPositionChange(const Position& newPos, const Position& oldPos)
{
callLuaField("onPositionChange", newPos, oldPos);
@ -560,6 +625,37 @@ void Creature::setOutfit(const Outfit& outfit)
callLuaField("onOutfitChange", m_outfit, oldOutfit);
}
void Creature::setOutfitColor(const Color& color, int duration)
{
if(m_outfitColorUpdateEvent) {
m_outfitColorUpdateEvent->cancel();
m_outfitColorUpdateEvent = nullptr;
}
if(duration > 0) {
Color delta = (color - m_outfitColor) / (float)duration;
m_outfitColorTimer.restart();
updateOutfitColor(m_outfitColor, color, delta, duration);
}
else
m_outfitColor = color;
}
void Creature::updateOutfitColor(Color color, Color finalColor, Color delta, int duration)
{
if(m_outfitColorTimer.ticksElapsed() < duration) {
m_outfitColor = color + delta * m_outfitColorTimer.ticksElapsed();
auto self = static_self_cast<Creature>();
m_outfitColorUpdateEvent = g_dispatcher.scheduleEvent([=] {
self->updateOutfitColor(color, finalColor, delta, duration);
}, 100);
}
else {
m_outfitColor = finalColor;
}
}
void Creature::setSpeed(uint16 speed)
{
uint16 oldSpeed = m_speed;
@ -707,11 +803,15 @@ int Creature::getStepDuration(bool ignoreDiagonal)
if(g_game.getProtocolVersion() >= 900)
interval = (interval / g_game.getServerBeat()) * g_game.getServerBeat();
float factor = 3;
if(g_game.getProtocolVersion() <= 810)
factor = 2;
interval = std::max(interval, g_game.getServerBeat());
if(!ignoreDiagonal && (m_lastStepDirection == Otc::NorthWest || m_lastStepDirection == Otc::NorthEast ||
m_lastStepDirection == Otc::SouthWest || m_lastStepDirection == Otc::SouthEast))
interval *= 3;
interval *= factor;
return interval;
}

@ -55,6 +55,7 @@ public:
void setHealthPercent(uint8 healthPercent);
void setDirection(Otc::Direction direction);
void setOutfit(const Outfit& outfit);
void setOutfitColor(const Color& color, int duration);
void setLight(const Light& light) { m_light = light; }
void setSpeed(uint16 speed);
void setSkull(uint8 skull);
@ -98,11 +99,13 @@ public:
virtual int getDisplacementX();
virtual int getDisplacementY();
virtual int getExactSize(int layer = 0, int xPattern = 0, int yPattern = 0, int zPattern = 0, int animationPhase = 0);
PointF getJumpOffset() { return m_jumpOffset; }
void updateShield();
// walk related
void turn(Otc::Direction direction);
void jump(int height, int duration);
virtual void walk(const Position& oldPos, const Position& newPos);
virtual void stopWalk();
void allowAppearWalk() { m_allowAppearWalk = true; }
@ -129,6 +132,9 @@ protected:
virtual void updateWalk();
virtual void terminateWalk();
void updateOutfitColor(Color color, Color finalColor, Color delta, int duration);
void updateJump();
uint32 m_id;
std::string m_name;
uint8 m_healthPercent;
@ -152,6 +158,9 @@ protected:
stdext::boolean<true> m_removed;
CachedText m_nameCache;
Color m_informationColor;
Color m_outfitColor;
ScheduledEventPtr m_outfitColorUpdateEvent;
Timer m_outfitColorTimer;
std::array<double, Otc::LastSpeedFormula> m_speedFormula;
@ -173,6 +182,12 @@ protected:
Position m_lastStepFromPosition;
Position m_lastStepToPosition;
Position m_oldPosition;
// jump related
float m_jumpHeight;
float m_jumpDuration;
PointF m_jumpOffset;
Timer m_jumpTimer;
};
// @bindclass

@ -39,6 +39,7 @@ Game g_game;
Game::Game()
{
m_protocolVersion = 0;
m_clientCustomOs = -1;
m_clientVersion = 0;
m_online = false;
m_denyBotCall = false;
@ -98,6 +99,7 @@ void Game::resetGameStates()
m_containers.clear();
m_vips.clear();
m_gmActions.clear();
g_map.resetAwareRange();
}
void Game::processConnectionError(const boost::system::error_code& ec)
@ -123,6 +125,11 @@ void Game::processDisconnect()
}
}
void Game::processUpdateNeeded(const std::string& signature)
{
g_lua.callGlobalField("g_game", "onUpdateNeeded", signature);
}
void Game::processLoginError(const std::string& error)
{
g_lua.callGlobalField("g_game", "onLoginError", error);
@ -164,7 +171,7 @@ void Game::processGameStart()
g_lua.callGlobalField("g_game", "onGameStart");
disableBotCall();
if(g_game.getFeature(Otc::GameClientPing)) {
if(g_game.getFeature(Otc::GameClientPing) || g_game.getFeature(Otc::GameExtendedClientPing)) {
m_protocolGame->sendPing();
m_pingEvent = g_dispatcher.cycleEvent([this] {
if(m_protocolGame && m_protocolGame->isConnected()) {
@ -464,7 +471,7 @@ void Game::processWalkCancel(Otc::Direction direction)
m_localPlayer->cancelWalk(direction);
}
void Game::loginWorld(const std::string& account, const std::string& password, const std::string& worldName, const std::string& worldHost, int worldPort, const std::string& characterName)
void Game::loginWorld(const std::string& account, const std::string& password, const std::string& worldName, const std::string& worldHost, int worldPort, const std::string& characterName, const std::string& locale)
{
if(m_protocolGame || isOnline())
stdext::throw_exception("Unable to login into a world while already online or logging.");
@ -479,7 +486,7 @@ void Game::loginWorld(const std::string& account, const std::string& password, c
m_localPlayer->setName(characterName);
m_protocolGame = ProtocolGamePtr(new ProtocolGame);
m_protocolGame->login(account, password, worldHost, (uint16)worldPort, characterName);
m_protocolGame->login(account, password, worldHost, (uint16)worldPort, characterName, locale);
m_characterName = characterName;
m_worldName = worldName;
}
@ -608,15 +615,13 @@ void Game::autoWalk(std::vector<Otc::Direction> dirs)
if(isFollowing())
cancelFollow();
auto it = dirs.begin();
Otc::Direction direction = *it;
Otc::Direction direction = dirs.front();
if(m_localPlayer->canWalk(direction)) {
TilePtr toTile = g_map.getTile(m_localPlayer->getPosition().translatedToDirection(direction));
if(toTile && toTile->isWalkable() && !m_localPlayer->isAutoWalking())
{
if(toTile && toTile->isWalkable() && !m_localPlayer->isAutoWalking()) {
m_localPlayer->preWalk(direction);
forceWalk(direction);
dirs.erase(it);
//forceWalk(direction);
//dirs.erase(it);
}
}
@ -774,7 +779,7 @@ void Game::useWith(const ItemPtr& item, const ThingPtr& toThing)
if(!pos.isValid()) // virtual item
pos = Position(0xFFFF, 0, 0); // means that is a item in inventory
if(toThing->isCreature() && g_game.getProtocolVersion() >= 860)
if(toThing->isCreature())
m_protocolGame->sendUseOnCreature(pos, item->getId(), item->getStackpos(), toThing->getId());
else
m_protocolGame->sendUseItemWith(pos, item->getId(), item->getStackpos(), toThing->getPosition(), toThing->getId(), toThing->getStackpos());
@ -793,10 +798,10 @@ void Game::useInventoryItemWith(int itemId, const ThingPtr& toThing)
m_protocolGame->sendUseItemWith(pos, itemId, 0, toThing->getPosition(), toThing->getId(), toThing->getStackpos());
}
void Game::open(const ItemPtr& item, const ContainerPtr& previousContainer)
int Game::open(const ItemPtr& item, const ContainerPtr& previousContainer)
{
if(!canPerformGameAction() || !item)
return;
return -1;
int id = 0;
if(!previousContainer)
@ -805,6 +810,7 @@ void Game::open(const ItemPtr& item, const ContainerPtr& previousContainer)
id = previousContainer->getId();
m_protocolGame->sendUseItem(item->getPosition(), item->getId(), item->getStackpos(), id);
return id;
}
void Game::openParent(const ContainerPtr& container)
@ -1218,6 +1224,13 @@ void Game::ping()
m_denyBotCall = true;
}
void Game::changeMapAwareRange(int xrange, int yrange)
{
if(!canPerformGameAction())
return;
m_protocolGame->sendChangeMapAwareRange(xrange, yrange);
}
bool Game::checkBotProtection()
{
#ifdef BOT_PROTECTION
@ -1269,6 +1282,10 @@ void Game::setProtocolVersion(int version)
enableFeature(Otc::GameCreatureEmblems);
}
if(version >= 860) {
enableFeature(Otc::GameAttackSeq);
}
if(version >= 862) {
enableFeature(Otc::GamePenalityOnDeath);
}
@ -1350,8 +1367,18 @@ void Game::setFollowingCreature(const CreaturePtr& creature)
std::string Game::formatCreatureName(const std::string& name)
{
std::string formatedName = name;
if(getFeature(Otc::GameFormatCreatureName) && name.length() > 0)
formatedName[0] = stdext::upchar(formatedName[0]);
if(getFeature(Otc::GameFormatCreatureName) && name.length() > 0) {
bool upnext = true;
for(uint i=0;i<formatedName.length();++i) {
char ch = formatedName[i];
if(upnext) {
formatedName[i] = stdext::upchar(ch);
upnext = false;
}
if(ch == ' ')
upnext = true;
}
}
return formatedName;
}
@ -1362,3 +1389,16 @@ int Game::findEmptyContainerId()
id++;
return id;
}
int Game::getOs()
{
if(m_clientCustomOs >= 0)
return m_clientCustomOs;
if(g_app.getOs() == "windows")
return 10;
else if(g_app.getOs() == "mac")
return 12;
else
return 10;
}

@ -56,6 +56,7 @@ protected:
void processPing();
void processPingBack(int elapsed);
void processUpdateNeeded(const std::string& signature);
void processLoginError(const std::string& error);
void processLoginAdvice(const std::string& message);
void processLoginWait(const std::string& message, int time);
@ -134,7 +135,7 @@ protected:
public:
// login related
void loginWorld(const std::string& account, const std::string& password, const std::string& worldName, const std::string& worldHost, int worldPort, const std::string& characterName);
void loginWorld(const std::string& account, const std::string& password, const std::string& worldName, const std::string& worldHost, int worldPort, const std::string& characterName, const std::string& locale);
void cancelLogin();
void forceLogout();
void safeLogout();
@ -157,7 +158,7 @@ public:
void useInventoryItemWith(int itemId, const ThingPtr& toThing);
// container related
void open(const ItemPtr& item, const ContainerPtr& previousContainer);
int open(const ItemPtr& item, const ContainerPtr& previousContainer);
void openParent(const ContainerPtr& container);
void close(const ContainerPtr& container);
void refreshContainer(const ContainerPtr& container);
@ -246,6 +247,9 @@ public:
//void reportRuleViolation2();
void ping();
// otclient only
void changeMapAwareRange(int xrange, int yrange);
// dynamic support for game features
void enableFeature(Otc::GameFeature feature) { m_features.set(feature, true); }
void disableFeature(Otc::GameFeature feature) { m_features.set(feature, false); }
@ -258,6 +262,12 @@ public:
void setClientVersion(int version);
int getClientVersion() { return m_clientVersion; }
void setCustomOs(int os) { m_clientCustomOs = os; }
int getOs();
void setUpdaterSignature(const std::string& sig) { m_clientSignature = sig; }
std::string getUpdaterSignature() { return m_clientSignature; }
bool canPerformGameAction();
bool checkBotProtection();
@ -281,6 +291,7 @@ public:
std::string getCharacterName() { return m_characterName; }
std::string getWorldName() { return m_worldName; }
std::vector<uint8> getGMActions() { return m_gmActions; }
bool isGM() { return m_gmActions.size() > 0; }
std::string formatCreatureName(const std::string &name);
int findEmptyContainerId();
@ -319,6 +330,8 @@ private:
ScheduledEventPtr m_walkEvent;
int m_protocolVersion;
int m_clientVersion;
std::string m_clientSignature;
int m_clientCustomOs;
};
extern Game g_game;

@ -513,5 +513,5 @@ void LocalPlayer::setSpells(const std::vector<int>& spells)
bool LocalPlayer::hasSight(const Position& pos)
{
return m_position.isInRange(pos, (Otc::VISIBLE_X_TILES - 1)/2, (Otc::VISIBLE_Y_TILES - 1)/2);
return m_position.isInRange(pos, g_map.getAwareRange().left - 1, g_map.getAwareRange().top - 1);
}

@ -54,6 +54,7 @@ void Client::registerLuaFunctions()
g_lua.bindSingletonFunction("g_things", "loadDat", &ThingTypeManager::loadDat, &g_things);
g_lua.bindSingletonFunction("g_things", "loadOtb", &ThingTypeManager::loadOtb, &g_things);
g_lua.bindSingletonFunction("g_things", "loadXml", &ThingTypeManager::loadXml, &g_things);
g_lua.bindSingletonFunction("g_things", "loadOtml", &ThingTypeManager::loadOtml, &g_things);
g_lua.bindSingletonFunction("g_things", "isDatLoaded", &ThingTypeManager::isDatLoaded, &g_things);
g_lua.bindSingletonFunction("g_things", "isOtbLoaded", &ThingTypeManager::isOtbLoaded, &g_things);
g_lua.bindSingletonFunction("g_things", "getDatSignature", &ThingTypeManager::getDatSignature, &g_things);
@ -205,6 +206,7 @@ void Client::registerLuaFunctions()
g_lua.bindSingletonFunction("g_game", "mount", &Game::mount, &g_game);
g_lua.bindSingletonFunction("g_game", "requestItemInfo", &Game::requestItemInfo, &g_game);
g_lua.bindSingletonFunction("g_game", "ping", &Game::ping, &g_game);
g_lua.bindSingletonFunction("g_game", "changeMapAwareRange", &Game::changeMapAwareRange, &g_game);
g_lua.bindSingletonFunction("g_game", "canPerformGameAction", &Game::canPerformGameAction, &g_game);
g_lua.bindSingletonFunction("g_game", "canReportBugs", &Game::canReportBugs, &g_game);
g_lua.bindSingletonFunction("g_game", "checkBotProtection", &Game::checkBotProtection, &g_game);
@ -225,12 +227,17 @@ void Client::registerLuaFunctions()
g_lua.bindSingletonFunction("g_game", "setProtocolVersion", &Game::setProtocolVersion, &g_game);
g_lua.bindSingletonFunction("g_game", "getClientVersion", &Game::getClientVersion, &g_game);
g_lua.bindSingletonFunction("g_game", "setClientVersion", &Game::setClientVersion, &g_game);
g_lua.bindSingletonFunction("g_game", "setUpdaterSignature", &Game::setUpdaterSignature, &g_game);
g_lua.bindSingletonFunction("g_game", "setCustomOs", &Game::setCustomOs, &g_game);
g_lua.bindSingletonFunction("g_game", "getOs", &Game::getOs, &g_game);
g_lua.bindSingletonFunction("g_game", "getCharacterName", &Game::getCharacterName, &g_game);
g_lua.bindSingletonFunction("g_game", "getWorldName", &Game::getWorldName, &g_game);
g_lua.bindSingletonFunction("g_game", "getGMActions", &Game::getGMActions, &g_game);
g_lua.bindSingletonFunction("g_game", "getFeature", &Game::getFeature, &g_game);
g_lua.bindSingletonFunction("g_game", "setFeature", &Game::setFeature, &g_game);
g_lua.bindSingletonFunction("g_game", "enableFeature", &Game::enableFeature, &g_game);
g_lua.bindSingletonFunction("g_game", "disableFeature", &Game::disableFeature, &g_game);
g_lua.bindSingletonFunction("g_game", "isGM", &Game::isGM, &g_game);
g_lua.bindSingletonFunction("g_game", "answerModalDialog", &Game::answerModalDialog, &g_game);
g_lua.registerSingletonClass("g_shaders");
@ -278,6 +285,7 @@ void Client::registerLuaFunctions()
g_lua.bindClassMemberFunction<Thing>("getPosition", &Thing::getPosition);
g_lua.bindClassMemberFunction<Thing>("getStackPriority", &Thing::getStackPriority);
g_lua.bindClassMemberFunction<Thing>("getAnimationPhases", &Thing::getAnimationPhases);
g_lua.bindClassMemberFunction<Thing>("getTile", &Thing::getTile);
g_lua.bindClassMemberFunction<Thing>("isItem", &Thing::isItem);
g_lua.bindClassMemberFunction<Thing>("isMonster", &Thing::isMonster);
g_lua.bindClassMemberFunction<Thing>("isNpc", &Thing::isNpc);
@ -364,6 +372,7 @@ void Client::registerLuaFunctions()
g_lua.bindClassMemberFunction<Creature>("getEmblem", &Creature::getEmblem);
g_lua.bindClassMemberFunction<Creature>("setOutfit", &Creature::setOutfit);
g_lua.bindClassMemberFunction<Creature>("getOutfit", &Creature::getOutfit);
g_lua.bindClassMemberFunction<Creature>("setOutfitColor", &Creature::setOutfitColor);
g_lua.bindClassMemberFunction<Creature>("getDirection", &Creature::getDirection);
g_lua.bindClassMemberFunction<Creature>("getStepDuration", &Creature::getStepDuration);
g_lua.bindClassMemberFunction<Creature>("getStepProgress", &Creature::getStepProgress);
@ -378,6 +387,7 @@ void Client::registerLuaFunctions()
g_lua.bindClassMemberFunction<Creature>("isWalking", &Creature::isWalking);
g_lua.bindClassMemberFunction<Creature>("isInvisible", &Creature::isInvisible);
g_lua.bindClassMemberFunction<Creature>("canBeSeen", &Creature::canBeSeen);
g_lua.bindClassMemberFunction<Creature>("jump", &Creature::jump);
g_lua.registerClass<ItemType>();
g_lua.bindClassMemberFunction<ItemType>("getServerId", &ItemType::getServerId);
@ -407,6 +417,10 @@ void Client::registerLuaFunctions()
g_lua.registerClass<StaticText, Thing>();
g_lua.bindClassStaticFunction<StaticText>("create", []{ return StaticTextPtr(new StaticText); });
g_lua.bindClassMemberFunction<StaticText>("addMessage", &StaticText::addMessage);
g_lua.bindClassMemberFunction<StaticText>("setText", &StaticText::setText);
g_lua.bindClassMemberFunction<StaticText>("setFont", &StaticText::setFont);
g_lua.bindClassMemberFunction<StaticText>("setColor", &StaticText::setColor);
g_lua.bindClassMemberFunction<StaticText>("getColor", &StaticText::getColor);
g_lua.registerClass<AnimatedText, Thing>();
@ -469,6 +483,8 @@ void Client::registerLuaFunctions()
g_lua.bindClassMemberFunction<Tile>("clean", &Tile::clean);
g_lua.bindClassMemberFunction<Tile>("addThing", &Tile::addThing);
g_lua.bindClassMemberFunction<Tile>("getThing", &Tile::getThing);
g_lua.bindClassMemberFunction<Tile>("getThings", &Tile::getThings);
g_lua.bindClassMemberFunction<Tile>("getItems", &Tile::getItems);
g_lua.bindClassMemberFunction<Tile>("getThingStackpos", &Tile::getThingStackpos);
g_lua.bindClassMemberFunction<Tile>("getThingCount", &Tile::getThingCount);
g_lua.bindClassMemberFunction<Tile>("getTopThing", &Tile::getTopThing);
@ -495,6 +511,7 @@ void Client::registerLuaFunctions()
g_lua.bindClassMemberFunction<UIItem>("setItemId", &UIItem::setItemId);
g_lua.bindClassMemberFunction<UIItem>("setItemCount", &UIItem::setItemCount);
g_lua.bindClassMemberFunction<UIItem>("setItemSubType", &UIItem::setItemSubType);
g_lua.bindClassMemberFunction<UIItem>("setItemVisible", &UIItem::setItemVisible);
g_lua.bindClassMemberFunction<UIItem>("setItem", &UIItem::setItem);
g_lua.bindClassMemberFunction<UIItem>("setVirtual", &UIItem::setVirtual);
g_lua.bindClassMemberFunction<UIItem>("clearItem", &UIItem::clearItem);
@ -503,6 +520,7 @@ void Client::registerLuaFunctions()
g_lua.bindClassMemberFunction<UIItem>("getItemSubType", &UIItem::getItemSubType);
g_lua.bindClassMemberFunction<UIItem>("getItem", &UIItem::getItem);
g_lua.bindClassMemberFunction<UIItem>("isVirtual", &UIItem::isVirtual);
g_lua.bindClassMemberFunction<UIItem>("isItemVisible", &UIItem::isItemVisible);
g_lua.registerClass<UICreature, UIWidget>();
g_lua.bindClassStaticFunction<UICreature>("create", []{ return UICreaturePtr(new UICreature); } );

@ -35,6 +35,11 @@
Map g_map;
TilePtr Map::m_nulltile;
void Map::init()
{
resetAwareRange();
}
void Map::terminate()
{
clean();
@ -100,14 +105,39 @@ void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos)
if(thing->isItem() || thing->isCreature() || thing->isEffect()) {
const TilePtr& tile = getOrCreateTile(pos);
tile->addThing(thing, stackPos);
if(tile)
tile->addThing(thing, stackPos);
} else {
if(thing->isMissile()) {
m_floorMissiles[pos.z].push_back(thing->static_self_cast<Missile>());
thing->onAppear();
} else if(thing->isAnimatedText()) {
// this code will stack animated texts of the same color
AnimatedTextPtr animatedText = thing->static_self_cast<AnimatedText>();
m_animatedTexts.push_back(animatedText);
AnimatedTextPtr prevAnimatedText;
bool merged = false;
for(auto other : m_animatedTexts) {
if(other->getPosition() == pos) {
prevAnimatedText = other;
if(other->merge(animatedText)) {
merged = true;
break;
}
}
}
if(!merged) {
if(prevAnimatedText) {
Point offset = prevAnimatedText->getOffset();
float t = prevAnimatedText->getTimer().ticksElapsed();
if(t < Otc::ANIMATED_TEXT_DURATION / 4.0) { // didnt move 12 pixels
int y = 12 - 48 * t / (float)Otc::ANIMATED_TEXT_DURATION;
offset += Point(0, y);
}
offset.y = std::min(offset.y, 12);
animatedText->setOffset(offset);
}
m_animatedTexts.push_back(animatedText);
}
} else if(thing->isStaticText()) {
StaticTextPtr staticText = thing->static_self_cast<StaticText>();
bool mustAdd = true;
@ -119,10 +149,10 @@ void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos)
}
}
if(mustAdd) {
if(mustAdd)
m_staticTexts.push_back(staticText);
staticText->onAppear();
}
else
return;
}
thing->setPosition(pos);
@ -181,6 +211,16 @@ bool Map::removeThingByPos(const Position& pos, int stackPos)
return false;
}
StaticTextPtr Map::getStaticText(const Position& pos)
{
for(auto staticText : m_staticTexts) {
// try to combine messages
if(staticText->getPosition() == pos)
return staticText;
}
return nullptr;
}
const TilePtr& Map::createTile(const Position& pos)
{
if(!pos.isMapPosition())
@ -251,6 +291,13 @@ void Map::cleanTile(const Position& pos)
notificateTileUpdateToMapViews(pos);
}
}
for(auto it = m_staticTexts.begin();it != m_staticTexts.end();) {
const StaticTextPtr& staticText = *it;
if(staticText->getPosition() == pos && staticText->getMessageMode() == Otc::MessageNone)
it = m_staticTexts.erase(it);
else
++it;
}
}
void Map::addCreature(const CreaturePtr& creature)
@ -276,10 +323,8 @@ void Map::removeCreatureById(uint32 id)
m_knownCreatures.erase(it);
}
void Map::setCentralPosition(const Position& centralPosition)
void Map::removeUnawareThings()
{
m_centralPosition = centralPosition;
// remove creatures from tiles that we are not aware anymore
for(const auto& pair : m_knownCreatures) {
const CreaturePtr& creature = pair.second;
@ -287,6 +332,25 @@ void Map::setCentralPosition(const Position& centralPosition)
removeThing(creature);
}
// remove static texts from tiles that we are not aware anymore
for(auto it = m_staticTexts.begin(); it != m_staticTexts.end();) {
const StaticTextPtr& staticText = *it;
if(staticText->getMessageMode() == Otc::MessageNone && !isAwareOfPosition(staticText->getPosition()))
it = m_staticTexts.erase(it);
else
++it;
}
}
void Map::setCentralPosition(const Position& centralPosition)
{
if(m_centralPosition == centralPosition)
return;
m_centralPosition = centralPosition;
removeUnawareThings();
// this fixes local player position when the local player is removed from the map,
// the local player is removed from the map when there are too many creatures on his tile,
// so there is no enough stackpos to the server send him
@ -313,9 +377,14 @@ void Map::setCentralPosition(const Position& centralPosition)
mapView->onMapCenterChange(centralPosition);
}
std::vector<CreaturePtr> Map::getSightSpectators(const Position& centerPos, bool multiFloor)
{
return getSpectatorsInRangeEx(centerPos, multiFloor, m_awareRange.left - 1, m_awareRange.right - 2, m_awareRange.top - 1, m_awareRange.bottom - 2);
}
std::vector<CreaturePtr> Map::getSpectators(const Position& centerPos, bool multiFloor)
{
return getSpectatorsInRange(centerPos, multiFloor, (Otc::VISIBLE_X_TILES - 1)/2, (Otc::VISIBLE_Y_TILES - 1)/2);
return getSpectatorsInRangeEx(centerPos, multiFloor, m_awareRange.left, m_awareRange.right, m_awareRange.top, m_awareRange.bottom);
}
std::vector<CreaturePtr> Map::getSpectatorsInRange(const Position& centerPos, bool multiFloor, int xRange, int yRange)
@ -410,10 +479,26 @@ bool Map::isAwareOfPosition(const Position& pos)
else
groundedPos.coveredDown();
}
return m_centralPosition.isInRange(groundedPos, Otc::AWARE_X_LEFT_TILES,
Otc::AWARE_X_RIGHT_TILES,
Otc::AWARE_Y_TOP_TILES,
Otc::AWARE_Y_BOTTOM_TILES);
return m_centralPosition.isInRange(groundedPos, m_awareRange.left,
m_awareRange.right,
m_awareRange.top,
m_awareRange.bottom);
}
void Map::setAwareRange(const AwareRange& range)
{
m_awareRange = range;
removeUnawareThings();
}
void Map::resetAwareRange()
{
AwareRange range;
range.left = 8;
range.top = 6;
range.bottom = 7;
range.right = 9;
setAwareRange(range);
}
int Map::getFirstAwareFloor()

@ -119,10 +119,22 @@ private:
std::array<TilePtr, BLOCK_SIZE*BLOCK_SIZE> m_tiles;
};
struct AwareRange
{
int top;
int right;
int bottom;
int left;
int horizontal() { return left + right + 1; }
int vertical() { return top + bottom + 1; }
};
//@bindsingleton g_map
class Map
{
public:
void init();
void terminate();
void addMapView(const MapViewPtr& mapView);
@ -157,6 +169,8 @@ public:
bool removeThing(const ThingPtr& thing);
bool removeThingByPos(const Position& pos, int stackPos);
StaticTextPtr getStaticText(const Position& pos);
// tile related
const TilePtr& createTile(const Position& pos);
template <typename... Items>
@ -169,6 +183,7 @@ public:
void addCreature(const CreaturePtr& creature);
CreaturePtr getCreatureById(uint32 id);
void removeCreatureById(uint32 id);
std::vector<CreaturePtr> getSightSpectators(const Position& centerPos, bool multiFloor);
std::vector<CreaturePtr> getSpectators(const Position& centerPos, bool multiFloor);
std::vector<CreaturePtr> getSpectatorsInRange(const Position& centerPos, bool multiFloor, int xRange, int yRange);
std::vector<CreaturePtr> getSpectatorsInRangeEx(const Position& centerPos, bool multiFloor, int minXRange, int maxXRange, int minYRange, int maxYRange);
@ -181,6 +196,10 @@ public:
bool isCompletelyCovered(const Position& pos, int firstFloor = 0);
bool isAwareOfPosition(const Position& pos);
void setAwareRange(const AwareRange& range);
void resetAwareRange();
AwareRange getAwareRange() { return m_awareRange; }
Light getLight() { return m_light; }
Position getCentralPosition() { return m_centralPosition; }
int getFirstAwareFloor();
@ -193,6 +212,7 @@ public:
std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> findPath(const Position& start, const Position& goal, int maxSteps, int flags = 0);
private:
void removeUnawareThings();
uint getBlockIndex(const Position& pos) { return ((pos.y / BLOCK_SIZE) * (65536 / BLOCK_SIZE)) + (pos.x / BLOCK_SIZE); }
std::unordered_map<uint, TileBlock> m_tileBlocks[Otc::MAX_Z+1];
@ -208,6 +228,7 @@ private:
Rect m_tilesRect;
stdext::packed_storage<uint8> m_attribs;
AwareRange m_awareRange;
static TilePtr m_nulltile;
};

@ -57,8 +57,10 @@ MapView::MapView()
m_cachedFirstVisibleFloor = 7;
m_cachedLastVisibleFloor = 7;
m_updateTilesPos = 0;
m_fadeOutTime = 0;
m_fadeInTime = 0;
m_minimumAmbientLight = 0;
m_optimizedSize = Size(Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES) * Otc::TILE_PIXELS;
m_optimizedSize = Size(g_map.getAwareRange().horizontal(), g_map.getAwareRange().vertical()) * Otc::TILE_PIXELS;
m_framebuffer = g_framebuffers.createFrameBuffer();
setVisibleDimension(Size(15, 11));
@ -155,6 +157,21 @@ void MapView::draw(const Rect& rect)
m_mustDrawVisibleTilesCache = false;
}
float fadeOpacity = 1.0f;
if(!m_shaderSwitchDone && m_fadeOutTime > 0) {
fadeOpacity = 1.0f - (m_fadeTimer.timeElapsed() / m_fadeOutTime);
if(fadeOpacity < 0.0f) {
m_shader = m_nextShader;
m_nextShader = nullptr;
m_shaderSwitchDone = true;
m_fadeTimer.restart();
}
}
if(m_shaderSwitchDone && m_shader && m_fadeInTime > 0)
fadeOpacity = std::min(m_fadeTimer.timeElapsed() / m_fadeInTime, 1.0f);
Point drawOffset = ((m_drawDimension - m_visibleDimension - Size(1,1)).toPoint()/2) * m_tileSize;
if(isFollowingCreature())
drawOffset += m_followingCreature->getWalkOffset() * scaleFactor;
@ -166,9 +183,20 @@ void MapView::draw(const Rect& rect)
drawOffset.y += (srcVisible.height() - srcSize.height()) / 2;
Rect srcRect = Rect(drawOffset, srcSize);
if(m_shader && g_painter->hasShaders() && g_graphics.shouldUseShaders() && m_viewMode == NEAR_VIEW) {
Rect framebufferRect = Rect(0,0, m_drawDimension * m_tileSize);
Point center = srcRect.center();
Point globalCoord = Point(cameraPosition.x - m_drawDimension.width()/2, -(cameraPosition.y - m_drawDimension.height()/2)) * m_tileSize;
m_shader->bind();
m_shader->setUniformValue(ShaderManager::MAP_CENTER_COORD, center.x / (float)framebufferRect.width(), 1.0f - center.y / (float)framebufferRect.height());
m_shader->setUniformValue(ShaderManager::MAP_GLOBAL_COORD, globalCoord.x / (float)framebufferRect.height(), globalCoord.y / (float)framebufferRect.height());
m_shader->setUniformValue(ShaderManager::MAP_ZOOM, scaleFactor);
g_painter->setShaderProgram(m_shader);
}
g_painter->setColor(Color::white);
g_painter->setOpacity(fadeOpacity);
glDisable(GL_BLEND);
g_painter->setShaderProgram(m_shader);
#if 0
// debug source area
g_painter->saveAndResetState();
@ -182,6 +210,7 @@ void MapView::draw(const Rect& rect)
m_framebuffer->draw(rect, srcRect);
#endif
g_painter->resetShaderProgram();
g_painter->resetOpacity();
glEnable(GL_BLEND);
@ -198,10 +227,11 @@ void MapView::draw(const Rect& rect)
if(!creature->canBeSeen())
continue;
PointF jumpOffset = creature->getJumpOffset() * scaleFactor;
Point creatureOffset = Point(16 - creature->getDisplacementX(), -3 - creature->getDisplacementY());
Position pos = creature->getPosition();
Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset;
p += (creature->getDrawOffset() + creatureOffset) * scaleFactor;
p += (creature->getDrawOffset() + creatureOffset) * scaleFactor - Point(stdext::round(jumpOffset.x), stdext::round(jumpOffset.y));
p.x = p.x * horizontalStretchFactor;
p.y = p.y * verticalStretchFactor;
p += rect.topLeft();
@ -222,6 +252,9 @@ void MapView::draw(const Rect& rect)
//if(pos.z != cameraPosition.z && !staticText->isYell())
// continue;
if(pos.z != cameraPosition.z && staticText->getMessageMode() == Otc::MessageNone)
continue;
Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset;
p.x = p.x * horizontalStretchFactor;
p.y = p.y * verticalStretchFactor;
@ -232,6 +265,7 @@ void MapView::draw(const Rect& rect)
for(const AnimatedTextPtr& animatedText : g_map.getAnimatedTexts()) {
Position pos = animatedText->getPosition();
/*
// only draw animated texts from visible floors
if(pos.z < m_cachedFirstVisibleFloor || pos.z > m_cachedLastVisibleFloor)
continue;
@ -239,6 +273,9 @@ void MapView::draw(const Rect& rect)
// dont draw animated texts from covered tiles
if(pos.z != cameraPosition.z && g_map.isCovered(pos, m_cachedFirstVisibleFloor))
continue;
*/
if(pos.z != cameraPosition.z)
continue;
Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset;
p.x = p.x * horizontalStretchFactor;
@ -408,7 +445,7 @@ void MapView::updateVisibleTilesCache(int start)
}
if(start == 0 && m_viewMode <= NEAR_VIEW)
m_cachedFloorVisibleCreatures = g_map.getSpectators(cameraPosition, false);
m_cachedFloorVisibleCreatures = g_map.getSightSpectators(cameraPosition, false);
}
void MapView::updateGeometry(const Size& visibleDimension, const Size& optimizedSize)
@ -459,7 +496,7 @@ void MapView::updateGeometry(const Size& visibleDimension, const Size& optimized
}
// draw actually more than what is needed to avoid massive recalculations on huge views
/*
/*
if(viewMode >= HUGE_VIEW) {
Size oldDimension = drawDimension;
drawDimension = (m_framebuffer->getSize() / tileSize);
@ -656,10 +693,30 @@ void MapView::setDrawMinimapColors(bool enable)
m_framebuffer->setSmooth(m_smooth);
}
void MapView::setShader(const PainterShaderProgramPtr& shader, float fadein, float fadeout)
{
if((m_shader == shader && m_shaderSwitchDone) || (m_nextShader == shader && !m_shaderSwitchDone))
return;
if(fadeout > 0.0f && m_shader) {
m_nextShader = shader;
m_shaderSwitchDone = false;
} else {
m_shader = shader;
m_nextShader = nullptr;
m_shaderSwitchDone = true;
}
m_fadeTimer.restart();
m_fadeInTime = fadein;
m_fadeOutTime = fadeout;
}
void MapView::setDrawLights(bool enable)
{
if(enable == m_drawLights)
return;
if(enable)
m_lightView = LightViewPtr(new LightView);
else

@ -107,10 +107,9 @@ public:
void setDrawLights(bool enable);
bool isDrawingLights() { return m_drawLights; }
void setShader(const PainterShaderProgramPtr& shader) { m_shader = shader; }
void setShader(const PainterShaderProgramPtr& shader, float fadein, float fadeout);
PainterShaderProgramPtr getShader() { return m_shader; }
MapViewPtr asMapView() { return static_self_cast<MapView>(); }
private:
@ -153,6 +152,11 @@ private:
std::vector<Point> m_spiral;
LightViewPtr m_lightView;
float m_minimumAmbientLight;
Timer m_fadeTimer;
PainterShaderProgramPtr m_nextShader;
float m_fadeInTime;
float m_fadeOutTime;
stdext::boolean<true> m_shaderSwitchDone;
};
#endif

@ -33,7 +33,8 @@ namespace Proto {
LoginServerCharacterList = 100
};
enum CreatureOpcode {
enum ItemOpcode {
StaticText = 96,
UnknownCreature = 97,
OutdatedCreature = 98,
Creature = 99
@ -44,6 +45,7 @@ namespace Proto {
GameServerInitGame = 10,
GameServerGMActions = 11,
GameServerEnterGame = 15,
GameServerUpdateNeeded = 17,
GameServerLoginError = 20,
GameServerLoginAdvice = 21,
GameServerLoginWait = 22,
@ -61,6 +63,7 @@ namespace Proto {
// NOTE: add any custom opcodes in this range
// 51 - 99
GameServerChangeMapAwareRange = 51,
// original tibia ONLY
GameServerFullMap = 100,
@ -156,6 +159,7 @@ namespace Proto {
// otclient ONLY
ClientExtendedOpcode = 50,
ClientChangeMapAwareRange = 51,
// NOTE: add any custom opcodes in this range
// 51 - 99

@ -26,11 +26,12 @@
#include "item.h"
#include "localplayer.h"
void ProtocolGame::login(const std::string& accountName, const std::string& accountPassword, const std::string& host, uint16 port, const std::string& characterName)
void ProtocolGame::login(const std::string& accountName, const std::string& accountPassword, const std::string& host, uint16 port, const std::string& characterName, const std::string& locale)
{
m_accountName = accountName;
m_accountPassword = accountPassword;
m_characterName = characterName;
m_locale = locale;
connect(host, port);
}

@ -31,7 +31,7 @@
class ProtocolGame : public Protocol
{
public:
void login(const std::string& accountName, const std::string& accountPassword, const std::string& host, uint16 port, const std::string& characterName);
void login(const std::string& accountName, const std::string& accountPassword, const std::string& host, uint16 port, const std::string& characterName, const std::string& locale);
void send(const OutputMessagePtr& outputMessage);
void sendExtendedOpcode(uint8 opcode, const std::string& buffer);
@ -107,6 +107,7 @@ public:
void sendNewNewRuleViolation(int reason, int action, const std::string& characterName, const std::string& comment, const std::string& translation);
void sendRequestItemInfo(int itemId, int subType, int index);
void sendAnswerModalDialog(int dialog, int button, int choice);
void sendChangeMapAwareRange(int xrange, int yrange);
protected:
void onConnect();
@ -124,6 +125,7 @@ private:
void parseEnterGame(const InputMessagePtr& msg);
void parseInitGame(const InputMessagePtr& msg);
void parseGMActions(const InputMessagePtr& msg);
void parseUpdateNeeded(const InputMessagePtr& msg);
void parseLoginError(const InputMessagePtr& msg);
void parseLoginAdvice(const InputMessagePtr& msg);
void parseLoginWait(const InputMessagePtr& msg);
@ -205,6 +207,7 @@ private:
void parsePlayerInventory(const InputMessagePtr& msg);
void parseShowModalDialog(const InputMessagePtr& msg);
void parseExtendedOpcode(const InputMessagePtr& msg);
void parseChangeMapAwareRange(const InputMessagePtr& msg);
public:
void setMapDescription(const InputMessagePtr& msg, int x, int y, int z, int width, int height);
@ -215,6 +218,7 @@ public:
ThingPtr getThing(const InputMessagePtr& msg);
ThingPtr getMappedThing(const InputMessagePtr& msg);
CreaturePtr getCreature(const InputMessagePtr& msg, int type = 0);
StaticTextPtr getStaticText(const InputMessagePtr& msg, int type = 0);
ItemPtr getItem(const InputMessagePtr& msg, int id = 0);
Position getPosition(const InputMessagePtr& msg);
@ -226,6 +230,7 @@ private:
std::string m_accountName;
std::string m_accountPassword;
std::string m_characterName;
std::string m_locale;
stdext::timer m_pingTimer;
LocalPlayerPtr m_localPlayer;
};

@ -72,6 +72,9 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
case Proto::GameServerGMActions:
parseGMActions(msg);
break;
case Proto::GameServerUpdateNeeded:
parseUpdateNeeded(msg);
break;
case Proto::GameServerLoginError:
parseLoginError(msg);
break;
@ -314,13 +317,16 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
case Proto::GameServerPlayerDataBasic:
parsePlayerInfo(msg);
break;
// PROTOCOL>=970
case Proto::GameServerShowModalDialog:
parseShowModalDialog(msg);
break;
// otclient ONLY
case Proto::GameServerExtendedOpcode:
parseExtendedOpcode(msg);
break;
// PROTOCOL>=970
case Proto::GameServerShowModalDialog:
parseShowModalDialog(msg);
case Proto::GameServerChangeMapAwareRange:
parseChangeMapAwareRange(msg);
break;
default:
stdext::throw_exception(stdext::format("unhandled opcode %d", (int)opcode));
@ -386,6 +392,12 @@ void ProtocolGame::parseGMActions(const InputMessagePtr& msg)
g_game.processGMActions(actions);
}
void ProtocolGame::parseUpdateNeeded(const InputMessagePtr& msg)
{
std::string signature = msg->getString();
g_game.processUpdateNeeded(signature);
}
void ProtocolGame::parseLoginError(const InputMessagePtr& msg)
{
std::string error = msg->getString();
@ -441,7 +453,9 @@ void ProtocolGame::parseMapDescription(const InputMessagePtr& msg)
m_localPlayer->setPosition(pos);
g_map.setCentralPosition(pos);
setMapDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z, Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES);
AwareRange range = g_map.getAwareRange();
setMapDescription(msg, pos.x - range.left, pos.y - range.top, pos.z, range.horizontal(), range.vertical());
if(!m_mapKnown) {
g_dispatcher.addEvent([] { g_lua.callGlobalField("g_game", "onMapKnown"); });
@ -453,37 +467,57 @@ void ProtocolGame::parseMapDescription(const InputMessagePtr& msg)
void ProtocolGame::parseMapMoveNorth(const InputMessagePtr& msg)
{
Position pos = g_map.getCentralPosition();
Position pos;
if(g_game.getFeature(Otc::GameMapMovePosition))
pos = getPosition(msg);
else
pos = g_map.getCentralPosition();
pos.y--;
setMapDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z, Otc::AWARE_X_TILES, 1);
AwareRange range = g_map.getAwareRange();
setMapDescription(msg, pos.x - range.left, pos.y - range.top, pos.z, range.horizontal(), 1);
g_map.setCentralPosition(pos);
}
void ProtocolGame::parseMapMoveEast(const InputMessagePtr& msg)
{
Position pos = g_map.getCentralPosition();
Position pos;
if(g_game.getFeature(Otc::GameMapMovePosition))
pos = getPosition(msg);
else
pos = g_map.getCentralPosition();
pos.x++;
setMapDescription(msg, pos.x + Otc::AWARE_X_RIGHT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z, 1, Otc::AWARE_Y_TILES);
AwareRange range = g_map.getAwareRange();
setMapDescription(msg, pos.x + range.right, pos.y - range.top, pos.z, 1, range.vertical());
g_map.setCentralPosition(pos);
}
void ProtocolGame::parseMapMoveSouth(const InputMessagePtr& msg)
{
Position pos = g_map.getCentralPosition();
Position pos;
if(g_game.getFeature(Otc::GameMapMovePosition))
pos = getPosition(msg);
else
pos = g_map.getCentralPosition();
pos.y++;
setMapDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y + Otc::AWARE_Y_BOTTOM_TILES, pos.z, Otc::AWARE_X_TILES, 1);
AwareRange range = g_map.getAwareRange();
setMapDescription(msg, pos.x - range.left, pos.y + range.bottom, pos.z, range.horizontal(), 1);
g_map.setCentralPosition(pos);
}
void ProtocolGame::parseMapMoveWest(const InputMessagePtr& msg)
{
Position pos = g_map.getCentralPosition();
Position pos;
if(g_game.getFeature(Otc::GameMapMovePosition))
pos = getPosition(msg);
else
pos = g_map.getCentralPosition();
pos.x--;
setMapDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z, 1, Otc::AWARE_Y_TILES);
AwareRange range = g_map.getAwareRange();
setMapDescription(msg, pos.x - range.left, pos.y - range.top, pos.z, 1, range.vertical());
g_map.setCentralPosition(pos);
}
@ -914,8 +948,16 @@ void ProtocolGame::parsePlayerInfo(const InputMessagePtr& msg)
void ProtocolGame::parsePlayerStats(const InputMessagePtr& msg)
{
double health = msg->getU16();
double maxHealth = msg->getU16();
double health;
double maxHealth;
if(g_game.getFeature(Otc::GameDoubleHealth)) {
health = msg->getU32();
maxHealth = msg->getU32();
} else {
health = msg->getU16();
maxHealth = msg->getU16();
}
double freeCapacity;
if(g_game.getFeature(Otc::GameDoubleFreeCapacity))
@ -935,8 +977,17 @@ void ProtocolGame::parsePlayerStats(const InputMessagePtr& msg)
double level = msg->getU16();
double levelPercent = msg->getU8();
double mana = msg->getU16();
double maxMana = msg->getU16();
double mana;
double maxMana;
if(g_game.getFeature(Otc::GameDoubleHealth)) {
mana = msg->getU32();
maxMana = msg->getU32();
} else {
mana = msg->getU16();
maxMana = msg->getU16();
}
double magicLevel = msg->getU8();
double baseMagicLevel;
@ -979,10 +1030,16 @@ void ProtocolGame::parsePlayerStats(const InputMessagePtr& msg)
void ProtocolGame::parsePlayerSkills(const InputMessagePtr& msg)
{
for(int skill = 0; skill < Otc::LastSkill; skill++) {
int level = msg->getU8();
int level;
if(g_game.getFeature(Otc::GameDoubleSkills))
level = msg->getU16();
else
level = msg->getU8();
int baseLevel;
if(g_game.getFeature(Otc::GameSkillsBase))
baseLevel = msg->getU8(); // base
baseLevel = msg->getU8();
else
baseLevel = level;
@ -1002,7 +1059,7 @@ void ProtocolGame::parsePlayerState(const InputMessagePtr& msg)
void ProtocolGame::parsePlayerCancelAttack(const InputMessagePtr& msg)
{
uint seq = 0;
if(g_game.getProtocolVersion() >= 860)
if(g_game.getFeature(Otc::GameAttackSeq))
seq = msg->getU32();
g_game.processAttackCancel(seq);
@ -1234,15 +1291,20 @@ void ProtocolGame::parseWalkWait(const InputMessagePtr& msg)
void ProtocolGame::parseFloorChangeUp(const InputMessagePtr& msg)
{
Position pos = g_map.getCentralPosition();
Position pos;
if(g_game.getFeature(Otc::GameMapMovePosition))
pos = getPosition(msg);
else
pos = g_map.getCentralPosition();
AwareRange range = g_map.getAwareRange();
pos.z--;
int skip = 0;
if(pos.z == Otc::SEA_FLOOR)
for(int i = Otc::SEA_FLOOR - Otc::AWARE_UNDEGROUND_FLOOR_RANGE; i >= 0; i--)
skip = setFloorDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, i, Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES, 8 - i, skip);
skip = setFloorDescription(msg, pos.x - range.left, pos.y - range.top, i, range.horizontal(), range.vertical(), 8 - i, skip);
else if(pos.z > Otc::SEA_FLOOR)
skip = setFloorDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z - Otc::AWARE_UNDEGROUND_FLOOR_RANGE, Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES, 3, skip);
skip = setFloorDescription(msg, pos.x - range.left, pos.y - range.top, pos.z - Otc::AWARE_UNDEGROUND_FLOOR_RANGE, range.horizontal(), range.vertical(), 3, skip);
pos.x++;
pos.y++;
@ -1251,17 +1313,22 @@ void ProtocolGame::parseFloorChangeUp(const InputMessagePtr& msg)
void ProtocolGame::parseFloorChangeDown(const InputMessagePtr& msg)
{
Position pos = g_map.getCentralPosition();
Position pos;
if(g_game.getFeature(Otc::GameMapMovePosition))
pos = getPosition(msg);
else
pos = g_map.getCentralPosition();
AwareRange range = g_map.getAwareRange();
pos.z++;
int skip = 0;
if(pos.z == Otc::UNDERGROUND_FLOOR) {
int j, i;
for(i = pos.z, j = -1; i <= pos.z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE; ++i, --j)
skip = setFloorDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, i, Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES, j, skip);
skip = setFloorDescription(msg, pos.x - range.left, pos.y - range.top, i, range.horizontal(), range.vertical(), j, skip);
}
else if(pos.z > Otc::UNDERGROUND_FLOOR && pos.z < Otc::MAX_Z-1)
skip = setFloorDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE, Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES, -3, skip);
skip = setFloorDescription(msg, pos.x - range.left, pos.y - range.top, pos.z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE, range.horizontal(), range.vertical(), -3, skip);
pos.x--;
pos.y--;
@ -1458,10 +1525,27 @@ void ProtocolGame::parseExtendedOpcode(const InputMessagePtr& msg)
if(opcode == 0)
m_enableSendExtendedOpcode = true;
else if(opcode == 2)
parsePingBack(msg);
else
callLuaField("onExtendedOpcode", opcode, buffer);
}
void ProtocolGame::parseChangeMapAwareRange(const InputMessagePtr& msg)
{
int xrange = msg->getU8();
int yrange = msg->getU8();
AwareRange range;
range.left = xrange/2 - ((xrange+1) % 2);
range.right = xrange/2;
range.top = yrange/2 - ((yrange+1) % 2);
range.bottom = yrange/2;
g_map.setAwareRange(range);
g_lua.callGlobalField("g_game", "onMapChangeAwareRange", xrange, yrange);
}
void ProtocolGame::setMapDescription(const InputMessagePtr& msg, int x, int y, int z, int width, int height)
{
int startz, endz, zstep;
@ -1502,7 +1586,7 @@ int ProtocolGame::setTileDescription(const InputMessagePtr& msg, Position positi
{
g_map.cleanTile(position);
bool gotEffect = 0;
bool gotEffect = false;
for(int stackPos=0;stackPos<256;stackPos++) {
if(msg->peekU16() >= 0xff00)
return msg->getU16() & 0xff;
@ -1558,6 +1642,7 @@ Outfit ProtocolGame::getOutfit(const InputMessagePtr& msg)
int mount = msg->getU16();
outfit.setMount(mount);
}
return outfit;
}
@ -1571,6 +1656,8 @@ ThingPtr ProtocolGame::getThing(const InputMessagePtr& msg)
stdext::throw_exception("invalid thing id");
else if(id == Proto::UnknownCreature || id == Proto::OutdatedCreature || id == Proto::Creature)
thing = getCreature(msg, id);
else if(id == Proto::StaticText) // otclient only
thing = getStaticText(msg, id);
else // item
thing = getItem(msg, id);
@ -1745,6 +1832,19 @@ ItemPtr ProtocolGame::getItem(const InputMessagePtr& msg, int id)
return item;
}
StaticTextPtr ProtocolGame::getStaticText(const InputMessagePtr& msg, int id)
{
int colorByte = msg->getU8();
Color color = Color::from8bit(colorByte);
std::string fontName = msg->getString();
std::string text = msg->getString();
StaticTextPtr staticText = StaticTextPtr(new StaticText);
staticText->setText(text);
staticText->setFont(fontName);
staticText->setColor(color);
return staticText;
}
Position ProtocolGame::getPosition(const InputMessagePtr& msg)
{
uint16 x = msg->getU16();

@ -22,6 +22,8 @@
#include "protocolgame.h"
#include "game.h"
#include "client.h"
#include <framework/core/application.h>
void ProtocolGame::send(const OutputMessagePtr& outputMessage)
{
@ -50,7 +52,16 @@ void ProtocolGame::sendLoginPacket(uint challangeTimestamp, uint8 challangeRando
msg->addU8(Proto::ClientPendingGame);
msg->addU16(g_lua.callGlobalField<int>("g_game", "getOsType"));
msg->addU16(g_game.getOs());
if(g_game.getFeature(Otc::GameUpdater)) {
msg->addString(g_app.getOs());
msg->addString(g_game.getUpdaterSignature());
}
if(g_game.getFeature(Otc::GameLoginLocale))
msg->addString(m_locale);
msg->addU16(g_game.getProtocolVersion());
if(g_game.getProtocolVersion() >= 971) {
@ -92,6 +103,12 @@ void ProtocolGame::sendLoginPacket(uint challangeTimestamp, uint8 challangeRando
paddingBytes -= 5;
}
if(paddingBytes < 0) {
g_game.processLoginError("AccountName or Password or CharacterName are too big!\nPlease contact game support.");
g_game.processDisconnect();
return;
}
// complete the 128 bytes for rsa encryption with zeros
msg->addPaddingBytes(paddingBytes);
@ -119,10 +136,14 @@ void ProtocolGame::sendLogout()
void ProtocolGame::sendPing()
{
OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientPing);
if(g_game.getFeature(Otc::GameExtendedClientPing))
sendExtendedOpcode(2, "");
else {
OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientPing);
Protocol::send(msg);
}
m_pingTimer.restart();
Protocol::send(msg);
}
void ProtocolGame::sendPingBack()
@ -774,6 +795,18 @@ void ProtocolGame::sendAnswerModalDialog(int dialog, int button, int choice)
send(msg);
}
void ProtocolGame::sendChangeMapAwareRange(int xrange, int yrange)
{
if(!g_game.getFeature(Otc::GameChangeMapAwareRange))
return;
OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientChangeMapAwareRange);
msg->addU8(xrange);
msg->addU8(yrange);
send(msg);
}
void ProtocolGame::addPosition(const OutputMessagePtr& msg, const Position& position)
{
msg->addU16(position.x);

@ -24,6 +24,7 @@
#include <framework/graphics/paintershaderprogram.h>
#include <framework/graphics/graphics.h>
#include <framework/graphics/painterogl2_shadersources.h>
#include <framework/core/resourcemanager.h>
ShaderManager g_shaders;
@ -59,12 +60,14 @@ PainterShaderProgramPtr ShaderManager::createShader(const std::string& name)
return shader;
}
PainterShaderProgramPtr ShaderManager::createFragmentShader(const std::string& name, const std::string& file)
PainterShaderProgramPtr ShaderManager::createFragmentShader(const std::string& name, std::string file)
{
PainterShaderProgramPtr shader = createShader(name);
if(!shader)
return nullptr;
file = g_resources.guessFileType(file, "frag");
shader->addShaderFromSourceCode(Shader::Vertex, glslMainWithTexCoordsVertexShader + glslPositionOnlyVertexShader);
if(!shader->addShaderFromSourceFile(Shader::Fragment, file)) {
g_logger.error(stdext::format("unable to load fragment shader '%s' from source file '%s'", name, file));
@ -109,6 +112,14 @@ PainterShaderProgramPtr ShaderManager::createItemShader(const std::string& name,
return shader;
}
PainterShaderProgramPtr ShaderManager::createMapShader(const std::string& name, const std::string& file)
{
PainterShaderProgramPtr shader = createFragmentShader(name, file);
if(shader)
setupMapShader(shader);
return shader;
}
void ShaderManager::setupItemShader(const PainterShaderProgramPtr& shader)
{
if(!shader)
@ -116,6 +127,15 @@ void ShaderManager::setupItemShader(const PainterShaderProgramPtr& shader)
shader->bindUniformLocation(ITEM_ID_UNIFORM, "u_ItemId");
}
void ShaderManager::setupMapShader(const PainterShaderProgramPtr& shader)
{
if(!shader)
return;
shader->bindUniformLocation(MAP_CENTER_COORD, "u_MapCenterCoord");
shader->bindUniformLocation(MAP_GLOBAL_COORD, "u_MapGlobalCoord");
shader->bindUniformLocation(MAP_ZOOM, "u_MapZoom");
}
PainterShaderProgramPtr ShaderManager::getShader(const std::string& name)
{
auto it = m_shaders.find(name);
@ -123,3 +143,4 @@ PainterShaderProgramPtr ShaderManager::getShader(const std::string& name)
return it->second;
return nullptr;
}

@ -31,18 +31,21 @@ class ShaderManager
{
public:
enum {
ITEM_ID_UNIFORM = 10
ITEM_ID_UNIFORM = 10,
MAP_CENTER_COORD = 10,
MAP_GLOBAL_COORD = 11,
MAP_ZOOM = 12
};
void init();
void terminate();
PainterShaderProgramPtr createShader(const std::string& name);
PainterShaderProgramPtr createFragmentShader(const std::string& name, const std::string& file);
PainterShaderProgramPtr createFragmentShader(const std::string& name, std::string file);
PainterShaderProgramPtr createFragmentShaderFromCode(const std::string& name, const std::string& code);
PainterShaderProgramPtr createItemShader(const std::string& name, const std::string& file);
PainterShaderProgramPtr createMapShader(const std::string& name, const std::string& file) { return createFragmentShader(name, file); }
PainterShaderProgramPtr createMapShader(const std::string& name, const std::string& file);
const PainterShaderProgramPtr& getDefaultItemShader() { return m_defaultItemShader; }
const PainterShaderProgramPtr& getDefaultMapShader() { return m_defaultMapShader; }
@ -51,6 +54,7 @@ public:
private:
void setupItemShader(const PainterShaderProgramPtr& shader);
void setupMapShader(const PainterShaderProgramPtr& shader);
PainterShaderProgramPtr m_defaultItemShader;
PainterShaderProgramPtr m_defaultMapShader;
@ -61,3 +65,4 @@ private:
extern ShaderManager g_shaders;
#endif

@ -39,12 +39,14 @@ void SpriteManager::terminate()
unload();
}
bool SpriteManager::loadSpr(const std::string& file)
bool SpriteManager::loadSpr(std::string file)
{
m_spritesCount = 0;
m_signature = 0;
m_loaded = false;
try {
file = g_resources.guessFileType(file, "spr");
m_spritesFile = g_resources.openFile(file);
// cache file buffer to avoid lags from hard drive
m_spritesFile->cache();

@ -34,7 +34,7 @@ public:
void terminate();
bool loadSpr(const std::string& file);
bool loadSpr(std::string file);
void unload();
uint32 getSignature() { return m_signature; }

@ -29,6 +29,8 @@
StaticText::StaticText()
{
m_mode = Otc::MessageNone;
m_color = Color::white;
m_cachedText.setFont(g_fonts.getFont("verdana-11px-rounded"));
m_cachedText.setAlign(Fw::AlignCenter);
}
@ -47,6 +49,16 @@ void StaticText::drawText(const Point& dest, const Rect& parentRect)
//}
}
void StaticText::setFont(const std::string& fontName)
{
m_cachedText.setFont(g_fonts.getFont(fontName));
}
void StaticText::setText(const std::string& text)
{
m_cachedText.setText(text);
}
bool StaticText::addMessage(const std::string& name, Otc::MessageMode mode, const std::string& text)
{
//TODO: this could be moved to lua

@ -41,11 +41,16 @@ public:
bool isYell() { return m_mode == Otc::MessageYell || m_mode == Otc::MessageMonsterYell || m_mode == Otc::MessageBarkLoud; }
void setText(const std::string& text);
void setFont(const std::string& fontName);
bool addMessage(const std::string& name, Otc::MessageMode mode, const std::string& text);
StaticTextPtr asStaticText() { return static_self_cast<StaticText>(); }
bool isStaticText() { return true; }
void setColor(const Color& color) { m_color = color; }
Color getColor() { return m_color; }
private:
void update();
void scheduleUpdate();

@ -30,6 +30,7 @@
#include <framework/graphics/image.h>
#include <framework/graphics/texturemanager.h>
#include <framework/core/filestream.h>
#include <framework/otml/otml.h>
ThingType::ThingType()
{
@ -41,6 +42,7 @@ ThingType::ThingType()
m_animationPhases = 0;
m_layers = 0;
m_elevation = 0;
m_opacity = 1.0f;
}
void ThingType::unserialize(uint16 clientId, ThingCategory category, const FileStreamPtr& fin)
@ -139,6 +141,16 @@ void ThingType::unserialize(uint16 clientId, ThingCategory category, const FileS
m_texturesFramesOffsets.resize(m_animationPhases);
}
void ThingType::unserializeOtml(const OTMLNodePtr& node)
{
for(const OTMLNodePtr& node2 : node->children()) {
if(node2->tag() == "opacity")
m_opacity = node2->value<float>();
if(node2->tag() == "notprewalkable")
m_attribs.set(ThingAttrNotPreWalkable, node2->value<bool>());
}
}
void ThingType::draw(const Point& dest, float scaleFactor, int layer, int xPattern, int yPattern, int zPattern, int animationPhase, LightView *lightView)
{
if(m_null)
@ -160,6 +172,7 @@ void ThingType::draw(const Point& dest, float scaleFactor, int layer, int xPatte
Rect screenRect(dest + (textureOffset - m_displacement - (m_size.toPoint() - Point(1, 1)) * 32) * scaleFactor,
textureRect.size() * scaleFactor);
g_painter->setOpacity(m_opacity);
g_painter->drawTexturedRect(screenRect, texture, textureRect);
if(lightView && hasLight()) {

@ -26,6 +26,7 @@
#include "declarations.h"
#include <framework/core/declarations.h>
#include <framework/otml/declarations.h>
#include <framework/graphics/texture.h>
#include <framework/graphics/coordsbuffer.h>
#include <framework/luaengine/luaobject.h>
@ -75,6 +76,11 @@ enum ThingAttr : uint8 {
ThingAttrLook = 31,
ThingAttrCloth = 32,
ThingAttrMarket = 33,
// additional
ThingAttrOpacity = 100,
ThingAttrNotPreWalkable = 101,
ThingAttrChargeable = 254, // deprecated
ThingLastAttr = 255
};
@ -107,6 +113,7 @@ public:
ThingType();
void unserialize(uint16 clientId, ThingCategory category, const FileStreamPtr& fin);
void unserializeOtml(const OTMLNodePtr& node);
void draw(const Point& dest, float scaleFactor, int layer, int xPattern, int yPattern, int zPattern, int animationPhase, LightView *lightView = nullptr);
@ -172,6 +179,10 @@ public:
bool isCloth() { return m_attribs.has(ThingAttrCloth); }
bool isMarketable() { return m_attribs.has(ThingAttrMarket); }
// additional
float getOpacity() { return m_opacity; }
bool isNotPreWalkable() { return m_attribs.has(ThingAttrNotPreWalkable); }
private:
const TexturePtr& getTexture(int animationPhase);
Size getBestTextureDimension(int w, int h, int count);
@ -190,6 +201,7 @@ private:
int m_animationPhases;
int m_layers;
int m_elevation;
float m_opacity;
std::vector<int> m_spritesIndex;
std::vector<TexturePtr> m_textures;

@ -32,6 +32,7 @@
#include <framework/core/filestream.h>
#include <framework/core/binarytree.h>
#include <framework/xml/tinyxml.h>
#include <framework/otml/otml.h>
ThingTypeManager g_things;
@ -60,11 +61,13 @@ void ThingTypeManager::terminate()
m_nullItemType = nullptr;
}
bool ThingTypeManager::loadDat(const std::string& file)
bool ThingTypeManager::loadDat(std::string file)
{
m_datLoaded = false;
m_datSignature = 0;
try {
file = g_resources.guessFileType(file, "dat");
FileStreamPtr fin = g_resources.openFile(file);
m_datSignature = fin->getU32();
@ -95,6 +98,41 @@ bool ThingTypeManager::loadDat(const std::string& file)
}
}
bool ThingTypeManager::loadOtml(std::string file)
{
try {
file = g_resources.guessFileType(file, "otml");
OTMLDocumentPtr doc = OTMLDocument::parse(file);
for(const OTMLNodePtr& node : doc->children()) {
ThingCategory category;
if(node->tag() == "creatures")
category = ThingCategoryCreature;
else if(node->tag() == "items")
category = ThingCategoryItem;
else if(node->tag() == "effects")
category = ThingCategoryEffect;
else if(node->tag() == "missiles")
category = ThingCategoryMissile;
else {
throw OTMLException(node, "not a valid thing category");
}
for(const OTMLNodePtr& node2 : node->children()) {
uint16 id = stdext::safe_cast<uint16>(node2->tag());
ThingTypePtr type = getThingType(id, category);
if(!type)
throw OTMLException(node2, "thing not found");
type->unserializeOtml(node2);
}
}
return true;
} catch(std::exception& e) {
g_logger.error(stdext::format("Failed to read dat otml '%s': %s'", file, e.what()));
return false;
}
}
void ThingTypeManager::loadOtb(const std::string& file)
{
FileStreamPtr fin = g_resources.openFile(file);

@ -35,7 +35,8 @@ public:
void init();
void terminate();
bool loadDat(const std::string& file);
bool loadDat(std::string file);
bool loadOtml(std::string file);
void loadOtb(const std::string& file);
void loadXml(const std::string& file);
void parseItemType(uint16 id, TiXmlElement *elem);

@ -278,14 +278,13 @@ bool Tile::hasThing(const ThingPtr& thing)
int Tile::getThingStackpos(const ThingPtr& thing)
{
for(uint stackpos = 0; stackpos < m_things.size(); ++stackpos) {
for(uint stackpos = 0; stackpos < m_things.size(); ++stackpos)
if(thing == m_things[stackpos])
return stackpos;
}
return -1;
}
ThingPtr Tile:: getTopThing()
ThingPtr Tile::getTopThing()
{
if(isEmpty())
return nullptr;
@ -374,6 +373,12 @@ ThingPtr Tile::getTopUseThing()
return thing;
}
for(uint i = 0; i < m_things.size(); ++i) {
ThingPtr thing = m_things[i];
if(!thing->isGround() && !thing->isGroundBorder() && !thing->isCreature())
return thing;
}
return m_things[0];
}
@ -434,30 +439,34 @@ ThingPtr Tile::getTopMoveThing()
return m_things[0];
}
ThingPtr Tile::getTopMultiUseThing(bool ignoreCreature)
ThingPtr Tile::getTopMultiUseThing()
{
// this is related to classic controls, getting top item, forceuse for creature
if(isEmpty())
return nullptr;
for(uint i = 0; i < m_things.size(); ++i) {
ThingPtr thing = m_things[i];
if(thing->isForceUse() || (!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop())) {
if(thing->isCreature() && ignoreCreature)
continue;
if(thing->isForceUse())
return thing;
}
if(CreaturePtr topCreature = getTopCreature())
return topCreature;
for(uint i = 0; i < m_things.size(); ++i) {
ThingPtr thing = m_things[i];
if(!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop()) {
if(i > 0 && thing->isSplash())
return m_things[i-1];
return thing;
}
}
for(uint i = 0; i < m_things.size(); ++i) {
ThingPtr thing = m_things[i];
if(!thing->isGround() && !thing->isGroundBorder() && !thing->isOnTop()) {
if(thing->isCreature() && ignoreCreature)
continue;
if(!thing->isGround() && !thing->isGroundBorder() && !thing->isOnTop())
return thing;
}
}
return m_things[0];

@ -81,7 +81,7 @@ public:
ThingPtr getTopUseThing();
CreaturePtr getTopCreature();
ThingPtr getTopMoveThing();
ThingPtr getTopMultiUseThing(bool ignoreCreature = true);
ThingPtr getTopMultiUseThing();
const Position& getPosition() { return m_position; }
int getDrawElevation() { return m_drawElevation; }

@ -55,7 +55,7 @@ public:
void setDrawLights(bool enable) { m_mapView->setDrawLights(enable); }
void setAnimated(bool enable) { m_mapView->setAnimated(enable); }
void setKeepAspectRatio(bool enable);
void setMapShader(const PainterShaderProgramPtr& shader) { m_mapView->setShader(shader); }
void setMapShader(const PainterShaderProgramPtr& shader, float fadeout, float fadein) { m_mapView->setShader(shader, fadein, fadeout); }
void setMinimumAmbientLight(float intensity) { m_mapView->setMinimumAmbientLight(intensity); }
bool isMultifloor() { return m_mapView->isMultifloor(); }

@ -0,0 +1,21 @@
# Try to find the ICU library
# ICU_FOUND - system has ICU
# ICU_INCLUDE_DIR - the ICU include directory
# ICU_LIBRARY - the ICU library
FIND_PATH(ICU_INCLUDE_DIR NAMES unicode/utf8.h)
SET(_ICUI18N_STATIC_LIBS libicui18n.a)
SET(_ICUI18N_SHARED_LIBS libicui18n.dll.a icui18n)
SET(_ICUUC_STATIC_LIBS libicuuc.a)
SET(_ICUUC_SHARED_LIBS libicuuc.dll.a icuuc)
IF(USE_STATIC_LIBS)
FIND_LIBRARY(ICUI18N_LIBRARY NAMES ${_ICUI18N_STATIC_LIBS} ${_ICUI18N_SHARED_LIBS})
FIND_LIBRARY(ICUUC_LIBRARY NAMES ${_ICUUC_STATIC_LIBS} ${_ICUUC_SHARED_LIBS})
ELSE()
FIND_LIBRARY(ICUI18N_LIBRARY NAMES ${_ICUI18N_SHARED_LIBS} ${_ICUI18N_STATIC_LIBS})
FIND_LIBRARY(ICUUC_LIBRARY NAMES ${_ICUUC_SHARED_LIBS} ${_ICUUC_STATIC_LIBS})
ENDIF()
SET(ICU_LIBRARIES ${ICUI18N_LIBRARY} ${ICUUC_LIBRARY})
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(ICU DEFAULT_MSG ICU_LIBRARY ICU_INCLUDE_DIR)
MARK_AS_ADVANCED(ICU_LIBRARIES ICU_INCLUDE_DIR)

@ -32,6 +32,7 @@
#include <framework/core/resourcemanager.h>
#include <framework/graphics/texturemanager.h>
#include <framework/stdext/net.h>
#include <framework/platform/platform.h>
#ifdef FW_GRAPHICS
#include <framework/graphics/graphics.h>
@ -68,6 +69,17 @@ void Application::registerLuaFunctions()
g_lua.bindGlobalFunction("listSubnetAddresses", [](uint32 a, uint8 b) { return stdext::listSubnetAddresses(a, b); });
g_lua.bindGlobalFunction("ucwords", [](std::string s) { return stdext::ucwords(s); });
// Platform
g_lua.registerSingletonClass("g_platform");
g_lua.bindSingletonFunction("g_platform", "spawnProcess", &Platform::spawnProcess, &g_platform);
g_lua.bindSingletonFunction("g_platform", "getProcessId", &Platform::getProcessId, &g_platform);
g_lua.bindSingletonFunction("g_platform", "copyFile", &Platform::copyFile, &g_platform);
g_lua.bindSingletonFunction("g_platform", "getTempPath", &Platform::getTempPath, &g_platform);
g_lua.bindSingletonFunction("g_platform", "openUrl", &Platform::openUrl, &g_platform);
g_lua.bindSingletonFunction("g_platform", "getCPUName", &Platform::getCPUName, &g_platform);
g_lua.bindSingletonFunction("g_platform", "getTotalSystemMemory", &Platform::getTotalSystemMemory, &g_platform);
g_lua.bindSingletonFunction("g_platform", "getOSName", &Platform::getOSName, &g_platform);
// Application
g_lua.registerSingletonClass("g_app");
g_lua.bindSingletonFunction("g_app", "setName", &Application::setName, static_cast<Application*>(&g_app));
@ -85,6 +97,7 @@ void Application::registerLuaFunctions()
g_lua.bindSingletonFunction("g_app", "getBuildType", &Application::getBuildType, static_cast<Application*>(&g_app));
g_lua.bindSingletonFunction("g_app", "getBuildArch", &Application::getBuildArch, static_cast<Application*>(&g_app));
g_lua.bindSingletonFunction("g_app", "getOs", &Application::getOs, static_cast<Application*>(&g_app));
g_lua.bindSingletonFunction("g_app", "getStartupOptions", &Application::getStartupOptions, static_cast<Application*>(&g_app));
g_lua.bindSingletonFunction("g_app", "exit", &Application::exit, static_cast<Application*>(&g_app));
// Crypt
@ -160,6 +173,7 @@ void Application::registerLuaFunctions()
g_lua.bindSingletonFunction("g_resources", "getWorkDir", &ResourceManager::getWorkDir, &g_resources);
g_lua.bindSingletonFunction("g_resources", "getSearchPaths", &ResourceManager::getSearchPaths, &g_resources);
g_lua.bindSingletonFunction("g_resources", "listDirectoryFiles", &ResourceManager::listDirectoryFiles, &g_resources);
g_lua.bindSingletonFunction("g_resources", "readFileContents", &ResourceManager::readFileContents, &g_resources);
// Module
g_lua.registerClass<Module>();
@ -212,6 +226,7 @@ void Application::registerLuaFunctions()
g_lua.bindSingletonFunction("g_window", "resize", &PlatformWindow::resize, &g_window);
g_lua.bindSingletonFunction("g_window", "show", &PlatformWindow::show, &g_window);
g_lua.bindSingletonFunction("g_window", "hide", &PlatformWindow::hide, &g_window);
g_lua.bindSingletonFunction("g_window", "poll", &PlatformWindow::poll, &g_window);
g_lua.bindSingletonFunction("g_window", "maximize", &PlatformWindow::maximize, &g_window);
g_lua.bindSingletonFunction("g_window", "restoreMouseCursor", &PlatformWindow::restoreMouseCursor, &g_window);
g_lua.bindSingletonFunction("g_window", "showMouse", &PlatformWindow::showMouse, &g_window);
@ -261,6 +276,8 @@ void Application::registerLuaFunctions()
g_lua.bindSingletonFunction("g_graphics", "selectPainterEngine", &Graphics::selectPainterEngine, &g_graphics);
g_lua.bindSingletonFunction("g_graphics", "canCacheBackbuffer", &Graphics::canCacheBackbuffer, &g_graphics);
g_lua.bindSingletonFunction("g_graphics", "canUseShaders", &Graphics::canUseShaders, &g_graphics);
g_lua.bindSingletonFunction("g_graphics", "shouldUseShaders", &Graphics::shouldUseShaders, &g_graphics);
g_lua.bindSingletonFunction("g_graphics", "setShouldUseShaders", &Graphics::setShouldUseShaders, &g_graphics);
g_lua.bindSingletonFunction("g_graphics", "getPainterEngine", &Graphics::getPainterEngine, &g_graphics);
g_lua.bindSingletonFunction("g_graphics", "getViewportSize", &Graphics::getViewportSize, &g_graphics);
g_lua.bindSingletonFunction("g_graphics", "getVendor", &Graphics::getVendor, &g_graphics);
@ -607,6 +624,7 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<UIGridLayout>("getNumColumns", &UIGridLayout::getNumColumns);
g_lua.bindClassMemberFunction<UIGridLayout>("getNumLines", &UIGridLayout::getNumLines);
g_lua.bindClassMemberFunction<UIGridLayout>("getCellSize", &UIGridLayout::getCellSize);
g_lua.bindClassMemberFunction<UIGridLayout>("getCellSpacing", &UIGridLayout::getCellSpacing);
g_lua.bindClassMemberFunction<UIGridLayout>("isUIGridLayout", &UIGridLayout::isUIGridLayout);
// UIAnchorLayout
@ -756,8 +774,10 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<DBResult>("getDataInt", &DBResult::getDataInt);
g_lua.bindClassMemberFunction<DBResult>("getDataLong", &DBResult::getDataLong);
g_lua.bindClassMemberFunction<DBResult>("getDataString", &DBResult::getDataString);
g_lua.bindClassMemberFunction<DBResult>("getRowCount", &DBResult::getRowCount);
g_lua.bindClassMemberFunction<DBResult>("next", &DBResult::next);
// Mysql
g_lua.registerClass<DatabaseMySQL, Database>();
g_lua.bindClassStaticFunction<DatabaseMySQL>("create", []{ return DatabaseMySQLPtr(new DatabaseMySQL); });

@ -57,4 +57,9 @@ float random_range(float min, float max)
return min + (max - min)*dis(gen);
}
double round(double r)
{
return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5);
}
}

@ -43,6 +43,8 @@ uint32_t adler32(const uint8_t *buffer, size_t size);
long random_range(long min, long max);
float random_range(float min, float max);
double round(double r);
}
#endif

Loading…
Cancel
Save