1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-07-13 01:20:34 +02:00

Hero movement speed and map scrolling speed now matches H3

This commit is contained in:
Ivan Savenko
2023-02-19 00:36:40 +02:00
parent 7cf00ba87d
commit ebe996fa74
10 changed files with 87 additions and 56 deletions

View File

@ -442,6 +442,20 @@ namespace vstd
} }
} }
// c++17: makes a to fit the range <b, c>
template <typename t1, typename t2, typename t3>
t1 clamp(const t1 &value, const t2 &low, const t3 &high)
{
if ( value > high)
return high;
if ( value < low)
return low;
return value;
}
//makes a to fit the range <b, c> //makes a to fit the range <b, c>
template <typename t1, typename t2, typename t3> template <typename t1, typename t2, typename t3>
t1 &abetween(t1 &a, const t2 &b, const t3 &c) t1 &abetween(t1 &a, const t2 &b, const t3 &c)

View File

@ -592,20 +592,22 @@ void CAdvMapInt::show(SDL_Surface * to)
void CAdvMapInt::handleMapScrollingUpdate() void CAdvMapInt::handleMapScrollingUpdate()
{ {
int scrollSpeed = static_cast<int>(settings["adventure"]["scrollSpeed"].Float()); uint32_t timePassed = GH.mainFPSmng->getElapsedMilliseconds();
double scrollSpeedPixels = settings["adventure"]["scrollSpeedPixels"].Float();
int32_t scrollDistance = static_cast<int32_t>(scrollSpeedPixels * timePassed / 1000);
//if advmap needs updating AND (no dialog is shown OR ctrl is pressed) //if advmap needs updating AND (no dialog is shown OR ctrl is pressed)
if(scrollingDir & LEFT) if(scrollingDir & LEFT)
terrain->moveViewBy(Point(-scrollSpeed, 0)); terrain->moveViewBy(Point(-scrollDistance, 0));
if(scrollingDir & RIGHT) if(scrollingDir & RIGHT)
terrain->moveViewBy(Point(+scrollSpeed, 0)); terrain->moveViewBy(Point(+scrollDistance, 0));
if(scrollingDir & UP) if(scrollingDir & UP)
terrain->moveViewBy(Point(0, -scrollSpeed)); terrain->moveViewBy(Point(0, -scrollDistance));
if(scrollingDir & DOWN) if(scrollingDir & DOWN)
terrain->moveViewBy(Point(0, +scrollSpeed)); terrain->moveViewBy(Point(0, +scrollDistance));
if(scrollingDir) if(scrollingDir)
{ {

View File

@ -396,8 +396,7 @@ std::shared_ptr<IImage> MapRendererObjects::getImage(const IMapRendererContext &
if(animation->size(groupIndex) == 0) if(animation->size(groupIndex) == 0)
return nullptr; return nullptr;
size_t frameCounter = context.getAnimationTime() / context.getAnimationPeriod(); size_t frameIndex = context.objectImageIndex(obj->id, animation->size(groupIndex));
size_t frameIndex = frameCounter % animation->size(groupIndex);
return animation->getImage(frameIndex, groupIndex); return animation->getImage(frameIndex, groupIndex);
} }

View File

@ -58,11 +58,8 @@ public:
/// returns object animation transparency. IF set to 0, object will not be visible /// returns object animation transparency. IF set to 0, object will not be visible
virtual double objectTransparency(ObjectInstanceID objectID) const = 0; virtual double objectTransparency(ObjectInstanceID objectID) const = 0;
/// returns how long should each frame of animation be visible, in milliseconds /// returns animation frame for selected object
virtual uint32_t getAnimationPeriod() const = 0; virtual size_t objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const = 0;
/// returns total animation time since creation of this context
virtual uint32_t getAnimationTime() const = 0;
/// returns size of ouput tile, in pixels. 32x32 for "standard" map, may be smaller for world view mode /// returns size of ouput tile, in pixels. 32x32 for "standard" map, may be smaller for world view mode
virtual Point getTileSize() const = 0; virtual Point getTileSize() const = 0;

View File

@ -166,19 +166,24 @@ const CGPath * MapRendererContext::currentPath() const
return &LOCPLINT->paths.getPath(hero); return &LOCPLINT->paths.getPath(hero);
} }
uint32_t MapRendererContext::getAnimationPeriod() const size_t MapRendererContext::objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const
{ {
assert(groupSize > 0);
if (groupSize == 0)
return 0;
// H3 timing for adventure map objects animation is 180 ms // H3 timing for adventure map objects animation is 180 ms
// Terrain animations also use identical interval, however those are only present in HotA and/or HD Mod // Terrain animations also use identical interval, however those are only present in HotA and/or HD Mod
// TODO: duration of fade-in/fade-out for teleport, entering/leaving boat, removal of objects size_t baseFrameTime = 180;
// TOOD: duration of hero movement animation, frame timing of hero movement animation, effect of hero speed option
// TOOD: duration of enemy hero movement animation, frame timing of enemy hero movement animation, effect of enemy hero speed option
return 180;
}
uint32_t MapRendererContext::getAnimationTime() const // hero movement animation always plays at ~50ms / frame
{ // in-game setting only affect movement across screen
return animationTime; if (movementAnimation && movementAnimation->target == objectID)
baseFrameTime = 50;
size_t frameCounter = animationTime / baseFrameTime;
size_t frameIndex = frameCounter % groupSize;
return frameIndex;
} }
Point MapRendererContext::getTileSize() const Point MapRendererContext::getTileSize() const
@ -193,14 +198,19 @@ bool MapRendererContext::showGrid() const
void MapViewController::setViewCenter(const int3 & position) void MapViewController::setViewCenter(const int3 & position)
{ {
model->setViewCenter(Point(position.x, position.y) * model->getSingleTileSize()); assert(context->isInMap(position));
model->setLevel(position.z); setViewCenter(Point(position) * model->getSingleTileSize(), position.z);
} }
void MapViewController::setViewCenter(const Point & position, int level) void MapViewController::setViewCenter(const Point & position, int level)
{ {
model->setViewCenter(position); Point betterPosition = {
model->setLevel(level); vstd::clamp(position.x, 0, context->getMapSize().x * model->getSingleTileSize().x),
vstd::clamp(position.y, 0, context->getMapSize().y * model->getSingleTileSize().y)
};
model->setViewCenter(betterPosition);
model->setLevel(vstd::clamp(level, 0, context->getMapSize().z));
} }
void MapViewController::setTileSize(const Point & tileSize) void MapViewController::setTileSize(const Point & tileSize)
@ -452,14 +462,16 @@ void MapViewController::update(uint32_t timeDelta)
{ {
static const double fadeOutDuration = 1.0; static const double fadeOutDuration = 1.0;
static const double fadeInDuration = 1.0; static const double fadeInDuration = 1.0;
static const double heroMoveDuration = 1.0;
static const double heroTeleportDuration = 1.0; static const double heroTeleportDuration = 1.0;
//FIXME: remove code duplication? //FIXME: remove code duplication?
if (context->movementAnimation) if (context->movementAnimation)
{ {
context->movementAnimation->progress += heroMoveDuration * timeDelta / 1000; // TODO: enemyMoveTime
double heroMoveTime = settings["adventure"]["heroMoveTime"].Float();
context->movementAnimation->progress += timeDelta / heroMoveTime;
Point positionFrom = Point(context->movementAnimation->tileFrom) * model->getSingleTileSize(); Point positionFrom = Point(context->movementAnimation->tileFrom) * model->getSingleTileSize();
Point positionDest = Point(context->movementAnimation->tileDest) * model->getSingleTileSize(); Point positionDest = Point(context->movementAnimation->tileDest) * model->getSingleTileSize();
@ -503,7 +515,7 @@ void MapViewController::update(uint32_t timeDelta)
} }
context->animationTime += timeDelta; context->animationTime += timeDelta;
context->tileSize =model->getSingleTileSize(); context->tileSize = model->getSingleTileSize();
} }
void MapViewController::onObjectFadeIn(const CGObjectInstance * obj) void MapViewController::onObjectFadeIn(const CGObjectInstance * obj)
@ -539,8 +551,17 @@ void MapViewController::onHeroMoved(const CGHeroInstance * obj, const int3 & fro
{ {
assert(!context->movementAnimation); assert(!context->movementAnimation);
context->removeObject(obj); context->removeObject(obj);
context->addMovingObject(obj, from, dest);
context->movementAnimation = HeroAnimationState{ obj->id, from, dest, 0.0 }; if (settings["adventure"]["heroMoveTime"].Float() > 1)
{
context->addMovingObject(obj, from, dest);
context->movementAnimation = HeroAnimationState{ obj->id, from, dest, 0.0 };
}
else
{
// instant movement
context->addObject(obj);
}
} }
void MapViewController::onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest) void MapViewController::onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest)

View File

@ -65,8 +65,7 @@ public:
size_t objectGroupIndex(ObjectInstanceID objectID) const override; size_t objectGroupIndex(ObjectInstanceID objectID) const override;
Point objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const override; Point objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const override;
double objectTransparency(ObjectInstanceID objectID) const override; double objectTransparency(ObjectInstanceID objectID) const override;
uint32_t getAnimationPeriod() const override; size_t objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const override;
uint32_t getAnimationTime() const override;
Point getTileSize() const override; Point getTileSize() const override;
bool showGrid() const override; bool showGrid() const override;
}; };

View File

@ -424,7 +424,6 @@ CLevelWindow::~CLevelWindow()
LOCPLINT->showingDialog->setn(false); LOCPLINT->showingDialog->setn(false);
} }
CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj) CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj)
: CStatusbarWindow(PLAYER_COLORED, "TPTAVERN"), : CStatusbarWindow(PLAYER_COLORED, "TPTAVERN"),
tavernObj(TavernObj) tavernObj(TavernObj)

View File

@ -37,15 +37,15 @@ AdventureOptionsTab::AdventureOptionsTab()
const JsonNode config(ResourceID("config/widgets/settings/adventureOptionsTab.json")); const JsonNode config(ResourceID("config/widgets/settings/adventureOptionsTab.json"));
addCallback("playerHeroSpeedChanged", [](int value) addCallback("playerHeroSpeedChanged", [](int value)
{ {
return setIntSetting("adventure", "heroSpeed", value); return setIntSetting("adventure", "heroMoveTime", value);
}); });
addCallback("enemyHeroSpeedChanged", [](int value) addCallback("enemyHeroSpeedChanged", [](int value)
{ {
return setIntSetting("adventure", "enemySpeed", value); return setIntSetting("adventure", "enemyMoveTime", value);
}); });
addCallback("mapScrollSpeedChanged", [](int value) addCallback("mapScrollSpeedChanged", [](int value)
{ {
return setIntSetting("adventure", "scrollSpeed", value); return setIntSetting("adventure", "scrollSpeedPixels", value);
}); });
addCallback("heroReminderChanged", [](bool value) addCallback("heroReminderChanged", [](bool value)
{ {
@ -93,4 +93,4 @@ AdventureOptionsTab::AdventureOptionsTab()
std::shared_ptr<CToggleButton> showGridCheckbox = widget<CToggleButton>("showGridCheckbox"); std::shared_ptr<CToggleButton> showGridCheckbox = widget<CToggleButton>("showGridCheckbox");
showGridCheckbox->setSelected(settings["gameTweaks"]["showGrid"].Bool()); showGridCheckbox->setSelected(settings["gameTweaks"]["showGrid"].Bool());
} }

View File

@ -161,19 +161,19 @@
"type" : "object", "type" : "object",
"additionalProperties" : false, "additionalProperties" : false,
"default": {}, "default": {},
"required" : [ "heroSpeed", "enemySpeed", "scrollSpeed", "heroReminder", "quickCombat" ], "required" : [ "heroMoveTime", "enemyMoveTime", "scrollSpeedPixels", "heroReminder", "quickCombat" ],
"properties" : { "properties" : {
"heroSpeed" : { "heroMoveTime" : {
"type" : "number", "type" : "number",
"default" : 2 "default" : 150
}, },
"enemySpeed" : { "enemyMoveTime" : {
"type" : "number", "type" : "number",
"default" : 2 "default" : 150
}, },
"scrollSpeed" : { "scrollSpeedPixels" : {
"type" : "number", "type" : "number",
"default" : 1 "default" : 800
}, },
"heroReminder" : { "heroReminder" : {
"type" : "boolean", "type" : "boolean",

View File

@ -31,7 +31,7 @@
"items": "items":
[ [
{ {
"index": 1, "index": 200,
"type": "toggleButton", "type": "toggleButton",
"image": "sysopb1", "image": "sysopb1",
"help": "core.help.349", "help": "core.help.349",
@ -39,7 +39,7 @@
}, },
{ {
"index": 2, "index": 150,
"type": "toggleButton", "type": "toggleButton",
"image": "sysopb2", "image": "sysopb2",
"help": "core.help.350", "help": "core.help.350",
@ -47,7 +47,7 @@
}, },
{ {
"index": 4, "index": 100,
"type": "toggleButton", "type": "toggleButton",
"image": "sysopb3", "image": "sysopb3",
"help": "core.help.351", "help": "core.help.351",
@ -55,7 +55,7 @@
}, },
{ {
"index": 16, "index": 0,
"type": "toggleButton", "type": "toggleButton",
"image": "sysopb4", "image": "sysopb4",
"help": "core.help.352", "help": "core.help.352",
@ -80,7 +80,7 @@
"items": "items":
[ [
{ {
"index": 2, "index": 150,
"type": "toggleButton", "type": "toggleButton",
"image": "sysopb5", "image": "sysopb5",
"help": "core.help.353", "help": "core.help.353",
@ -88,7 +88,7 @@
}, },
{ {
"index": 4, "index": 100,
"type": "toggleButton", "type": "toggleButton",
"image": "sysopb6", "image": "sysopb6",
"help": "core.help.354", "help": "core.help.354",
@ -96,7 +96,7 @@
}, },
{ {
"index": 8, "index": 0,
"type": "toggleButton", "type": "toggleButton",
"image": "sysopb7", "image": "sysopb7",
"help": "core.help.355", "help": "core.help.355",
@ -104,7 +104,7 @@
}, },
{ {
"index": 0, "index": -1,
"type": "toggleButton", "type": "toggleButton",
"image": "sysopb8", "image": "sysopb8",
"help": "core.help.356", "help": "core.help.356",
@ -121,7 +121,7 @@
"items": "items":
[ [
{ {
"index": 1, "index": 400,
"type": "toggleButton", "type": "toggleButton",
"image": "sysopb9", "image": "sysopb9",
"help": "core.help.357", "help": "core.help.357",
@ -129,7 +129,7 @@
}, },
{ {
"index": 2, "index": 800,
"type": "toggleButton", "type": "toggleButton",
"image": "sysob10", "image": "sysob10",
"help": "core.help.358", "help": "core.help.358",
@ -137,7 +137,7 @@
}, },
{ {
"index": 4, "index": 1200,
"type": "toggleButton", "type": "toggleButton",
"image": "sysob11", "image": "sysob11",
"help": "core.help.359", "help": "core.help.359",
@ -264,4 +264,4 @@
"callback": "showGridChanged" "callback": "showGridChanged"
} }
] ]
} }