Support for changing the game's timescale

Context-specific (like per-world) timescales can also be added later
This commit is contained in:
Kae 2023-07-21 00:58:49 +10:00
parent 607be74945
commit 4b0bc220e4
187 changed files with 626 additions and 571 deletions

View File

@ -172,6 +172,8 @@ Mixer::Mixer(unsigned sampleRate, unsigned channels) {
m_groupVolumes[MixerGroup::Effects] = {1.0f, 1.0f, 0}; m_groupVolumes[MixerGroup::Effects] = {1.0f, 1.0f, 0};
m_groupVolumes[MixerGroup::Music] = {1.0f, 1.0f, 0}; m_groupVolumes[MixerGroup::Music] = {1.0f, 1.0f, 0};
m_groupVolumes[MixerGroup::Cinematic] = {1.0f, 1.0f, 0}; m_groupVolumes[MixerGroup::Cinematic] = {1.0f, 1.0f, 0};
m_speed = 1.0f;
} }
unsigned Mixer::sampleRate() const { unsigned Mixer::sampleRate() const {
@ -203,6 +205,10 @@ bool Mixer::hasEffect(String const& effectName) {
return m_effects.contains(effectName); return m_effects.contains(effectName);
} }
void Mixer::setSpeed(float speed) {
m_speed = speed;
}
void Mixer::setVolume(float volume, float rampTime) { void Mixer::setVolume(float volume, float rampTime) {
MutexLocker locker(m_mutex); MutexLocker locker(m_mutex);
m_volume.target = volume; m_volume.target = volume;
@ -259,6 +265,8 @@ void Mixer::read(int16_t* outBuffer, size_t frameCount, ExtraMixFunction extraMi
for (size_t i = 0; i < bufferSize; ++i) for (size_t i = 0; i < bufferSize; ++i)
outBuffer[i] = 0; outBuffer[i] = 0;
float speed = m_speed;
{ {
MutexLocker locker(m_queueMutex); MutexLocker locker(m_queueMutex);
// Mix all active sounds // Mix all active sounds
@ -288,6 +296,9 @@ void Mixer::read(int16_t* outBuffer, size_t frameCount, ExtraMixFunction extraMi
? approach(audioInstance->m_pitchMultiplierTarget, audioInstance->m_pitchMultiplier, audioInstance->m_pitchMultiplierVelocity * time) ? approach(audioInstance->m_pitchMultiplierTarget, audioInstance->m_pitchMultiplier, audioInstance->m_pitchMultiplierVelocity * time)
: audioInstance->m_pitchMultiplier; : audioInstance->m_pitchMultiplier;
if (audioInstance->m_mixerGroup == MixerGroup::Effects)
pitchMultiplier *= speed;
if (audioStopVolEnd == 0.0f && audioInstance->m_stopping) if (audioStopVolEnd == 0.0f && audioInstance->m_stopping)
finished = true; finished = true;
@ -461,7 +472,7 @@ void Mixer::setGroupVolume(MixerGroup group, float targetValue, float rampTime)
} }
} }
void Mixer::update(PositionalAttenuationFunction positionalAttenuationFunction) { void Mixer::update(float dt, PositionalAttenuationFunction positionalAttenuationFunction) {
{ {
MutexLocker locker(m_queueMutex); MutexLocker locker(m_queueMutex);
eraseWhere(m_audios, [&](auto& p) { eraseWhere(m_audios, [&](auto& p) {

View File

@ -115,6 +115,9 @@ public:
StringList currentEffects(); StringList currentEffects();
bool hasEffect(String const& effectName); bool hasEffect(String const& effectName);
// Global speed
void setSpeed(float speed);
// Global volume // Global volume
void setVolume(float volume, float rampTime); void setVolume(float volume, float rampTime);
@ -131,7 +134,7 @@ public:
// Call within the main loop of the program using Mixer, calculates // Call within the main loop of the program using Mixer, calculates
// positional attenuation of audio and does cleanup. // positional attenuation of audio and does cleanup.
void update(PositionalAttenuationFunction positionalAttenuationFunction = {}); void update(float dt, PositionalAttenuationFunction positionalAttenuationFunction = {});
private: private:
struct EffectInfo { struct EffectInfo {
@ -161,6 +164,7 @@ private:
List<int16_t> m_mixBuffer; List<int16_t> m_mixBuffer;
Map<MixerGroup, RampedValue> m_groupVolumes; Map<MixerGroup, RampedValue> m_groupVolumes;
atomic<float> m_speed;
}; };
} }

View File

@ -346,6 +346,7 @@ void ClientApplication::processInput(InputEvent const& event) {
} }
void ClientApplication::update() { void ClientApplication::update() {
float dt = WorldTimestep * GlobalTimescale;
if (m_state >= MainAppState::Title) { if (m_state >= MainAppState::Title) {
if (auto p2pNetworkingService = appController()->p2pNetworkingService()) { if (auto p2pNetworkingService = appController()->p2pNetworkingService()) {
if (auto join = p2pNetworkingService->pullPendingJoin()) { if (auto join = p2pNetworkingService->pullPendingJoin()) {
@ -361,21 +362,21 @@ void ClientApplication::update() {
} }
if (!m_errorScreen->accepted()) if (!m_errorScreen->accepted())
m_errorScreen->update(); m_errorScreen->update(dt);
if (m_state == MainAppState::Mods) if (m_state == MainAppState::Mods)
updateMods(); updateMods(dt);
else if (m_state == MainAppState::ModsWarning) else if (m_state == MainAppState::ModsWarning)
updateModsWarning(); updateModsWarning(dt);
if (m_state == MainAppState::Splash) if (m_state == MainAppState::Splash)
updateSplash(); updateSplash(dt);
else if (m_state == MainAppState::Error) else if (m_state == MainAppState::Error)
updateError(); updateError(dt);
else if (m_state == MainAppState::Title) else if (m_state == MainAppState::Title)
updateTitle(); updateTitle(dt);
else if (m_state > MainAppState::Title) else if (m_state > MainAppState::Title)
updateRunning(); updateRunning(dt);
// Swallow leftover encoded voice data if we aren't in-game to allow mic read to continue for settings. // Swallow leftover encoded voice data if we aren't in-game to allow mic read to continue for settings.
if (m_state <= MainAppState::Title) { if (m_state <= MainAppState::Title) {
@ -647,8 +648,8 @@ void ClientApplication::setError(String const& error, std::exception const& e) {
changeState(MainAppState::Title); changeState(MainAppState::Title);
} }
void ClientApplication::updateMods() { void ClientApplication::updateMods(float dt) {
m_cinematicOverlay->update(); m_cinematicOverlay->update(dt);
auto ugcService = appController()->userGeneratedContentService(); auto ugcService = appController()->userGeneratedContentService();
if (ugcService) { if (ugcService) {
if (ugcService->triggerContentDownload()) { if (ugcService->triggerContentDownload()) {
@ -686,27 +687,28 @@ void ClientApplication::updateMods() {
} }
} }
void ClientApplication::updateModsWarning() { void ClientApplication::updateModsWarning(float dt) {
if (m_errorScreen->accepted()) if (m_errorScreen->accepted())
changeState(MainAppState::Splash); changeState(MainAppState::Splash);
} }
void ClientApplication::updateSplash() { void ClientApplication::updateSplash(float dt) {
m_cinematicOverlay->update(); m_cinematicOverlay->update(dt);
if (!m_rootLoader.isRunning() && (m_cinematicOverlay->completable() || m_cinematicOverlay->completed())) if (!m_rootLoader.isRunning() && (m_cinematicOverlay->completable() || m_cinematicOverlay->completed()))
changeState(MainAppState::Title); changeState(MainAppState::Title);
} }
void ClientApplication::updateError() { void ClientApplication::updateError(float dt) {
if (m_errorScreen->accepted()) if (m_errorScreen->accepted())
changeState(MainAppState::Title); changeState(MainAppState::Title);
} }
void ClientApplication::updateTitle() { void ClientApplication::updateTitle(float dt) {
m_cinematicOverlay->update(); m_cinematicOverlay->update(dt);
m_titleScreen->update(); m_titleScreen->update(dt);
m_mainMixer->update(); m_mainMixer->update(dt);
m_mainMixer->setSpeed(GlobalTimescale);
appController()->setAcceptingTextInput(m_titleScreen->textInputActive()); appController()->setAcceptingTextInput(m_titleScreen->textInputActive());
@ -752,7 +754,7 @@ void ClientApplication::updateTitle() {
} }
} }
void ClientApplication::updateRunning() { void ClientApplication::updateRunning(float dt) {
try { try {
auto p2pNetworkingService = appController()->p2pNetworkingService(); auto p2pNetworkingService = appController()->p2pNetworkingService();
bool clientIPJoinable = m_root->configuration()->get("clientIPJoinable").toBool(); bool clientIPJoinable = m_root->configuration()->get("clientIPJoinable").toBool();
@ -869,12 +871,13 @@ void ClientApplication::updateRunning() {
voiceData.setByteOrder(ByteOrder::LittleEndian); voiceData.setByteOrder(ByteOrder::LittleEndian);
//voiceData.writeBytes(VoiceBroadcastPrefix.utf8Bytes()); transmitting with SE compat for now //voiceData.writeBytes(VoiceBroadcastPrefix.utf8Bytes()); transmitting with SE compat for now
bool needstoSendVoice = m_voice->send(voiceData, 5000); bool needstoSendVoice = m_voice->send(voiceData, 5000);
m_universeClient->update(); m_universeClient->update(dt);
if (checkDisconnection()) if (checkDisconnection())
return; return;
if (auto worldClient = m_universeClient->worldClient()) { if (auto worldClient = m_universeClient->worldClient()) {
m_worldPainter->update(dt);
auto& broadcastCallback = worldClient->broadcastCallback(); auto& broadcastCallback = worldClient->broadcastCallback();
if (!broadcastCallback) { if (!broadcastCallback) {
broadcastCallback = [&](PlayerPtr player, StringView broadcast) -> bool { broadcastCallback = [&](PlayerPtr player, StringView broadcast) -> bool {
@ -909,12 +912,12 @@ void ClientApplication::updateRunning() {
} }
worldClient->setInteractiveHighlightMode(isActionTaken(InterfaceAction::ShowLabels)); worldClient->setInteractiveHighlightMode(isActionTaken(InterfaceAction::ShowLabels));
} }
updateCamera(dt);
updateCamera(); m_cinematicOverlay->update(dt);
m_mainInterface->update(dt);
m_cinematicOverlay->update(); m_mainMixer->update(dt, m_cinematicOverlay->muteSfx(), m_cinematicOverlay->muteMusic());
m_mainInterface->update(); m_mainMixer->setSpeed(GlobalTimescale);
m_mainMixer->update(m_cinematicOverlay->muteSfx(), m_cinematicOverlay->muteMusic());
bool inputActive = m_mainInterface->textInputActive(); bool inputActive = m_mainInterface->textInputActive();
appController()->setAcceptingTextInput(inputActive); appController()->setAcceptingTextInput(inputActive);
@ -970,12 +973,12 @@ bool ClientApplication::isActionTakenEdge(InterfaceAction action) const {
return false; return false;
} }
void ClientApplication::updateCamera() { void ClientApplication::updateCamera(float dt) {
if (!m_universeClient->worldClient()) if (!m_universeClient->worldClient())
return; return;
WorldCamera& camera = m_worldPainter->camera(); WorldCamera& camera = m_worldPainter->camera();
camera.update(WorldTimestep); camera.update(dt);
if (m_mainInterface->fixedCamera()) if (m_mainInterface->fixedCamera())
return; return;

View File

@ -58,17 +58,17 @@ private:
void setError(String const& error); void setError(String const& error);
void setError(String const& error, std::exception const& e); void setError(String const& error, std::exception const& e);
void updateMods(); void updateMods(float dt);
void updateModsWarning(); void updateModsWarning(float dt);
void updateSplash(); void updateSplash(float dt);
void updateError(); void updateError(float dt);
void updateTitle(); void updateTitle(float dt);
void updateRunning(); void updateRunning(float dt);
bool isActionTaken(InterfaceAction action) const; bool isActionTaken(InterfaceAction action) const;
bool isActionTakenEdge(InterfaceAction action) const; bool isActionTakenEdge(InterfaceAction action) const;
void updateCamera(); void updateCamera(float dt);
RootUPtr m_root; RootUPtr m_root;
ThreadFunction<void> m_rootLoader; ThreadFunction<void> m_rootLoader;

View File

@ -173,7 +173,7 @@ bool ActionBar::sendEvent(InputEvent const& event) {
return false; return false;
} }
void ActionBar::update() { void ActionBar::update(float dt) {
auto inventory = m_player->inventory(); auto inventory = m_player->inventory();
auto abl = inventory->selectedActionBarLocation(); auto abl = inventory->selectedActionBarLocation();
if (abl.is<CustomBarIndex>()) { if (abl.is<CustomBarIndex>()) {

View File

@ -22,7 +22,7 @@ public:
PanePtr createTooltip(Vec2I const& screenPosition) override; PanePtr createTooltip(Vec2I const& screenPosition) override;
bool sendEvent(InputEvent const& event) override; bool sendEvent(InputEvent const& event) override;
void update() override; void update(float dt) override;
Maybe<String> cursorOverride(Vec2I const& screenPosition) override; Maybe<String> cursorOverride(Vec2I const& screenPosition) override;

View File

@ -98,21 +98,21 @@ AiInterface::AiInterface(UniverseClientPtr client, CinematicPtr cinematic, MainI
m_defaultRecruitDescription = assets->json("/interface/ai/ai.config:defaultRecruitDescription").toString(); m_defaultRecruitDescription = assets->json("/interface/ai/ai.config:defaultRecruitDescription").toString();
} }
void AiInterface::update() { void AiInterface::update(float dt) {
if (!m_client->playerOnOwnShip()) if (!m_client->playerOnOwnShip())
dismiss(); dismiss();
Pane::update(); Pane::update(dt);
m_showCrewButton->setVisibility(m_currentPage == AiPages::StatusPage); m_showCrewButton->setVisibility(m_currentPage == AiPages::StatusPage);
m_showMissionsButton->setVisibility(m_currentPage == AiPages::StatusPage); m_showMissionsButton->setVisibility(m_currentPage == AiPages::StatusPage);
m_backButton->setVisibility(m_currentPage != AiPages::StatusPage); m_backButton->setVisibility(m_currentPage != AiPages::StatusPage);
m_staticAnimation.update(WorldTimestep); m_staticAnimation.update(dt);
m_scanlineAnimation.update(WorldTimestep); m_scanlineAnimation.update(dt);
if (m_currentSpeech) { if (m_currentSpeech) {
m_textLength += m_currentSpeech->speedModifier * m_aiDatabase->charactersPerSecond() * WorldTimestep; m_textLength += m_currentSpeech->speedModifier * m_aiDatabase->charactersPerSecond() * dt;
m_currentTextWidget->setText(m_currentSpeech->text); m_currentTextWidget->setText(m_currentSpeech->text);
m_currentTextWidget->setTextCharLimit(min(m_textMaxLength, floor(m_textLength))); m_currentTextWidget->setTextCharLimit(min(m_textMaxLength, floor(m_textLength)));
@ -129,10 +129,10 @@ void AiInterface::update() {
if (m_chatterSound) if (m_chatterSound)
m_chatterSound->stop(); m_chatterSound->stop();
} }
m_faceAnimation.second.update(WorldTimestep * m_currentSpeech->speedModifier); m_faceAnimation.second.update(dt * m_currentSpeech->speedModifier);
} else { } else {
setFaceAnimation("idle"); setFaceAnimation("idle");
m_faceAnimation.second.update(WorldTimestep); m_faceAnimation.second.update(dt);
if (m_chatterSound) if (m_chatterSound)
m_chatterSound->stop(); m_chatterSound->stop();
} }

View File

@ -34,7 +34,7 @@ class AiInterface : public Pane {
public: public:
AiInterface(UniverseClientPtr client, CinematicPtr cinematic, MainInterfacePaneManager* paneManager); AiInterface(UniverseClientPtr client, CinematicPtr cinematic, MainInterfacePaneManager* paneManager);
void update() override; void update(float dt) override;
void displayed() override; void displayed() override;
void dismissed() override; void dismissed() override;

View File

@ -71,8 +71,8 @@ void BaseScriptPane::dismissed() {
m_script.uninit(); m_script.uninit();
} }
void BaseScriptPane::tick() { void BaseScriptPane::tick(float dt) {
Pane::tick(); Pane::tick(dt);
for (auto p : m_canvasClickCallbacks) { for (auto p : m_canvasClickCallbacks) {
for (auto const& clickEvent : p.first->pullClickEvents()) for (auto const& clickEvent : p.first->pullClickEvents())
@ -83,7 +83,7 @@ void BaseScriptPane::tick() {
m_script.invoke(p.second, (int)keyEvent.key, keyEvent.keyDown); m_script.invoke(p.second, (int)keyEvent.key, keyEvent.keyDown);
} }
m_script.update(m_script.updateDt()); m_script.update(m_script.updateDt(dt));
} }
bool BaseScriptPane::sendEvent(InputEvent const& event) { bool BaseScriptPane::sendEvent(InputEvent const& event) {

View File

@ -22,7 +22,7 @@ public:
void displayed() override; void displayed() override;
void dismissed() override; void dismissed() override;
void tick() override; void tick(float dt) override;
bool sendEvent(InputEvent const& event) override; bool sendEvent(InputEvent const& event) override;

View File

@ -183,13 +183,13 @@ void CharCreationPane::randomize() {
changed(); changed();
} }
void CharCreationPane::tick() { void CharCreationPane::tick(float dt) {
Pane::tick(); Pane::tick(dt);
if (!active()) if (!active())
return; return;
if (!m_previewPlayer) if (!m_previewPlayer)
return; return;
m_previewPlayer->animatePortrait(); m_previewPlayer->animatePortrait(dt);
} }
bool CharCreationPane::sendEvent(InputEvent const& event) { bool CharCreationPane::sendEvent(InputEvent const& event) {

View File

@ -23,7 +23,7 @@ public:
void randomize(); void randomize();
void randomizeName(); void randomizeName();
virtual void tick() override; virtual void tick(float dt) override;
virtual bool sendEvent(InputEvent const& event) override; virtual bool sendEvent(InputEvent const& event) override;
virtual PanePtr createTooltip(Vec2I const&) override; virtual PanePtr createTooltip(Vec2I const&) override;

View File

@ -95,8 +95,8 @@ Chat::Chat(UniverseClientPtr client) : m_client(client) {
updateSize(); updateSize();
} }
void Chat::update() { void Chat::update(float dt) {
Pane::update(); Pane::update(dt);
auto team = m_client->teamClient()->currentTeam(); auto team = m_client->teamClient()->currentTeam();
for (auto button : fetchChild<ButtonGroup>("filterGroup")->buttons()) { for (auto button : fetchChild<ButtonGroup>("filterGroup")->buttons()) {

View File

@ -26,7 +26,7 @@ public:
virtual void renderImpl() override; virtual void renderImpl() override;
virtual void hide() override; virtual void hide() override;
virtual void update() override; virtual void update(float dt) override;
void addLine(String const& text, bool showPane = true); void addLine(String const& text, bool showPane = true);
void addMessages(List<ChatReceivedMessage> const& messages, bool showPane = true); void addMessages(List<ChatReceivedMessage> const& messages, bool showPane = true);

View File

@ -86,9 +86,9 @@ void ChatBubbleManager::setCamera(WorldCamera const& camera) {
} }
} }
void ChatBubbleManager::update(WorldClientPtr world) { void ChatBubbleManager::update(float dt, WorldClientPtr world) {
m_bubbles.forEach([this, &world](BubbleState<Bubble>& bubbleState, Bubble& bubble) { m_bubbles.forEach([this, dt, &world](BubbleState<Bubble>& bubbleState, Bubble& bubble) {
bubble.age += WorldTimestep; bubble.age += dt;
if (auto entity = world->get<ChattyEntity>(bubble.entity)) { if (auto entity = world->get<ChattyEntity>(bubble.entity)) {
bubble.onscreen = m_camera.worldGeometry().rectIntersectsRect( bubble.onscreen = m_camera.worldGeometry().rectIntersectsRect(
m_camera.worldScreenRect(), entity->metaBoundBox().translated(entity->position())); m_camera.worldScreenRect(), entity->metaBoundBox().translated(entity->position()));
@ -97,7 +97,7 @@ void ChatBubbleManager::update(WorldClientPtr world) {
}); });
for (auto& portraitBubble : m_portraitBubbles) { for (auto& portraitBubble : m_portraitBubbles) {
portraitBubble.age += WorldTimestep; portraitBubble.age += dt;
if (auto entity = world->entity(portraitBubble.entity)) { if (auto entity = world->entity(portraitBubble.entity)) {
portraitBubble.onscreen = m_camera.worldGeometry().rectIntersectsRect(m_camera.worldScreenRect(), entity->metaBoundBox().translated(entity->position())); portraitBubble.onscreen = m_camera.worldGeometry().rectIntersectsRect(m_camera.worldScreenRect(), entity->metaBoundBox().translated(entity->position()));
if (auto chatter = as<ChattyEntity>(entity)) if (auto chatter = as<ChattyEntity>(entity))
@ -125,7 +125,7 @@ void ChatBubbleManager::update(WorldClientPtr world) {
return false; return false;
}); });
m_bubbles.update(); m_bubbles.update(dt);
} }
uint8_t ChatBubbleManager::calcDistanceFadeAlpha(Vec2F bubbleScreenPosition, StoredFunctionPtr fadeFunction) const { uint8_t ChatBubbleManager::calcDistanceFadeAlpha(Vec2F bubbleScreenPosition, StoredFunctionPtr fadeFunction) const {

View File

@ -22,7 +22,7 @@ public:
void addChatActions(List<ChatAction> chatActions, bool silent = false); void addChatActions(List<ChatAction> chatActions, bool silent = false);
void update(WorldClientPtr world); void update(float dt, WorldClientPtr world);
void render(); void render();
private: private:

View File

@ -52,7 +52,7 @@ public:
List<Bubble> filtered(function<bool(Bubble const&, T const&)> func); List<Bubble> filtered(function<bool(Bubble const&, T const&)> func);
void forEach(function<void(Bubble&, T&)> func); void forEach(function<void(Bubble&, T&)> func);
void update(); void update(float dt);
void clear(); void clear();
bool empty() const; bool empty() const;
@ -170,7 +170,7 @@ void BubbleSeparator<T>::forEach(function<void(Bubble&, T&)> func) {
} }
template <typename T> template <typename T>
void BubbleSeparator<T>::update() { void BubbleSeparator<T>::update(float dt) {
m_bubbles.exec([this](Bubble& bubble) { m_bubbles.exec([this](Bubble& bubble) {
Vec2F delta = bubble.seperatedOffset - bubble.currentOffset; Vec2F delta = bubble.seperatedOffset - bubble.currentOffset;
bubble.currentOffset += m_tweenFactor * delta; bubble.currentOffset += m_tweenFactor * delta;

View File

@ -103,7 +103,7 @@ void Cinematic::setPlayer(PlayerPtr player) {
m_player = player; m_player = player;
} }
void Cinematic::update() { void Cinematic::update(float dt) {
m_currentTimeSkip = {}; m_currentTimeSkip = {};
for (auto timeSkip : m_timeSkips) { for (auto timeSkip : m_timeSkips) {
if (currentTimecode() >= timeSkip.availableTime && currentTimecode() < timeSkip.skipToTime) if (currentTimecode() >= timeSkip.availableTime && currentTimecode() < timeSkip.skipToTime)

View File

@ -22,7 +22,7 @@ public:
void setPlayer(PlayerPtr player); void setPlayer(PlayerPtr player);
void update(); void update(float dt);
void render(); void render();
bool completed() const; bool completed() const;

View File

@ -50,7 +50,7 @@ void CodexInterface::show() {
updateCodexList(); updateCodexList();
} }
void CodexInterface::tick() { void CodexInterface::tick(float dt) {
updateCodexList(); updateCodexList();
} }

View File

@ -21,7 +21,7 @@ public:
CodexInterface(PlayerPtr player); CodexInterface(PlayerPtr player);
virtual void show() override; virtual void show() override;
virtual void tick() override; virtual void tick(float dt) override;
void showTitles(); void showTitles();
void showSelectedContents(); void showSelectedContents();

View File

@ -258,11 +258,11 @@ void ContainerPane::burn() {
m_containerInteractor->burnContainer(); m_containerInteractor->burnContainer();
} }
void ContainerPane::update() { void ContainerPane::update(float dt) {
Pane::update(); Pane::update(dt);
if (m_script) if (m_script)
m_script->update(m_script->updateDt()); m_script->update(m_script->updateDt(dt));
m_itemBag->clearItems(); m_itemBag->clearItems();

View File

@ -27,7 +27,7 @@ public:
bool giveContainerResult(ContainerResult result); bool giveContainerResult(ContainerResult result);
protected: protected:
void update() override; void update(float dt) override;
private: private:
enum class ExpectingSwap { enum class ExpectingSwap {

View File

@ -226,7 +226,7 @@ size_t CraftingPane::itemCount(List<ItemPtr> const& store, ItemDescriptor const&
return itemDb->getCountOfItem(store, item); return itemDb->getCountOfItem(store, item);
} }
void CraftingPane::update() { void CraftingPane::update(float dt) {
// shut down if we can't reach the table anymore. // shut down if we can't reach the table anymore.
if (m_sourceEntityId != NullEntityId) { if (m_sourceEntityId != NullEntityId) {
auto sourceEntity = as<TileEntity>(m_worldClient->entity(m_sourceEntityId)); auto sourceEntity = as<TileEntity>(m_worldClient->entity(m_sourceEntityId));
@ -296,7 +296,7 @@ void CraftingPane::update() {
setLabel("lblPlayerMoney", toString((int)m_player->currency("money"))); setLabel("lblPlayerMoney", toString((int)m_player->currency("money")));
Pane::update(); Pane::update(dt);
} }
void CraftingPane::updateCraftButtons() { void CraftingPane::updateCraftButtons() {

View File

@ -34,7 +34,7 @@ private:
List<ItemRecipe> determineRecipes(); List<ItemRecipe> determineRecipes();
virtual void update() override; virtual void update(float dt) override;
void updateCraftButtons(); void updateCraftButtons();
void updateAvailableRecipes(); void updateAvailableRecipes();
bool consumeIngredients(ItemRecipe& recipe, int count); bool consumeIngredients(ItemRecipe& recipe, int count);

View File

@ -70,12 +70,12 @@ bool ErrorScreen::handleInputEvent(InputEvent const& event) {
return m_paneManager->sendInputEvent(event); return m_paneManager->sendInputEvent(event);
} }
void ErrorScreen::update() { void ErrorScreen::update(float dt) {
m_paneManager->update(); m_paneManager->update(dt);
m_cursor.update(dt);
} }
void ErrorScreen::renderCursor() { void ErrorScreen::renderCursor() {
m_cursor.update(WorldTimestep);
Vec2I cursorPos = m_cursorScreenPos; Vec2I cursorPos = m_cursorScreenPos;
Vec2I cursorSize = m_cursor.size(); Vec2I cursorSize = m_cursor.size();
Vec2I cursorOffset = m_cursor.offset(); Vec2I cursorOffset = m_cursor.offset();

View File

@ -26,7 +26,7 @@ public:
void render(bool useBackdrop = false); void render(bool useBackdrop = false);
bool handleInputEvent(InputEvent const& event); bool handleInputEvent(InputEvent const& event);
void update(); void update(float dt);
private: private:
void renderCursor(); void renderCursor();

View File

@ -236,7 +236,7 @@ bool InventoryPane::containsNewItems() const {
return false; return false;
} }
void InventoryPane::update() { void InventoryPane::update(float dt) {
auto inventory = m_player->inventory(); auto inventory = m_player->inventory();
auto context = Widget::context(); auto context = Widget::context();
@ -255,7 +255,7 @@ void InventoryPane::update() {
m_trashSlot->setItem(inventory->itemsAt(TrashSlot())); m_trashSlot->setItem(inventory->itemsAt(TrashSlot()));
m_trashSlot->showLinkIndicator(customBarItems.contains(m_trashSlot->item())); m_trashSlot->showLinkIndicator(customBarItems.contains(m_trashSlot->item()));
if (auto trashItem = m_trashSlot->item()) { if (auto trashItem = m_trashSlot->item()) {
if (m_trashBurn.tick() && trashItem->count() > 0) { if (m_trashBurn.tick(dt) && trashItem->count() > 0) {
m_player->statistics()->recordEvent("trashItem", JsonObject{ m_player->statistics()->recordEvent("trashItem", JsonObject{
{"itemName", trashItem->name()}, {"itemName", trashItem->name()},
{"count", trashItem->count()}, {"count", trashItem->count()},

View File

@ -35,7 +35,7 @@ public:
bool containsNewItems() const; bool containsNewItems() const;
protected: protected:
virtual void update() override; virtual void update(float dt) override;
void selectTab(String const& selected); void selectTab(String const& selected);
private: private:

View File

@ -520,8 +520,9 @@ void MainInterface::handleInteractAction(InteractAction interactAction) {
} }
} }
void MainInterface::update() { void MainInterface::update(float dt) {
m_paneManager.update(); m_paneManager.update(dt);
m_cursor.update(dt);
m_questLogInterface->pollDialog(&m_paneManager); m_questLogInterface->pollDialog(&m_paneManager);
@ -544,7 +545,7 @@ void MainInterface::update() {
// update mouseover target // update mouseover target
EntityId newMouseOverTarget = NullEntityId; EntityId newMouseOverTarget = NullEntityId;
m_stickyTargetingTimer.tick(); m_stickyTargetingTimer.tick(dt);
auto mouseoverEntities = m_client->worldClient()->query<DamageBarEntity>(RectF::withCenter(cursorWorldPos, Vec2F(1, 1)), [=](shared_ptr<DamageBarEntity> const& entity) { auto mouseoverEntities = m_client->worldClient()->query<DamageBarEntity>(RectF::withCenter(cursorWorldPos, Vec2F(1, 1)), [=](shared_ptr<DamageBarEntity> const& entity) {
return entity != player return entity != player
&& entity->damageBar() == DamageBarType::Default && entity->damageBar() == DamageBarType::Default
@ -578,10 +579,10 @@ void MainInterface::update() {
if (damageBarEntity && damageBarEntity->damageBar() == DamageBarType::Special) { if (damageBarEntity && damageBarEntity->damageBar() == DamageBarType::Special) {
float targetHealth = damageBarEntity->health() / damageBarEntity->maxHealth(); float targetHealth = damageBarEntity->health() / damageBarEntity->maxHealth();
float fillSpeed = 1.0f / Root::singleton().assets()->json("/interface.config:specialDamageBar.fillTime").toFloat(); float fillSpeed = 1.0f / Root::singleton().assets()->json("/interface.config:specialDamageBar.fillTime").toFloat();
if (abs(targetHealth - m_specialDamageBarValue) < fillSpeed * WorldTimestep) if (abs(targetHealth - m_specialDamageBarValue) < fillSpeed * dt)
m_specialDamageBarValue = targetHealth; m_specialDamageBarValue = targetHealth;
else else
m_specialDamageBarValue += copysign(1.0f, targetHealth - m_specialDamageBarValue) * fillSpeed * WorldTimestep; m_specialDamageBarValue += copysign(1.0f, targetHealth - m_specialDamageBarValue) * fillSpeed * dt;
} else { } else {
m_specialDamageBarTarget = NullEntityId; m_specialDamageBarTarget = NullEntityId;
} }
@ -696,7 +697,7 @@ void MainInterface::update() {
for (auto it = m_messages.begin(); it != m_messages.end();) { for (auto it = m_messages.begin(); it != m_messages.end();) {
auto& message = *it; auto& message = *it;
message->cooldown -= WorldTimestep; message->cooldown -= dt;
if (message->cooldown < 0) if (message->cooldown < 0)
it = m_messages.erase(it); it = m_messages.erase(it);
else else
@ -713,7 +714,7 @@ void MainInterface::update() {
auto worldId = m_client->playerWorld(); auto worldId = m_client->playerWorld();
if (worldId.is<CelestialWorldId>()) { if (worldId.is<CelestialWorldId>()) {
if (m_planetNameTimer.tick()) if (m_planetNameTimer.tick(dt))
m_paneManager.dismissRegisteredPane(MainInterfacePanes::PlanetText); m_paneManager.dismissRegisteredPane(MainInterfacePanes::PlanetText);
else else
m_paneManager.displayRegisteredPane(MainInterfacePanes::PlanetText); m_paneManager.displayRegisteredPane(MainInterfacePanes::PlanetText);
@ -755,8 +756,8 @@ void MainInterface::update() {
updateCursor(); updateCursor();
m_nameplatePainter->update(m_client->worldClient(), m_worldPainter->camera(), m_client->worldClient()->interactiveHighlightMode()); m_nameplatePainter->update(dt, m_client->worldClient(), m_worldPainter->camera(), m_client->worldClient()->interactiveHighlightMode());
m_questIndicatorPainter->update(m_client->worldClient(), m_worldPainter->camera()); m_questIndicatorPainter->update(dt, m_client->worldClient(), m_worldPainter->camera());
m_chatBubbleManager->setCamera(m_worldPainter->camera()); m_chatBubbleManager->setCamera(m_worldPainter->camera());
if (auto worldClient = m_client->worldClient()) { if (auto worldClient = m_client->worldClient()) {
@ -781,7 +782,7 @@ void MainInterface::update() {
} }
m_chatBubbleManager->addChatActions(chatActions); m_chatBubbleManager->addChatActions(chatActions);
m_chatBubbleManager->update(worldClient); m_chatBubbleManager->update(dt, worldClient);
} }
if (auto container = m_client->worldClient()->get<ContainerEntity>(m_containerInteractor->openContainerId())) { if (auto container = m_client->worldClient()->get<ContainerEntity>(m_containerInteractor->openContainerId())) {
@ -803,7 +804,7 @@ void MainInterface::update() {
pair.second->setSize(Vec2I(m_guiContext->windowSize())); pair.second->setSize(Vec2I(m_guiContext->windowSize()));
else else
pair.second->setSize(Vec2I(m_guiContext->windowInterfaceSize())); pair.second->setSize(Vec2I(m_guiContext->windowInterfaceSize()));
pair.second->update(); pair.second->update(dt);
} }
} }
@ -1427,8 +1428,6 @@ void MainInterface::renderCursor() {
if (m_cinematicOverlay && !m_cinematicOverlay->completed()) if (m_cinematicOverlay && !m_cinematicOverlay->completed())
return m_guiContext->applicationController()->setCursorVisible(false); return m_guiContext->applicationController()->setCursorVisible(false);
m_cursor.update(WorldTimestep);
Vec2I cursorPos = m_cursorScreenPos; Vec2I cursorPos = m_cursorScreenPos;
Vec2I cursorSize = m_cursor.size(); Vec2I cursorSize = m_cursor.size();
Vec2I cursorOffset = m_cursor.offset(); Vec2I cursorOffset = m_cursor.offset();

View File

@ -91,7 +91,7 @@ public:
void handleInteractAction(InteractAction interactAction); void handleInteractAction(InteractAction interactAction);
// Handles incoming client messages, aims main player, etc. // Handles incoming client messages, aims main player, etc.
void update(); void update(float dt);
// Render things e.g. quest indicators that should be drawn in the world // Render things e.g. quest indicators that should be drawn in the world
// behind interface e.g. chat bubbles // behind interface e.g. chat bubbles

View File

@ -23,7 +23,7 @@ void MainMixer::setWorldPainter(WorldPainterPtr worldPainter) {
m_worldPainter = move(worldPainter); m_worldPainter = move(worldPainter);
} }
void MainMixer::update(bool muteSfx, bool muteMusic) { void MainMixer::update(float dt, bool muteSfx, bool muteMusic) {
auto assets = Root::singleton().assets(); auto assets = Root::singleton().assets();
auto updateGroupVolume = [&](MixerGroup group, bool muted, String const& settingName) { auto updateGroupVolume = [&](MixerGroup group, bool muted, String const& settingName) {
@ -111,9 +111,9 @@ void MainMixer::update(bool muteSfx, bool muteMusic) {
}; };
if (Voice* voice = Voice::singletonPtr()) if (Voice* voice = Voice::singletonPtr())
voice->update(attenuationFunction); voice->update(dt, attenuationFunction);
m_mixer->update(attenuationFunction); m_mixer->update(dt, attenuationFunction);
} else { } else {
if (m_mixer->hasEffect("lowpass")) if (m_mixer->hasEffect("lowpass"))
@ -122,9 +122,9 @@ void MainMixer::update(bool muteSfx, bool muteMusic) {
m_mixer->removeEffect("echo", 0); m_mixer->removeEffect("echo", 0);
if (Voice* voice = Voice::singletonPtr()) if (Voice* voice = Voice::singletonPtr())
voice->update(); voice->update(dt);
m_mixer->update(); m_mixer->update(dt);
} }
} }
@ -132,6 +132,10 @@ MixerPtr MainMixer::mixer() const {
return m_mixer; return m_mixer;
} }
void MainMixer::setSpeed(float speed) {
m_mixer->setSpeed(max(speed, 0.0f));
}
void MainMixer::setVolume(float volume, float rampTime) { void MainMixer::setVolume(float volume, float rampTime) {
m_mixer->setVolume(volume, rampTime); m_mixer->setVolume(volume, rampTime);
} }

View File

@ -17,10 +17,11 @@ public:
void setUniverseClient(UniverseClientPtr universeClient); void setUniverseClient(UniverseClientPtr universeClient);
void setWorldPainter(WorldPainterPtr worldPainter); void setWorldPainter(WorldPainterPtr worldPainter);
void update(bool muteSfx = false, bool muteMusic = false); void update(float dt, bool muteSfx = false, bool muteMusic = false);
MixerPtr mixer() const; MixerPtr mixer() const;
void setSpeed(float speed);
void setVolume(float volume, float rampTime = 0.0f); void setVolume(float volume, float rampTime = 0.0f);
void read(int16_t* sampleData, size_t frameCount, Mixer::ExtraMixFunction = {}); void read(int16_t* sampleData, size_t frameCount, Mixer::ExtraMixFunction = {});

View File

@ -138,8 +138,8 @@ PanePtr MerchantPane::createTooltip(Vec2I const& screenPosition) {
return {}; return {};
} }
void MerchantPane::update() { void MerchantPane::update(float dt) {
Pane::update(); Pane::update(dt);
if (!m_worldClient->playerCanReachEntity(m_sourceEntityId)) if (!m_worldClient->playerCanReachEntity(m_sourceEntityId))
dismiss(); dismiss();

View File

@ -30,7 +30,7 @@ public:
ItemPtr addItems(ItemPtr const& items); ItemPtr addItems(ItemPtr const& items);
protected: protected:
void update() override; void update(float dt) override;
private: private:
void swapSlot(); void swapSlot();

View File

@ -51,8 +51,8 @@ ModsMenu::ModsMenu() {
copyLinkLabel->setVisibility(!hasDesktopService); copyLinkLabel->setVisibility(!hasDesktopService);
} }
void ModsMenu::update() { void ModsMenu::update(float dt) {
Pane::update(); Pane::update(dt);
size_t selectedItem = m_modList->selectedItem(); size_t selectedItem = m_modList->selectedItem();
if (selectedItem == NPos) { if (selectedItem == NPos) {

View File

@ -13,7 +13,7 @@ class ModsMenu : public Pane {
public: public:
ModsMenu(); ModsMenu();
void update() override; void update(float dt) override;
private: private:
static String bestModName(JsonObject const& metadata, String const& sourcePath); static String bestModName(JsonObject const& metadata, String const& sourcePath);

View File

@ -28,7 +28,7 @@ NameplatePainter::NameplatePainter() {
m_nametags.setMovementThreshold(nametagConfig.getFloat("movementThreshold")); m_nametags.setMovementThreshold(nametagConfig.getFloat("movementThreshold"));
} }
void NameplatePainter::update(WorldClientPtr const& world, WorldCamera const& camera, bool inspectionMode) { void NameplatePainter::update(float dt, WorldClientPtr const& world, WorldCamera const& camera, bool inspectionMode) {
m_camera = camera; m_camera = camera;
Set<EntityId> foundEntities; Set<EntityId> foundEntities;
@ -70,7 +70,7 @@ void NameplatePainter::update(WorldClientPtr const& world, WorldCamera const& ca
}); });
m_entitiesWithNametags = move(foundEntities); m_entitiesWithNametags = move(foundEntities);
m_nametags.update(); m_nametags.update(dt);
} }
void NameplatePainter::render() { void NameplatePainter::render() {

View File

@ -15,7 +15,7 @@ class NameplatePainter {
public: public:
NameplatePainter(); NameplatePainter();
void update(WorldClientPtr const& world, WorldCamera const& camera, bool inspectionMode); void update(float dt, WorldClientPtr const& world, WorldCamera const& camera, bool inspectionMode);
void render(); void render();
private: private:

View File

@ -16,7 +16,7 @@ AnimationPtr indicatorAnimation(String indicatorPath) {
return make_shared<Animation>(assets->json(indicatorPath), indicatorPath); return make_shared<Animation>(assets->json(indicatorPath), indicatorPath);
} }
void QuestIndicatorPainter::update(WorldClientPtr const& world, WorldCamera const& camera) { void QuestIndicatorPainter::update(float dt, WorldClientPtr const& world, WorldCamera const& camera) {
m_camera = camera; m_camera = camera;
Set<EntityId> foundIndicators; Set<EntityId> foundIndicators;
@ -30,7 +30,7 @@ void QuestIndicatorPainter::update(WorldClientPtr const& world, WorldCamera cons
if (auto currentIndicator = m_indicators.ptr(entity->entityId())) { if (auto currentIndicator = m_indicators.ptr(entity->entityId())) {
currentIndicator->screenPos = screenPos; currentIndicator->screenPos = screenPos;
if (currentIndicator->indicatorName == indicator->indicatorImage) { if (currentIndicator->indicatorName == indicator->indicatorImage) {
currentIndicator->animation->update(WorldTimestep); currentIndicator->animation->update(dt);
} else { } else {
currentIndicator->indicatorName = indicator->indicatorImage; currentIndicator->indicatorName = indicator->indicatorImage;
currentIndicator->animation = indicatorAnimation(indicator->indicatorImage); currentIndicator->animation = indicatorAnimation(indicator->indicatorImage);

View File

@ -13,7 +13,7 @@ class QuestIndicatorPainter {
public: public:
QuestIndicatorPainter(UniverseClientPtr const& client); QuestIndicatorPainter(UniverseClientPtr const& client);
void update(WorldClientPtr const& world, WorldCamera const& camera); void update(float dt, WorldClientPtr const& world, WorldCamera const& camera);
void render(); void render();
private: private:

View File

@ -93,12 +93,12 @@ void QuestLogInterface::pollDialog(PaneManager* paneManager) {
void QuestLogInterface::displayed() { void QuestLogInterface::displayed() {
Pane::displayed(); Pane::displayed();
tick(); tick(0);
fetchData(); fetchData();
} }
void QuestLogInterface::tick() { void QuestLogInterface::tick(float dt) {
Pane::tick(); Pane::tick(dt);
auto selected = getSelected(); auto selected = getSelected();
if (selected && m_manager->hasQuest(selected->data().toString())) { if (selected && m_manager->hasQuest(selected->data().toString())) {
auto quest = m_manager->getQuest(selected->data().toString()); auto quest = m_manager->getQuest(selected->data().toString());
@ -284,7 +284,7 @@ void QuestLogInterface::showQuests(List<QuestPtr> quests) {
} }
auto verticalLayout = fetchChild<VerticalLayout>("scrollArea.verticalLayout"); auto verticalLayout = fetchChild<VerticalLayout>("scrollArea.verticalLayout");
verticalLayout->update(); verticalLayout->update(0);
} }
QuestPane::QuestPane(QuestPtr const& quest, PlayerPtr player) : Pane(), m_quest(quest), m_player(move(player)) {} QuestPane::QuestPane(QuestPtr const& quest, PlayerPtr player) : Pane(), m_quest(quest), m_player(move(player)) {}

View File

@ -19,7 +19,7 @@ public:
virtual ~QuestLogInterface() {} virtual ~QuestLogInterface() {}
virtual void displayed() override; virtual void displayed() override;
virtual void tick() override; virtual void tick(float dt) override;
virtual PanePtr createTooltip(Vec2I const& screenPosition) override; virtual PanePtr createTooltip(Vec2I const& screenPosition) override;
void fetchData(); void fetchData();

View File

@ -71,7 +71,7 @@ bool QuestTrackerPane::sendEvent(InputEvent const& event) {
return false; return false;
} }
void QuestTrackerPane::update() { void QuestTrackerPane::update(float dt) {
if (m_currentQuest) { if (m_currentQuest) {
if (auto objectiveList = m_currentQuest->objectiveList()) { if (auto objectiveList = m_currentQuest->objectiveList()) {
if (objectiveList->size() == 0) { if (objectiveList->size() == 0) {
@ -172,7 +172,7 @@ void QuestTrackerPane::update() {
} }
} }
Pane::update(); Pane::update(dt);
} }
void QuestTrackerPane::setQuest(QuestPtr const& quest) { void QuestTrackerPane::setQuest(QuestPtr const& quest) {

View File

@ -16,7 +16,7 @@ public:
QuestTrackerPane(); QuestTrackerPane();
bool sendEvent(InputEvent const& event) override; bool sendEvent(InputEvent const& event) override;
void update() override; void update(float dt) override;
void setQuest(QuestPtr const& quest); void setQuest(QuestPtr const& quest);

View File

@ -44,9 +44,9 @@ RadioMessagePopup::RadioMessagePopup() {
enterStage(PopupStage::Hidden); enterStage(PopupStage::Hidden);
} }
void RadioMessagePopup::update() { void RadioMessagePopup::update(float dt) {
if (messageActive()) { if (messageActive()) {
if (m_stageTimer.tick()) if (m_stageTimer.tick(dt))
nextPopupStage(); nextPopupStage();
if (m_popupStage == PopupStage::AnimateIn) { if (m_popupStage == PopupStage::AnimateIn) {
@ -65,11 +65,11 @@ void RadioMessagePopup::update() {
setBG("", strf("{}:{}", m_animateOutImage, frame), ""); setBG("", strf("{}:{}", m_animateOutImage, frame), "");
} }
m_slideTimer = min(m_slideTimer + WorldTimestep, m_slideTime); m_slideTimer = min(m_slideTimer + dt, m_slideTime);
updateAnchorOffset(); updateAnchorOffset();
} }
Pane::update(); Pane::update(dt);
} }
void RadioMessagePopup::dismissed() { void RadioMessagePopup::dismissed() {

View File

@ -16,7 +16,7 @@ class RadioMessagePopup : public Pane {
public: public:
RadioMessagePopup(); RadioMessagePopup();
void update() override; void update(float dt) override;
void dismissed() override; void dismissed() override;
bool messageActive(); bool messageActive();

View File

@ -47,11 +47,11 @@ void ScriptPane::dismissed() {
m_script.removeCallbacks("world"); m_script.removeCallbacks("world");
} }
void ScriptPane::tick() { void ScriptPane::tick(float dt) {
if (m_sourceEntityId != NullEntityId && !m_client->worldClient()->playerCanReachEntity(m_sourceEntityId)) if (m_sourceEntityId != NullEntityId && !m_client->worldClient()->playerCanReachEntity(m_sourceEntityId))
dismiss(); dismiss();
BaseScriptPane::tick(); BaseScriptPane::tick(dt);
} }
PanePtr ScriptPane::createTooltip(Vec2I const& screenPosition) { PanePtr ScriptPane::createTooltip(Vec2I const& screenPosition) {

View File

@ -16,7 +16,7 @@ public:
void displayed() override; void displayed() override;
void dismissed() override; void dismissed() override;
void tick() override; void tick(float dt) override;
PanePtr createTooltip(Vec2I const& screenPosition) override; PanePtr createTooltip(Vec2I const& screenPosition) override;

View File

@ -58,8 +58,8 @@ void StatusPane::renderImpl() {
} }
} }
void StatusPane::update() { void StatusPane::update(float dt) {
Pane::update(); Pane::update(dt);
auto assets = Root::singleton().assets(); auto assets = Root::singleton().assets();
auto interfaceScale = m_guiContext->interfaceScale(); auto interfaceScale = m_guiContext->interfaceScale();

View File

@ -18,7 +18,7 @@ public:
protected: protected:
virtual void renderImpl() override; virtual void renderImpl() override;
virtual void update() override; virtual void update(float dt) override;
private: private:
struct StatusEffectIndicator { struct StatusEffectIndicator {

View File

@ -80,8 +80,8 @@ void TeamBar::acceptInvitation(Uuid const& inviterUuid) {
m_client->teamClient()->acceptInvitation(inviterUuid); m_client->teamClient()->acceptInvitation(inviterUuid);
} }
void TeamBar::update() { void TeamBar::update(float dt) {
Pane::update(); Pane::update(dt);
updatePlayerResources(); updatePlayerResources();
@ -338,7 +338,7 @@ void TeamMemberMenu::open(Uuid memberUuid, Vec2I position) {
Pane::show(); Pane::show();
} }
void TeamMemberMenu::update() { void TeamMemberMenu::update(float dt) {
auto stillValid = false; auto stillValid = false;
auto members = m_owner->m_client->teamClient()->members(); auto members = m_owner->m_client->teamClient()->members();
for (auto member : members) { for (auto member : members) {
@ -355,7 +355,7 @@ void TeamMemberMenu::update() {
updateWidgets(); updateWidgets();
Pane::update(); Pane::update(dt);
} }
void TeamMemberMenu::updateWidgets() { void TeamMemberMenu::updateWidgets() {

View File

@ -52,7 +52,7 @@ public:
void open(Uuid memberUuid, Vec2I position); void open(Uuid memberUuid, Vec2I position);
virtual void update() override; virtual void update(float dt) override;
private: private:
void updateWidgets(); void updateWidgets();
@ -77,7 +77,7 @@ public:
void acceptInvitation(Uuid const& inviterUuid); void acceptInvitation(Uuid const& inviterUuid);
protected: protected:
virtual void update() override; virtual void update(float dt) override;
private: private:
void updatePlayerResources(); void updatePlayerResources();

View File

@ -120,7 +120,7 @@ TeleportDialog::TeleportDialog(UniverseClientPtr client,
fetchChild<ButtonWidget>("btnTeleport")->setEnabled(destList->selectedItem() != NPos); fetchChild<ButtonWidget>("btnTeleport")->setEnabled(destList->selectedItem() != NPos);
} }
void TeleportDialog::tick() { void TeleportDialog::tick(float dt) {
if (!m_client->worldClient()->playerCanReachEntity(m_sourceEntityId)) if (!m_client->worldClient()->playerCanReachEntity(m_sourceEntityId))
dismiss(); dismiss();
} }

View File

@ -19,7 +19,7 @@ public:
EntityId sourceEntityId, EntityId sourceEntityId,
TeleportBookmark currentLocation); TeleportBookmark currentLocation);
void tick() override; void tick(float dt) override;
void selectDestination(); void selectDestination();
void teleport(); void teleport();

View File

@ -114,15 +114,17 @@ bool TitleScreen::handleInputEvent(InputEvent const& event) {
return m_paneManager.sendInputEvent(event); return m_paneManager.sendInputEvent(event);
} }
void TitleScreen::update() { void TitleScreen::update(float dt) {
m_cursor.update(dt);
for (auto p : m_rightAnchoredButtons) for (auto p : m_rightAnchoredButtons)
p.first->setPosition(Vec2I(m_guiContext->windowWidth() / m_guiContext->interfaceScale(), 0) + p.second); p.first->setPosition(Vec2I(m_guiContext->windowWidth() / m_guiContext->interfaceScale(), 0) + p.second);
m_mainMenu->determineSizeFromChildren(); m_mainMenu->determineSizeFromChildren();
m_skyBackdrop->update(); m_skyBackdrop->update(dt);
m_environmentPainter->update(); m_environmentPainter->update(dt);
m_paneManager.update(); m_paneManager.update(dt);
if (!finishedState()) { if (!finishedState()) {
if (auto audioSample = m_musicTrackManager.updateAmbient(m_musicTrack, m_skyBackdrop->isDayTime())) { if (auto audioSample = m_musicTrackManager.updateAmbient(m_musicTrack, m_skyBackdrop->isDayTime())) {
@ -423,7 +425,6 @@ void TitleScreen::back() {
void TitleScreen::renderCursor() { void TitleScreen::renderCursor() {
auto assets = Root::singleton().assets(); auto assets = Root::singleton().assets();
m_cursor.update(WorldTimestep);
Vec2I cursorPos = m_cursorScreenPos; Vec2I cursorPos = m_cursorScreenPos;
Vec2I cursorSize = m_cursor.size(); Vec2I cursorSize = m_cursor.size();
Vec2I cursorOffset = m_cursor.offset(); Vec2I cursorOffset = m_cursor.offset();

View File

@ -47,7 +47,7 @@ public:
void render(); void render();
bool handleInputEvent(InputEvent const& event); bool handleInputEvent(InputEvent const& event);
void update(); void update(float dt);
bool textInputActive() const; bool textInputActive() const;

View File

@ -420,7 +420,7 @@ void Voice::mix(int16_t* buffer, size_t frameCount, unsigned channels) {
} }
} }
void Voice::update(PositionalAttenuationFunction positionalAttenuationFunction) { void Voice::update(float dt, PositionalAttenuationFunction positionalAttenuationFunction) {
for (auto& entry : m_speakers) { for (auto& entry : m_speakers) {
if (SpeakerPtr& speaker = entry.second) { if (SpeakerPtr& speaker = entry.second) {
if (positionalAttenuationFunction) { if (positionalAttenuationFunction) {

View File

@ -136,7 +136,7 @@ public:
void mix(int16_t* buffer, size_t frames, unsigned channels); void mix(int16_t* buffer, size_t frames, unsigned channels);
typedef function<float(unsigned, Vec2F, float)> PositionalAttenuationFunction; typedef function<float(unsigned, Vec2F, float)> PositionalAttenuationFunction;
void update(PositionalAttenuationFunction positionalAttenuationFunction = {}); void update(float dt, PositionalAttenuationFunction positionalAttenuationFunction = {});
void setDeviceName(Maybe<String> device); void setDeviceName(Maybe<String> device);
StringList availableDevices(); StringList availableDevices();

View File

@ -35,7 +35,7 @@ void WirePane::reset() {
m_connecting = false; m_connecting = false;
} }
void WirePane::update() { void WirePane::update(float dt) {
if (!active()) if (!active())
return; return;
if (!m_worldClient->inWorld()) { if (!m_worldClient->inWorld()) {

View File

@ -16,7 +16,7 @@ public:
WirePane(WorldClientPtr worldClient, PlayerPtr player, WorldPainterPtr worldPainter); WirePane(WorldClientPtr worldClient, PlayerPtr player, WorldPainterPtr worldPainter);
virtual ~WirePane() {} virtual ~WirePane() {}
virtual void update() override; virtual void update(float dt) override;
virtual bool sendEvent(InputEvent const& event) override; virtual bool sendEvent(InputEvent const& event) override;
virtual SwingResult swing(WorldGeometry const& geometry, Vec2F position, FireMode mode) override; virtual SwingResult swing(WorldGeometry const& geometry, Vec2F position, FireMode mode) override;

View File

@ -720,7 +720,7 @@ void ActorMovementController::clearControls() {
m_controlModifiers = ActorMovementModifiers(); m_controlModifiers = ActorMovementModifiers();
} }
void ActorMovementController::tickMaster() { void ActorMovementController::tickMaster(float dt) {
EntityAnchorConstPtr newAnchor; EntityAnchorConstPtr newAnchor;
if (auto anchorState = m_anchorState.get()) { if (auto anchorState = m_anchorState.get()) {
if (auto anchorableEntity = as<AnchorableEntity>(world()->entity(anchorState->entityId))) if (auto anchorableEntity = as<AnchorableEntity>(world()->entity(anchorState->entityId)))
@ -743,8 +743,8 @@ void ActorMovementController::tickMaster() {
m_groundMovement.set(false); m_groundMovement.set(false);
m_liquidMovement.set(false); m_liquidMovement.set(false);
setVelocity((m_entityAnchor->position - MovementController::position()) / WorldTimestep); setVelocity((m_entityAnchor->position - MovementController::position()) / dt);
MovementController::tickMaster(); MovementController::tickMaster(dt);
setPosition(m_entityAnchor->position); setPosition(m_entityAnchor->position);
} else { } else {
auto activeParameters = m_baseParameters.merge(m_controlParameters); auto activeParameters = m_baseParameters.merge(m_controlParameters);
@ -775,7 +775,7 @@ void ActorMovementController::tickMaster() {
if (appliedForceRegion()) { if (appliedForceRegion()) {
m_pathController->reset(); m_pathController->reset();
} else if (!m_pathController->pathfinding()) { } else if (!m_pathController->pathfinding()) {
m_pathMoveResult = m_pathController->move(*this, activeParameters, activeModifiers, m_controlPathMove->second, WorldTimestep) m_pathMoveResult = m_pathController->move(*this, activeParameters, activeModifiers, m_controlPathMove->second, dt)
.apply([this](bool result) { return pair<Vec2F, bool>(m_controlPathMove->first, result); }); .apply([this](bool result) { return pair<Vec2F, bool>(m_controlPathMove->first, result); });
auto action = m_pathController->curAction(); auto action = m_pathController->curAction();
@ -811,7 +811,7 @@ void ActorMovementController::tickMaster() {
// MovementController still handles updating liquid percentage and updating force regions // MovementController still handles updating liquid percentage and updating force regions
updateLiquidPercentage(); updateLiquidPercentage();
updateForceRegions(); updateForceRegions(dt);
// onGround flag needs to be manually set, won't be set by MovementController::tickMaster // onGround flag needs to be manually set, won't be set by MovementController::tickMaster
setOnGround(onGround); setOnGround(onGround);
clearControls(); clearControls();
@ -894,7 +894,7 @@ void ActorMovementController::tickMaster() {
float minGroundSustain = *activeParameters.groundMovementMinimumSustain; float minGroundSustain = *activeParameters.groundMovementMinimumSustain;
float maxGroundSustain = *activeParameters.groundMovementMaximumSustain; float maxGroundSustain = *activeParameters.groundMovementMaximumSustain;
float groundCheckDistance = *activeParameters.groundMovementCheckDistance; float groundCheckDistance = *activeParameters.groundMovementCheckDistance;
m_groundMovementSustainTimer.tick(); m_groundMovementSustainTimer.tick(dt);
if (onGround()) { if (onGround()) {
m_groundMovementSustainTimer = GameTimer(maxGroundSustain); m_groundMovementSustainTimer = GameTimer(maxGroundSustain);
} else if (!m_groundMovementSustainTimer.ready() && groundCheckDistance > 0.0f && maxGroundSustain - m_groundMovementSustainTimer.timer > minGroundSustain) { } else if (!m_groundMovementSustainTimer.ready() && groundCheckDistance > 0.0f && maxGroundSustain - m_groundMovementSustainTimer.timer > minGroundSustain) {
@ -933,15 +933,15 @@ void ActorMovementController::tickMaster() {
m_groundMovementSustainTimer = GameTimer(0); m_groundMovementSustainTimer = GameTimer(0);
} else if (holdJump) { } else if (holdJump) {
m_reJumpTimer.tick(); m_reJumpTimer.tick(dt);
if (m_jumpHoldTimer) if (m_jumpHoldTimer)
m_jumpHoldTimer->tick(); m_jumpHoldTimer->tick(dt);
approachYVelocity(*jumpProfile.jumpSpeed * jumpModifier, *jumpProfile.jumpControlForce * jumpModifier); approachYVelocity(*jumpProfile.jumpSpeed * jumpModifier, *jumpProfile.jumpControlForce * jumpModifier);
} else { } else {
m_jumping.set(false); m_jumping.set(false);
m_reJumpTimer.tick(); m_reJumpTimer.tick(dt);
} }
if (m_controlMove == Direction::Left) { if (m_controlMove == Direction::Left) {
@ -1003,7 +1003,7 @@ void ActorMovementController::tickMaster() {
bool falling = (yVelocity() < *activeParameters.fallStatusSpeedMin) && !m_groundMovement.get(); bool falling = (yVelocity() < *activeParameters.fallStatusSpeedMin) && !m_groundMovement.get();
m_falling.set(falling); m_falling.set(falling);
MovementController::tickMaster(); MovementController::tickMaster(dt);
m_lastControlJump = m_controlJump; m_lastControlJump = m_controlJump;
m_lastControlDown = m_controlDown; m_lastControlDown = m_controlDown;
@ -1017,8 +1017,8 @@ void ActorMovementController::tickMaster() {
clearControls(); clearControls();
} }
void ActorMovementController::tickSlave() { void ActorMovementController::tickSlave(float dt) {
MovementController::tickSlave(); MovementController::tickSlave(dt);
m_entityAnchor.reset(); m_entityAnchor.reset();
if (auto anchorState = m_anchorState.get()) { if (auto anchorState = m_anchorState.get()) {
@ -1309,7 +1309,7 @@ Maybe<bool> PathController::move(ActorMovementController& movementController, Ac
float angleFactor = movementController.velocity().normalized() * delta.normalized(); float angleFactor = movementController.velocity().normalized() * delta.normalized();
float speedAlongAngle = angleFactor * movementController.velocity().magnitude(); float speedAlongAngle = angleFactor * movementController.velocity().magnitude();
auto acc = parameters.airForce.value(0.0) / movementController.mass(); auto acc = parameters.airForce.value(0.0) / movementController.mass();
sourceVelocity = delta.normalized() * fmin(parameters.flySpeed.value(0.0), speedAlongAngle + acc * WorldTimestep); sourceVelocity = delta.normalized() * fmin(parameters.flySpeed.value(0.0), speedAlongAngle + acc * dt);
targetVelocity = sourceVelocity; targetVelocity = sourceVelocity;
} }
break; break;

View File

@ -245,11 +245,11 @@ public:
// Clears all control data. // Clears all control data.
void clearControls(); void clearControls();
// Integrates the ActorMovementController one WorldTimestep and applies all // Integrates the ActorMovementController and applies all
// the control data and clears it for the next step. // the control data and clears it for the next step.
void tickMaster(); void tickMaster(float dt);
void tickSlave(); void tickSlave(float dt);
private: private:

View File

@ -58,7 +58,7 @@ DataStream& operator>>(DataStream& ds, RemoteDamageNotification& damageNotificat
DamageManager::DamageManager(World* world, ConnectionId connectionId) : m_world(world), m_connectionId(connectionId) {} DamageManager::DamageManager(World* world, ConnectionId connectionId) : m_world(world), m_connectionId(connectionId) {}
void DamageManager::update() { void DamageManager::update(float dt) {
float const DefaultDamageTimeout = 1.0f; float const DefaultDamageTimeout = 1.0f;
auto damageIt = makeSMutableMapIterator(m_recentEntityDamages); auto damageIt = makeSMutableMapIterator(m_recentEntityDamages);
@ -67,7 +67,7 @@ void DamageManager::update() {
auto eventIt = makeSMutableIterator(events); auto eventIt = makeSMutableIterator(events);
while (eventIt.hasNext()) { while (eventIt.hasNext()) {
auto& event = eventIt.next(); auto& event = eventIt.next();
event.timeout -= WorldTimestep; event.timeout -= dt;
auto entityIdTimeoutGroup = event.timeoutGroup.maybe<EntityId>(); auto entityIdTimeoutGroup = event.timeoutGroup.maybe<EntityId>();
if (event.timeout <= 0.0f || (entityIdTimeoutGroup && !m_world->entity(*entityIdTimeoutGroup))) if (event.timeout <= 0.0f || (entityIdTimeoutGroup && !m_world->entity(*entityIdTimeoutGroup)))
eventIt.remove(); eventIt.remove();

View File

@ -48,7 +48,7 @@ public:
// Notify entities that they have caused damage, apply damage to master // Notify entities that they have caused damage, apply damage to master
// entities, produce damage notifications, and run down damage timeouts. // entities, produce damage notifications, and run down damage timeouts.
void update(); void update(float dt);
// Incoming RemoteHitRequest and RemoteDamageRequest must have the // Incoming RemoteHitRequest and RemoteDamageRequest must have the
// destinationConnection equal to the DamageManager's connectionId // destinationConnection equal to the DamageManager's connectionId

View File

@ -30,7 +30,7 @@ void EffectEmitter::setBaseVelocity(Vec2F const& velocity) {
m_baseVelocity = velocity; m_baseVelocity = velocity;
} }
void EffectEmitter::tick(EntityMode mode) { void EffectEmitter::tick(float dt, EntityMode mode) {
if (mode == EntityMode::Master) { if (mode == EntityMode::Master) {
m_activeSources.set(move(m_newSources)); m_activeSources.set(move(m_newSources));
m_newSources.clear(); m_newSources.clear();
@ -42,7 +42,7 @@ void EffectEmitter::tick(EntityMode mode) {
eraseWhere(m_sources, [](EffectSourcePtr const& source) { return source->expired(); }); eraseWhere(m_sources, [](EffectSourcePtr const& source) { return source->expired(); });
for (auto& ps : m_sources) for (auto& ps : m_sources)
ps->tick(); ps->tick(dt);
Set<pair<String, String>> current; Set<pair<String, String>> current;
for (auto& ps : m_sources) { for (auto& ps : m_sources) {

View File

@ -19,7 +19,7 @@ public:
void setDirection(Direction direction); void setDirection(Direction direction);
void setBaseVelocity(Vec2F const& velocity); void setBaseVelocity(Vec2F const& velocity);
void tick(EntityMode mode); void tick(float dt, EntityMode mode);
void reset(); void reset();
void render(RenderCallback* renderCallback); void render(RenderCallback* renderCallback);

View File

@ -72,8 +72,8 @@ void EffectSource::stop() {
m_stop = true; m_stop = true;
} }
void EffectSource::tick() { void EffectSource::tick(float dt) {
m_timer -= WorldTimestep; m_timer -= dt;
if ((m_timer <= 0) && m_loops) { if ((m_timer <= 0) && m_loops) {
m_timer = m_loopDuration + m_durationVariance * Random::randf(-0.5f, 0.5f); m_timer = m_loopDuration + m_durationVariance * Random::randf(-0.5f, 0.5f);
m_loopTick = true; m_loopTick = true;

View File

@ -17,7 +17,7 @@ class EffectSource {
public: public:
EffectSource(String const& kind, String suggestedSpawnLocation, Json const& definition); EffectSource(String const& kind, String suggestedSpawnLocation, Json const& definition);
String const& kind() const; String const& kind() const;
void tick(); void tick(float dt);
bool expired() const; bool expired() const;
void stop(); void stop();
List<String> particles(); List<String> particles();

View File

@ -2,9 +2,8 @@
namespace Star { namespace Star {
float GlobalTimescale = 1.0f;
float WorldTimestep = 1.0f / 60.0f; float WorldTimestep = 1.0f / 60.0f;
// This is used to correct interpolation. It must match the timestep of the server you are connected to.
float ServerWorldTimestep = 1.0f / 60.0f; float ServerWorldTimestep = 1.0f / 60.0f;
EnumMap<Direction> const DirectionNames{ EnumMap<Direction> const DirectionNames{

View File

@ -93,6 +93,7 @@ extern EnumMap<Rarity> const RarityNames;
// distance (one tile). // distance (one tile).
unsigned const TilePixels = 8; unsigned const TilePixels = 8;
extern float GlobalTimescale;
extern float WorldTimestep; extern float WorldTimestep;
extern float ServerWorldTimestep; extern float ServerWorldTimestep;
float const SystemWorldTimestep = 1.0f / 20.0f; float const SystemWorldTimestep = 1.0f / 20.0f;

View File

@ -176,7 +176,7 @@ RectF ItemDrop::collisionArea() const {
return m_boundBox; return m_boundBox;
} }
void ItemDrop::update(uint64_t) { void ItemDrop::update(float dt, uint64_t) {
if (isMaster()) { if (isMaster()) {
if (m_owningEntity.get() != NullEntityId) { if (m_owningEntity.get() != NullEntityId) {
auto owningEntity = world()->entity(m_owningEntity.get()); auto owningEntity = world()->entity(m_owningEntity.get());
@ -228,9 +228,9 @@ void ItemDrop::update(uint64_t) {
m_movementController.applyParameters(parameters); m_movementController.applyParameters(parameters);
} }
m_movementController.tickMaster(); m_movementController.tickMaster(dt);
m_intangibleTimer.tick(WorldTimestep); m_intangibleTimer.tick(dt);
m_dropAge.update(world()->epochTime()); m_dropAge.update(world()->epochTime());
m_ageItemsTimer.update(world()->epochTime()); m_ageItemsTimer.update(world()->epochTime());
@ -252,7 +252,7 @@ void ItemDrop::update(uint64_t) {
} else { } else {
m_netGroup.tickNetInterpolation(WorldTimestep); m_netGroup.tickNetInterpolation(WorldTimestep);
Root::singleton().itemDatabase()->loadItem(m_itemDescriptor.get(), m_item); Root::singleton().itemDatabase()->loadItem(m_itemDescriptor.get(), m_item);
m_movementController.tickSlave(); m_movementController.tickSlave(dt);
} }
} }

View File

@ -52,7 +52,7 @@ public:
RectF collisionArea() const override; RectF collisionArea() const override;
void update(uint64_t currentStep) override; void update(float dt, uint64_t currentStep) override;
bool shouldDestroy() const override; bool shouldDestroy() const override;

View File

@ -441,41 +441,43 @@ void Monster::damagedOther(DamageNotification const& damage) {
m_statusController->damagedOther(damage); m_statusController->damagedOther(damage);
} }
void Monster::update(uint64_t) { void Monster::update(float dt, uint64_t) {
if (!inWorld()) if (!inWorld())
return; return;
m_movementController->setTimestep(dt);
if (isMaster()) { if (isMaster()) {
m_networkedAnimator.setFlipped((m_movementController->facingDirection() == Direction::Left) != m_monsterVariant.reversed); m_networkedAnimator.setFlipped((m_movementController->facingDirection() == Direction::Left) != m_monsterVariant.reversed);
if (m_knockedOut) { if (m_knockedOut) {
m_knockoutTimer -= WorldTimestep; m_knockoutTimer -= dt;
} else { } else {
if (m_scriptComponent.updateReady()) if (m_scriptComponent.updateReady())
m_physicsForces.set({}); m_physicsForces.set({});
m_scriptComponent.update(m_scriptComponent.updateDt()); m_scriptComponent.update(m_scriptComponent.updateDt(dt));
if (shouldDie()) if (shouldDie())
knockout(); knockout();
} }
m_movementController->tickMaster(); m_movementController->tickMaster(dt);
m_statusController->tickMaster(); m_statusController->tickMaster(dt);
updateStatus(); updateStatus(dt);
} else { } else {
m_netGroup.tickNetInterpolation(WorldTimestep); m_netGroup.tickNetInterpolation(WorldTimestep);
m_statusController->tickSlave(); m_statusController->tickSlave(dt);
updateStatus(); updateStatus(dt);
m_movementController->tickSlave(); m_movementController->tickSlave(dt);
} }
if (world()->isServer()) { if (world()->isServer()) {
m_networkedAnimator.update(WorldTimestep, nullptr); m_networkedAnimator.update(dt, nullptr);
} else { } else {
m_networkedAnimator.update(WorldTimestep, &m_networkedAnimatorDynamicTarget); m_networkedAnimator.update(dt, &m_networkedAnimatorDynamicTarget);
m_networkedAnimatorDynamicTarget.updatePosition(position()); m_networkedAnimatorDynamicTarget.updatePosition(position());
m_scriptedAnimator.update(); m_scriptedAnimator.update();
@ -543,12 +545,12 @@ Vec2F Monster::getAbsolutePosition(Vec2F relativePosition) const {
return m_movementController->position() + relativePosition; return m_movementController->position() + relativePosition;
} }
void Monster::updateStatus() { void Monster::updateStatus(float dt) {
m_effectEmitter.setSourcePosition("normal", position()); m_effectEmitter.setSourcePosition("normal", position());
m_effectEmitter.setSourcePosition("mouth", position() + mouthOffset()); m_effectEmitter.setSourcePosition("mouth", position() + mouthOffset());
m_effectEmitter.setSourcePosition("feet", position() + feetOffset()); m_effectEmitter.setSourcePosition("feet", position() + feetOffset());
m_effectEmitter.setDirection(m_movementController->facingDirection()); m_effectEmitter.setDirection(m_movementController->facingDirection());
m_effectEmitter.tick(*entityMode()); m_effectEmitter.tick(dt, *entityMode());
} }
LuaCallbacks Monster::makeMonsterCallbacks() { LuaCallbacks Monster::makeMonsterCallbacks() {

View File

@ -88,7 +88,7 @@ public:
bool shouldDestroy() const override; bool shouldDestroy() const override;
void destroy(RenderCallback* renderCallback) override; void destroy(RenderCallback* renderCallback) override;
void update(uint64_t currentStep) override; void update(float dt, uint64_t currentStep) override;
void render(RenderCallback* renderCallback) override; void render(RenderCallback* renderCallback) override;
@ -137,7 +137,7 @@ public:
private: private:
Vec2F getAbsolutePosition(Vec2F relativePosition) const; Vec2F getAbsolutePosition(Vec2F relativePosition) const;
void updateStatus(); void updateStatus(float dt);
LuaCallbacks makeMonsterCallbacks(); LuaCallbacks makeMonsterCallbacks();
void addChatMessage(String const& message, String const& portrait = ""); void addChatMessage(String const& message, String const& portrait = "");

View File

@ -170,6 +170,8 @@ DataStream& operator<<(DataStream& ds, MovementParameters const& movementParamet
MovementController::MovementController(MovementParameters const& parameters) { MovementController::MovementController(MovementParameters const& parameters) {
m_resting = false; m_resting = false;
m_timeStep = WorldTimestep;
m_liquidPercentage = 0.0f; m_liquidPercentage = 0.0f;
m_liquidId = EmptyLiquidId; m_liquidId = EmptyLiquidId;
@ -392,15 +394,15 @@ void MovementController::rotate(float rotationRate) {
return; return;
m_resting = false; m_resting = false;
m_rotation.set(fmod(rotation() + rotationRate * WorldTimestep, 2 * Constants::pi)); m_rotation.set(fmod(rotation() + rotationRate * m_timeStep, 2 * Constants::pi));
} }
void MovementController::accelerate(Vec2F const& acceleration) { void MovementController::accelerate(Vec2F const& acceleration) {
setVelocity(velocity() + acceleration * WorldTimestep); setVelocity(velocity() + acceleration * m_timeStep);
} }
void MovementController::force(Vec2F const& force) { void MovementController::force(Vec2F const& force) {
setVelocity(velocity() + force / mass() * WorldTimestep); setVelocity(velocity() + force / mass() * m_timeStep);
} }
void MovementController::approachVelocity(Vec2F const& targetVelocity, float maxControlForce) { void MovementController::approachVelocity(Vec2F const& targetVelocity, float maxControlForce) {
@ -414,7 +416,7 @@ void MovementController::approachVelocity(Vec2F const& targetVelocity, float max
if (diffMagnitude == 0.0f) if (diffMagnitude == 0.0f)
return; return;
float maximumAcceleration = maxControlForce / mass() * WorldTimestep; float maximumAcceleration = maxControlForce / mass() * m_timeStep;
float clampedMagnitude = clamp(diffMagnitude, 0.0f, maximumAcceleration); float clampedMagnitude = clamp(diffMagnitude, 0.0f, maximumAcceleration);
setVelocity(velocity() + diff * (clampedMagnitude / diffMagnitude)); setVelocity(velocity() + diff * (clampedMagnitude / diffMagnitude));
@ -437,7 +439,7 @@ void MovementController::approachVelocityAlongAngle(float angle, float targetVel
if (positiveOnly && diff < 0) if (positiveOnly && diff < 0)
return; return;
float maximumAcceleration = maxControlForce / mass() * WorldTimestep; float maximumAcceleration = maxControlForce / mass() * m_timeStep;
float diffMagnitude = std::fabs(diff); float diffMagnitude = std::fabs(diff);
float clampedMagnitude = clamp(diffMagnitude, 0.0f, maximumAcceleration); float clampedMagnitude = clamp(diffMagnitude, 0.0f, maximumAcceleration);
@ -464,7 +466,12 @@ void MovementController::uninit() {
updatePositionInterpolators(); updatePositionInterpolators();
} }
void MovementController::tickMaster() { void MovementController::setTimestep(float dt) {
m_timeStep = dt;
}
void MovementController::tickMaster(float dt) {
setTimestep(dt);
auto geometry = world()->geometry(); auto geometry = world()->geometry();
m_zeroG.set(!*m_parameters.gravityEnabled || *m_parameters.gravityMultiplier == 0 || world()->gravity(position()) == 0); m_zeroG.set(!*m_parameters.gravityEnabled || *m_parameters.gravityMultiplier == 0 || world()->gravity(position()) == 0);
@ -478,7 +485,7 @@ void MovementController::tickMaster() {
if (surfaceCollision) { if (surfaceCollision) {
Vec2F surfacePositionDelta = geometry.diff(surfaceCollision->position, m_surfaceMovingCollisionPosition); Vec2F surfacePositionDelta = geometry.diff(surfaceCollision->position, m_surfaceMovingCollisionPosition);
setPosition(position() + surfacePositionDelta); setPosition(position() + surfacePositionDelta);
Vec2F newSurfaceVelocity = surfacePositionDelta / WorldTimestep; Vec2F newSurfaceVelocity = surfacePositionDelta / dt;
setVelocity(velocity() - m_surfaceVelocity + newSurfaceVelocity); setVelocity(velocity() - m_surfaceVelocity + newSurfaceVelocity);
m_surfaceVelocity = newSurfaceVelocity; m_surfaceVelocity = newSurfaceVelocity;
} else { } else {
@ -496,7 +503,7 @@ void MovementController::tickMaster() {
// don't integrate velocity when resting // don't integrate velocity when resting
Vec2F relativeVelocity = m_resting ? Vec2F() : velocity(); Vec2F relativeVelocity = m_resting ? Vec2F() : velocity();
Vec2F originalMovement = relativeVelocity * WorldTimestep; Vec2F originalMovement = relativeVelocity * dt;
if (surfaceCollision) if (surfaceCollision)
relativeVelocity -= m_surfaceVelocity; relativeVelocity -= m_surfaceVelocity;
@ -507,7 +514,7 @@ void MovementController::tickMaster() {
unsigned steps; unsigned steps;
float maxMovementPerStep = *m_parameters.maxMovementPerStep; float maxMovementPerStep = *m_parameters.maxMovementPerStep;
if (maxMovementPerStep > 0.0f) if (maxMovementPerStep > 0.0f)
steps = std::floor(vmag(relativeVelocity) * WorldTimestep / maxMovementPerStep) + 1; steps = std::floor(vmag(relativeVelocity) * dt / maxMovementPerStep) + 1;
else else
steps = 1; steps = 1;
@ -516,8 +523,8 @@ void MovementController::tickMaster() {
steps = 0; steps = 0;
for (unsigned i = 0; i < steps; ++i) { for (unsigned i = 0; i < steps; ++i) {
float dt = WorldTimestep / steps; float dtSteps = dt / steps;
Vec2F movement = relativeVelocity * dt; Vec2F movement = relativeVelocity * dtSteps;
if (!*m_parameters.collisionEnabled || collisionPoly().isNull()) { if (!*m_parameters.collisionEnabled || collisionPoly().isNull()) {
setPosition(position() + movement); setPosition(position() + movement);
@ -546,7 +553,7 @@ void MovementController::tickMaster() {
queryBounds.combine(queryBounds.translated(movement)); queryBounds.combine(queryBounds.translated(movement));
queryCollisions(queryBounds); queryCollisions(queryBounds);
auto result = collisionMove(m_workingCollisions, body, movement, ignorePlatforms, *m_parameters.enableSurfaceSlopeCorrection && !zeroG(), auto result = collisionMove(m_workingCollisions, body, movement, ignorePlatforms, *m_parameters.enableSurfaceSlopeCorrection && !zeroG(),
maximumCorrection, maximumPlatformCorrection, bodyCenter); maximumCorrection, maximumPlatformCorrection, bodyCenter, dtSteps);
setPosition(position() + result.movement); setPosition(position() + result.movement);
@ -572,7 +579,7 @@ void MovementController::tickMaster() {
if (*m_parameters.stickyCollision && result.collisionKind != CollisionKind::Slippery) { if (*m_parameters.stickyCollision && result.collisionKind != CollisionKind::Slippery) {
// When sticking, cancel all velocity and apply stickyForce in the // When sticking, cancel all velocity and apply stickyForce in the
// opposite of the direction of collision correction. // opposite of the direction of collision correction.
relativeVelocity = -normCorrection * *m_parameters.stickyForce / mass() * dt; relativeVelocity = -normCorrection * *m_parameters.stickyForce / mass() * dtSteps;
m_stickingDirection.set(-normCorrection.angle()); m_stickingDirection.set(-normCorrection.angle());
break; break;
} else { } else {
@ -597,14 +604,14 @@ void MovementController::tickMaster() {
// independently). // independently).
if (relativeVelocity[0] < 0 && correction[0] > 0) if (relativeVelocity[0] < 0 && correction[0] > 0)
relativeVelocity[0] = min(0.0f, relativeVelocity[0] + correction[0] / WorldTimestep); relativeVelocity[0] = min(0.0f, relativeVelocity[0] + correction[0] / dt);
else if (relativeVelocity[0] > 0 && correction[0] < 0) else if (relativeVelocity[0] > 0 && correction[0] < 0)
relativeVelocity[0] = max(0.0f, relativeVelocity[0] + correction[0] / WorldTimestep); relativeVelocity[0] = max(0.0f, relativeVelocity[0] + correction[0] / dt);
if (relativeVelocity[1] < 0 && correction[1] > 0) if (relativeVelocity[1] < 0 && correction[1] > 0)
relativeVelocity[1] = min(0.0f, relativeVelocity[1] + correction[1] / WorldTimestep); relativeVelocity[1] = min(0.0f, relativeVelocity[1] + correction[1] / dt);
else if (relativeVelocity[1] > 0 && correction[1] < 0) else if (relativeVelocity[1] > 0 && correction[1] < 0)
relativeVelocity[1] = max(0.0f, relativeVelocity[1] + correction[1] / WorldTimestep); relativeVelocity[1] = max(0.0f, relativeVelocity[1] + correction[1] / dt);
} }
} }
} }
@ -641,7 +648,7 @@ void MovementController::tickMaster() {
float buoyancy = *m_parameters.liquidBuoyancy * m_liquidPercentage + *m_parameters.airBuoyancy * (1.0f - liquidPercentage()); float buoyancy = *m_parameters.liquidBuoyancy * m_liquidPercentage + *m_parameters.airBuoyancy * (1.0f - liquidPercentage());
float gravity = world()->gravity(position()) * *m_parameters.gravityMultiplier * (1.0f - buoyancy); float gravity = world()->gravity(position()) * *m_parameters.gravityMultiplier * (1.0f - buoyancy);
Vec2F environmentVelocity; Vec2F environmentVelocity;
environmentVelocity[1] -= gravity * WorldTimestep; environmentVelocity[1] -= gravity * dt;
if (onGround() && *m_parameters.slopeSlidingFactor != 0 && m_surfaceSlope[1] != 0) if (onGround() && *m_parameters.slopeSlidingFactor != 0 && m_surfaceSlope[1] != 0)
environmentVelocity += -m_surfaceSlope * (m_surfaceSlope[0] * m_surfaceSlope[1]) * *m_parameters.slopeSlidingFactor; environmentVelocity += -m_surfaceSlope * (m_surfaceSlope[0] * m_surfaceSlope[1]) * *m_parameters.slopeSlidingFactor;
@ -673,16 +680,17 @@ void MovementController::tickMaster() {
// but it is applied here as a multiplicitave factor from [0, 1] so it does // but it is applied here as a multiplicitave factor from [0, 1] so it does
// not induce oscillation at very high friction and so it cannot be // not induce oscillation at very high friction and so it cannot be
// negative. // negative.
float frictionFactor = clamp(friction / mass() * WorldTimestep, 0.0f, 1.0f); float frictionFactor = clamp(friction / mass() * dt, 0.0f, 1.0f);
newVelocity = lerp(frictionFactor, newVelocity, refVel); newVelocity = lerp(frictionFactor, newVelocity, refVel);
} }
setVelocity(newVelocity); setVelocity(newVelocity);
updateForceRegions(); updateForceRegions(dt);
} }
void MovementController::tickSlave() { void MovementController::tickSlave(float dt) {
setTimestep(dt);
if (auto movingCollisionId = m_surfaceMovingCollision.get()) { if (auto movingCollisionId = m_surfaceMovingCollision.get()) {
if (auto physicsEntity = world()->get<PhysicsEntity>(movingCollisionId->physicsEntityId)) { if (auto physicsEntity = world()->get<PhysicsEntity>(movingCollisionId->physicsEntityId)) {
if (auto collision = physicsEntity->movingCollision(movingCollisionId->collisionIndex)) { if (auto collision = physicsEntity->movingCollision(movingCollisionId->collisionIndex)) {
@ -724,7 +732,7 @@ void MovementController::forEachMovingCollision(RectF const& region, function<bo
} }
} }
void MovementController::updateForceRegions() { void MovementController::updateForceRegions(float dt) {
auto geometry = world()->geometry(); auto geometry = world()->geometry();
auto pos = position(); auto pos = position();
auto body = collisionBody(); auto body = collisionBody();
@ -841,11 +849,12 @@ CollisionKind MovementController::maxOrNullCollision(CollisionKind a, CollisionK
} }
MovementController::CollisionResult MovementController::collisionMove(List<CollisionPoly>& collisionPolys, PolyF const& body, Vec2F const& movement, MovementController::CollisionResult MovementController::collisionMove(List<CollisionPoly>& collisionPolys, PolyF const& body, Vec2F const& movement,
bool ignorePlatforms, bool enableSurfaceSlopeCorrection, float maximumCorrection, float maximumPlatformCorrection, Vec2F sortCenter) { bool ignorePlatforms, bool enableSurfaceSlopeCorrection, float maximumCorrection, float maximumPlatformCorrection, Vec2F sortCenter, float dt) {
unsigned const MaximumSeparationLoops = 3; unsigned const MaximumSeparationLoops = 3;
float const SlideAngle = Constants::pi / 3; float const SlideAngle = Constants::pi / 3;
float const SlideCorrectionLimit = 0.2f; float const SlideCorrectionLimit = 0.2f;
float const SeparationTolerance = 0.001f; float separationTolerance = 0.001f * (dt * 60.f);
maximumPlatformCorrection *= (dt * 60.f);
if (body.isNull()) if (body.isNull())
return {movement, Vec2F(), {}, false, false, Vec2F(1, 0), CollisionKind::None}; return {movement, Vec2F(), {}, false, false, Vec2F(1, 0), CollisionKind::None};
@ -861,7 +870,7 @@ MovementController::CollisionResult MovementController::collisionMove(List<Colli
if (enableSurfaceSlopeCorrection) { if (enableSurfaceSlopeCorrection) {
// First try separating with our ground sliding cheat. // First try separating with our ground sliding cheat.
separation = collisionSeparate(collisionPolys, checkBody, ignorePlatforms, maximumPlatformCorrection, sortCenter, true, SeparationTolerance); separation = collisionSeparate(collisionPolys, checkBody, ignorePlatforms, maximumPlatformCorrection, sortCenter, true, separationTolerance);
totalCorrection += separation.correction; totalCorrection += separation.correction;
checkBody.translate(separation.correction); checkBody.translate(separation.correction);
maxCollided = maxOrNullCollision(maxCollided, separation.collisionKind); maxCollided = maxOrNullCollision(maxCollided, separation.collisionKind);
@ -889,7 +898,7 @@ MovementController::CollisionResult MovementController::collisionMove(List<Colli
totalCorrection = Vec2F(); totalCorrection = Vec2F();
for (size_t i = 0; i < MaximumSeparationLoops; ++i) { for (size_t i = 0; i < MaximumSeparationLoops; ++i) {
separation = collisionSeparate(collisionPolys, checkBody, ignorePlatforms, separation = collisionSeparate(collisionPolys, checkBody, ignorePlatforms,
maximumPlatformCorrection, sortCenter, false, SeparationTolerance); maximumPlatformCorrection, sortCenter, false, separationTolerance);
totalCorrection += separation.correction; totalCorrection += separation.correction;
checkBody.translate(separation.correction); checkBody.translate(separation.correction);
maxCollided = maxOrNullCollision(maxCollided, separation.collisionKind); maxCollided = maxOrNullCollision(maxCollided, separation.collisionKind);
@ -911,7 +920,7 @@ MovementController::CollisionResult MovementController::collisionMove(List<Colli
checkBody = body; checkBody = body;
totalCorrection = -movement; totalCorrection = -movement;
for (size_t i = 0; i < MaximumSeparationLoops; ++i) { for (size_t i = 0; i < MaximumSeparationLoops; ++i) {
separation = collisionSeparate(collisionPolys, checkBody, true, maximumPlatformCorrection, sortCenter, false, SeparationTolerance); separation = collisionSeparate(collisionPolys, checkBody, true, maximumPlatformCorrection, sortCenter, false, separationTolerance);
totalCorrection += separation.correction; totalCorrection += separation.correction;
checkBody.translate(separation.correction); checkBody.translate(separation.correction);
maxCollided = maxOrNullCollision(maxCollided, separation.collisionKind); maxCollided = maxOrNullCollision(maxCollided, separation.collisionKind);
@ -931,7 +940,7 @@ MovementController::CollisionResult MovementController::collisionMove(List<Colli
result.movement = movement + totalCorrection; result.movement = movement + totalCorrection;
result.correction = totalCorrection; result.correction = totalCorrection;
result.isStuck = false; result.isStuck = false;
result.onGround = result.correction[1] > SeparationTolerance; result.onGround = result.correction[1] > separationTolerance;
result.surfaceMovingCollisionId = surfaceMovingCollisionId; result.surfaceMovingCollisionId = surfaceMovingCollisionId;
result.collisionKind = maxCollided; result.collisionKind = maxCollided;
result.groundSlope = Vec2F(1, 0); result.groundSlope = Vec2F(1, 0);
@ -944,7 +953,7 @@ MovementController::CollisionResult MovementController::collisionMove(List<Colli
// geometry, rather than off to the side. // geometry, rather than off to the side.
float maxSideHorizontalOverlap = 0.0f; float maxSideHorizontalOverlap = 0.0f;
RectF touchingBounds = checkBody.boundBox(); RectF touchingBounds = checkBody.boundBox();
touchingBounds.pad(SeparationTolerance); touchingBounds.pad(separationTolerance);
for (auto const& cp : collisionPolys) { for (auto const& cp : collisionPolys) {
if (!cp.polyBounds.intersects(touchingBounds)) if (!cp.polyBounds.intersects(touchingBounds))
continue; continue;
@ -959,7 +968,7 @@ MovementController::CollisionResult MovementController::collisionMove(List<Colli
float t = clamp(side.lineProjection(bodyVertex), 0.0f, 1.0f); float t = clamp(side.lineProjection(bodyVertex), 0.0f, 1.0f);
Vec2F nearPoint = side.eval(t); Vec2F nearPoint = side.eval(t);
if (nearPoint[1] > cp.sortPosition[1]) { if (nearPoint[1] > cp.sortPosition[1]) {
if (vmagSquared(bodyVertex - nearPoint) <= square(SeparationTolerance)) { if (vmagSquared(bodyVertex - nearPoint) <= square(separationTolerance)) {
maxSideHorizontalOverlap = thisSideHorizontalOverlap; maxSideHorizontalOverlap = thisSideHorizontalOverlap;
result.groundSlope = side.diff().normalized(); result.groundSlope = side.diff().normalized();
if (result.groundSlope[0] < 0) if (result.groundSlope[0] < 0)
@ -981,8 +990,6 @@ MovementController::CollisionResult MovementController::collisionMove(List<Colli
MovementController::CollisionSeparation MovementController::collisionSeparate(List<CollisionPoly>& collisionPolys, PolyF const& poly, MovementController::CollisionSeparation MovementController::collisionSeparate(List<CollisionPoly>& collisionPolys, PolyF const& poly,
bool ignorePlatforms, float maximumPlatformCorrection, Vec2F const& sortCenter, bool upward, float separationTolerance) { bool ignorePlatforms, float maximumPlatformCorrection, Vec2F const& sortCenter, bool upward, float separationTolerance) {
maximumPlatformCorrection *= WorldTimestep * 60.0f;
CollisionSeparation separation = {}; CollisionSeparation separation = {};
separation.collisionKind = CollisionKind::None; separation.collisionKind = CollisionKind::None;
bool intersects = false; bool intersects = false;

View File

@ -180,13 +180,16 @@ public:
void init(World* world); void init(World* world);
void uninit(); void uninit();
// Stores dt value for Lua calls.
void setTimestep(float dt);
// Integrates the ActorMovementController one WorldTimestep and applies all // Integrates the ActorMovementController one WorldTimestep and applies all
// forces. // forces.
void tickMaster(); void tickMaster(float dt);
// Does not integrate, only tracks master state and updates non-networked // Does not integrate, only tracks master state and updates non-networked
// fields based on local data // fields based on local data
void tickSlave(); void tickSlave(float dt);
void setIgnorePhysicsEntities(Set<EntityId> ignorePhysicsEntities); void setIgnorePhysicsEntities(Set<EntityId> ignorePhysicsEntities);
// iterate over all physics entity collision polys in the region, iteration stops if the callback returns false // iterate over all physics entity collision polys in the region, iteration stops if the callback returns false
@ -194,7 +197,7 @@ public:
protected: protected:
// forces the movement controller onGround status, used when manually controlling movement outside the movement controller // forces the movement controller onGround status, used when manually controlling movement outside the movement controller
void updateForceRegions(); void updateForceRegions(float dt);
void updateLiquidPercentage(); void updateLiquidPercentage();
void setOnGround(bool onGround); void setOnGround(bool onGround);
@ -239,7 +242,7 @@ private:
static CollisionKind maxOrNullCollision(CollisionKind a, CollisionKind b); static CollisionKind maxOrNullCollision(CollisionKind a, CollisionKind b);
static CollisionResult collisionMove(List<CollisionPoly>& collisionPolys, PolyF const& body, Vec2F const& movement, static CollisionResult collisionMove(List<CollisionPoly>& collisionPolys, PolyF const& body, Vec2F const& movement,
bool ignorePlatforms, bool enableSurfaceSlopeCorrection, float maximumCorrection, float maximumPlatformCorrection, Vec2F sortCenter); bool ignorePlatforms, bool enableSurfaceSlopeCorrection, float maximumCorrection, float maximumPlatformCorrection, Vec2F sortCenter, float dt);
static CollisionSeparation collisionSeparate(List<CollisionPoly>& collisionPolys, PolyF const& poly, static CollisionSeparation collisionSeparate(List<CollisionPoly>& collisionPolys, PolyF const& poly,
bool ignorePlatforms, float maximumPlatformCorrection, Vec2F const& sortCenter, bool upward, float separationTolerance); bool ignorePlatforms, float maximumPlatformCorrection, Vec2F const& sortCenter, bool upward, float separationTolerance);
@ -287,6 +290,7 @@ private:
bool m_resting; bool m_resting;
int m_restTicks; int m_restTicks;
float m_timeStep;
List<CollisionPoly> m_workingCollisions; List<CollisionPoly> m_workingCollisions;
List<PolyF> m_collisionBuffers; List<PolyF> m_collisionBuffers;

View File

@ -345,12 +345,14 @@ void Npc::damagedOther(DamageNotification const& damage) {
m_statusController->damagedOther(damage); m_statusController->damagedOther(damage);
} }
void Npc::update(uint64_t) { void Npc::update(float dt, uint64_t) {
if (!inWorld()) if (!inWorld())
return; return;
m_movementController->setTimestep(dt);
if (isMaster()) { if (isMaster()) {
m_scriptComponent.update(m_scriptComponent.updateDt()); m_scriptComponent.update(m_scriptComponent.updateDt(dt));
if (inConflictingLoungeAnchor()) if (inConflictingLoungeAnchor())
m_movementController->resetAnchorState(); m_movementController->resetAnchorState();
@ -385,10 +387,10 @@ void Npc::update(uint64_t) {
m_statusController->setPersistentEffects("armor", m_armor->statusEffects()); m_statusController->setPersistentEffects("armor", m_armor->statusEffects());
m_statusController->setPersistentEffects("tools", m_tools->statusEffects()); m_statusController->setPersistentEffects("tools", m_tools->statusEffects());
m_movementController->tickMaster(); m_movementController->tickMaster(dt);
m_statusController->tickMaster(); m_statusController->tickMaster(dt);
tickShared(); tickShared(dt);
if (!is<LoungeAnchor>(m_movementController->entityAnchor())) { if (!is<LoungeAnchor>(m_movementController->entityAnchor())) {
if (m_movementController->groundMovement()) { if (m_movementController->groundMovement()) {
@ -413,9 +415,9 @@ void Npc::update(uint64_t) {
} }
} }
if (m_emoteCooldownTimer.tick()) if (m_emoteCooldownTimer.tick(dt))
m_emoteState = HumanoidEmote::Idle; m_emoteState = HumanoidEmote::Idle;
if (m_danceCooldownTimer.tick()) if (m_danceCooldownTimer.tick(dt))
m_dance = {}; m_dance = {};
if (m_chatMessageUpdated) { if (m_chatMessageUpdated) {
@ -425,7 +427,7 @@ void Npc::update(uint64_t) {
m_chatMessageUpdated = false; m_chatMessageUpdated = false;
} }
if (m_blinkCooldownTimer.tick()) { if (m_blinkCooldownTimer.tick(dt)) {
m_blinkCooldownTimer = GameTimer(Random::randf(m_blinkInterval[0], m_blinkInterval[1])); m_blinkCooldownTimer = GameTimer(Random::randf(m_blinkInterval[0], m_blinkInterval[1]));
if (m_emoteState == HumanoidEmote::Idle) if (m_emoteState == HumanoidEmote::Idle)
addEmote(HumanoidEmote::Blink); addEmote(HumanoidEmote::Blink);
@ -436,10 +438,10 @@ void Npc::update(uint64_t) {
} else { } else {
m_netGroup.tickNetInterpolation(WorldTimestep); m_netGroup.tickNetInterpolation(WorldTimestep);
m_movementController->tickSlave(); m_movementController->tickSlave(dt);
m_statusController->tickSlave(); m_statusController->tickSlave(dt);
tickShared(); tickShared(dt);
} }
if (world()->isClient()) if (world()->isClient())
@ -534,7 +536,7 @@ Vec2F Npc::getAbsolutePosition(Vec2F relativePosition) const {
return m_movementController->position() + relativePosition; return m_movementController->position() + relativePosition;
} }
void Npc::tickShared() { void Npc::tickShared(float dt) {
if (m_hitDamageNotificationLimiter) if (m_hitDamageNotificationLimiter)
m_hitDamageNotificationLimiter--; m_hitDamageNotificationLimiter--;
@ -547,7 +549,7 @@ void Npc::tickShared() {
m_effectEmitter->setSourcePosition("backArmor", backArmorOffset() + position()); m_effectEmitter->setSourcePosition("backArmor", backArmorOffset() + position());
m_effectEmitter->setDirection(m_humanoid.facingDirection()); m_effectEmitter->setDirection(m_humanoid.facingDirection());
m_effectEmitter->tick(*entityMode()); m_effectEmitter->tick(dt, *entityMode());
m_humanoid.setMovingBackwards(m_movementController->movingDirection() != m_movementController->facingDirection()); m_humanoid.setMovingBackwards(m_movementController->movingDirection() != m_movementController->facingDirection());
m_humanoid.setFacingDirection(m_movementController->facingDirection()); m_humanoid.setFacingDirection(m_movementController->facingDirection());
@ -575,12 +577,12 @@ void Npc::tickShared() {
m_armor->setupHumanoidClothingDrawables(m_humanoid, false); m_armor->setupHumanoidClothingDrawables(m_humanoid, false);
m_tools->suppressItems(!canUseTool()); m_tools->suppressItems(!canUseTool());
m_tools->tick(m_shifting.get(), {}); m_tools->tick(dt, m_shifting.get(), {});
if (auto overrideDirection = m_tools->setupHumanoidHandItems(m_humanoid, position(), aimPosition())) if (auto overrideDirection = m_tools->setupHumanoidHandItems(m_humanoid, position(), aimPosition()))
m_movementController->controlFace(*overrideDirection); m_movementController->controlFace(*overrideDirection);
m_humanoid.animate(WorldTimestep); m_humanoid.animate(dt);
} }
LuaCallbacks Npc::makeNpcCallbacks() { LuaCallbacks Npc::makeNpcCallbacks() {

View File

@ -90,7 +90,7 @@ public:
bool shouldDestroy() const override; bool shouldDestroy() const override;
void destroy(RenderCallback* renderCallback) override; void destroy(RenderCallback* renderCallback) override;
void update(uint64_t currentVersion) override; void update(float dt, uint64_t currentVersion) override;
void render(RenderCallback* renderCallback) override; void render(RenderCallback* renderCallback) override;
@ -177,7 +177,7 @@ public:
private: private:
Vec2F getAbsolutePosition(Vec2F relativePosition) const; Vec2F getAbsolutePosition(Vec2F relativePosition) const;
void tickShared(); void tickShared(float dt);
LuaCallbacks makeNpcCallbacks(); LuaCallbacks makeNpcCallbacks();
void setupNetStates(); void setupNetStates();

View File

@ -352,12 +352,12 @@ List<Vec2I> Object::roots() const {
return {}; return {};
} }
void Object::update(uint64_t) { void Object::update(float dt, uint64_t) {
if (!inWorld()) if (!inWorld())
return; return;
if (isMaster()) { if (isMaster()) {
m_tileDamageStatus->recover(m_config->tileDamageParameters, WorldTimestep); m_tileDamageStatus->recover(m_config->tileDamageParameters, dt);
if (m_liquidCheckTimer.wrapTick()) if (m_liquidCheckTimer.wrapTick())
checkLiquidBroken(); checkLiquidBroken();
@ -369,24 +369,24 @@ void Object::update(uint64_t) {
setImageKey("frame", toString(frame)); setImageKey("frame", toString(frame));
} }
m_animationTimer = std::fmod(m_animationTimer + WorldTimestep, orientation->animationCycle); m_animationTimer = std::fmod(m_animationTimer + dt, orientation->animationCycle);
} }
m_networkedAnimator->update(WorldTimestep, nullptr); m_networkedAnimator->update(dt, nullptr);
m_networkedAnimator->setFlipped(direction() == Direction::Left, m_animationCenterLine); m_networkedAnimator->setFlipped(direction() == Direction::Left, m_animationCenterLine);
m_scriptComponent.update(m_scriptComponent.updateDt()); m_scriptComponent.update(m_scriptComponent.updateDt(dt));
} else { } else {
m_networkedAnimator->update(WorldTimestep, &m_networkedAnimatorDynamicTarget); m_networkedAnimator->update(dt, &m_networkedAnimatorDynamicTarget);
m_networkedAnimatorDynamicTarget.updatePosition(position() + m_animationPosition); m_networkedAnimatorDynamicTarget.updatePosition(position() + m_animationPosition);
} }
if (m_lightFlickering) if (m_lightFlickering)
m_lightFlickering->update(WorldTimestep); m_lightFlickering->update(dt);
for (auto& timer : m_emissionTimers) for (auto& timer : m_emissionTimers)
timer.tick(); timer.tick(dt);
if (world()->isClient()) if (world()->isClient())
m_scriptedAnimator.update(); m_scriptedAnimator.update();

View File

@ -62,7 +62,7 @@ public:
virtual bool shouldDestroy() const override; virtual bool shouldDestroy() const override;
virtual void destroy(RenderCallback* renderCallback) override; virtual void destroy(RenderCallback* renderCallback) override;
virtual void update(uint64_t currentStep) override; virtual void update(float dt, uint64_t currentStep) override;
virtual void render(RenderCallback* renderCallback) override; virtual void render(RenderCallback* renderCallback) override;

View File

@ -718,18 +718,18 @@ float Plant::branchRotation(float xPos, float rotoffset) const {
return copysign(0.00117f, m_windLevel) * (std::sin(m_windTime + rotoffset + xPos / 10.0f) * intensity - intensity / 300.0f); return copysign(0.00117f, m_windLevel) * (std::sin(m_windTime + rotoffset + xPos / 10.0f) * intensity - intensity / 300.0f);
} }
void Plant::update(uint64_t) { void Plant::update(float dt, uint64_t) {
m_windTime += WorldTimestep; m_windTime += dt;
m_windTime = std::fmod(m_windTime, 628.32f); m_windTime = std::fmod(m_windTime, 628.32f);
m_windLevel = world()->windLevel(Vec2F(m_tilePosition)); m_windLevel = world()->windLevel(Vec2F(m_tilePosition));
if (isMaster()) { if (isMaster()) {
if (m_tileDamageStatus.damaged()) if (m_tileDamageStatus.damaged())
m_tileDamageStatus.recover(m_tileDamageParameters, WorldTimestep); m_tileDamageStatus.recover(m_tileDamageParameters, dt);
} else { } else {
if (m_tileDamageStatus.damaged() && !m_tileDamageStatus.damageProtected()) { if (m_tileDamageStatus.damaged() && !m_tileDamageStatus.damageProtected()) {
float damageEffectPercentage = m_tileDamageStatus.damageEffectPercentage(); float damageEffectPercentage = m_tileDamageStatus.damageEffectPercentage();
m_windTime += damageEffectPercentage * 10 * WorldTimestep; m_windTime += damageEffectPercentage * 10 * dt;
m_windLevel += damageEffectPercentage * 20; m_windLevel += damageEffectPercentage * 20;
} }

View File

@ -96,7 +96,7 @@ public:
// Root blocks for this plant. // Root blocks for this plant.
List<Vec2I> roots() const override; List<Vec2I> roots() const override;
void update(uint64_t currentStep) override; void update(float dt, uint64_t currentStep) override;
void render(RenderCallback* renderCallback) override; void render(RenderCallback* renderCallback) override;

View File

@ -176,9 +176,10 @@ RectF PlantDrop::collisionRect() const {
return shape.boundBox(); return shape.boundBox();
} }
void PlantDrop::update(uint64_t) { void PlantDrop::update(float dt, uint64_t) {
m_time -= WorldTimestep; m_time -= dt;
m_movementController.setTimestep(dt);
if (isMaster()) { if (isMaster()) {
if (m_spawnedDropEffects && !m_spawnedDrops.get()) if (m_spawnedDropEffects && !m_spawnedDrops.get())
m_spawnedDropEffects = false; // false positive assumption over already having done the effect m_spawnedDropEffects = false; // false positive assumption over already having done the effect
@ -187,7 +188,7 @@ void PlantDrop::update(uint64_t) {
m_firstTick = false; m_firstTick = false;
// think up a better curve then sin // think up a better curve then sin
auto rotationAcceleration = 0.01f * world()->gravity(position()) * copysign(1.0f, m_rotationRate) * WorldTimestep; auto rotationAcceleration = 0.01f * world()->gravity(position()) * copysign(1.0f, m_rotationRate) * dt;
if (abs(m_movementController.rotation()) > m_rotationCap) if (abs(m_movementController.rotation()) > m_rotationCap)
m_rotationRate -= rotationAcceleration; m_rotationRate -= rotationAcceleration;
else if (std::fabs(m_movementController.rotation()) < m_rotationFallThreshold) else if (std::fabs(m_movementController.rotation()) < m_rotationFallThreshold)
@ -203,7 +204,7 @@ void PlantDrop::update(uint64_t) {
parameters.gravityEnabled = std::fabs(m_movementController.rotation()) >= m_rotationFallThreshold; parameters.gravityEnabled = std::fabs(m_movementController.rotation()) >= m_rotationFallThreshold;
m_movementController.applyParameters(parameters); m_movementController.applyParameters(parameters);
m_movementController.tickMaster(); m_movementController.tickMaster(dt);
if (m_movementController.onGround()) if (m_movementController.onGround())
m_time = 0; m_time = 0;
} }
@ -243,7 +244,7 @@ void PlantDrop::update(uint64_t) {
if (m_spawnedDrops.get()) if (m_spawnedDrops.get())
m_firstTick = false; m_firstTick = false;
m_movementController.tickSlave(); m_movementController.tickSlave(dt);
} }
} }

View File

@ -44,7 +44,7 @@ public:
RectF collisionRect() const; RectF collisionRect() const;
void update(uint64_t currentStep) override; void update(float dt, uint64_t currentStep) override;
void render(RenderCallback* renderCallback) override; void render(RenderCallback* renderCallback) override;

View File

@ -153,6 +153,7 @@ Player::Player(PlayerConfigPtr config, Uuid uuid) {
m_statusController->resetAllResources(); m_statusController->resetAllResources();
m_landingNoisePending = false; m_landingNoisePending = false;
m_footstepPending = false;
setKeepAlive(true); setKeepAlive(true);
@ -776,10 +777,12 @@ Maybe<Json> Player::receiveMessage(ConnectionId fromConnection, String const& me
return {}; return {};
} }
void Player::update(uint64_t) { void Player::update(float dt, uint64_t) {
m_movementController->setTimestep(dt);
if (isMaster()) { if (isMaster()) {
if (m_emoteCooldownTimer) { if (m_emoteCooldownTimer) {
m_emoteCooldownTimer -= WorldTimestep; m_emoteCooldownTimer -= dt;
if (m_emoteCooldownTimer <= 0) { if (m_emoteCooldownTimer <= 0) {
m_emoteCooldownTimer = 0; m_emoteCooldownTimer = 0;
m_emoteState = HumanoidEmote::Idle; m_emoteState = HumanoidEmote::Idle;
@ -793,7 +796,7 @@ void Player::update(uint64_t) {
m_chatMessageUpdated = false; m_chatMessageUpdated = false;
} }
m_blinkCooldownTimer -= WorldTimestep; m_blinkCooldownTimer -= dt;
if (m_blinkCooldownTimer <= 0) { if (m_blinkCooldownTimer <= 0) {
m_blinkCooldownTimer = Random::randf(m_blinkInterval[0], m_blinkInterval[1]); m_blinkCooldownTimer = Random::randf(m_blinkInterval[0], m_blinkInterval[1]);
auto loungeAnchor = as<LoungeAnchor>(m_movementController->entityAnchor()); auto loungeAnchor = as<LoungeAnchor>(m_movementController->entityAnchor());
@ -801,13 +804,13 @@ void Player::update(uint64_t) {
addEmote(HumanoidEmote::Blink); addEmote(HumanoidEmote::Blink);
} }
m_lastDamagedOtherTimer += WorldTimestep; m_lastDamagedOtherTimer += dt;
if (m_movementController->zeroG()) if (m_movementController->zeroG())
m_movementController->controlParameters(m_zeroGMovementParameters); m_movementController->controlParameters(m_zeroGMovementParameters);
if (isTeleporting()) { if (isTeleporting()) {
m_teleportTimer -= WorldTimestep; m_teleportTimer -= dt;
if (m_teleportTimer <= 0 && m_state == State::TeleportIn) { if (m_teleportTimer <= 0 && m_state == State::TeleportIn) {
m_state = State::Idle; m_state = State::Idle;
m_effectsAnimator->burstParticleEmitter(m_teleportAnimationType + "Burst"); m_effectsAnimator->burstParticleEmitter(m_teleportAnimationType + "Burst");
@ -817,9 +820,9 @@ void Player::update(uint64_t) {
if (!isTeleporting()) { if (!isTeleporting()) {
processControls(); processControls();
m_questManager->update(); m_questManager->update(dt);
m_companions->update(); m_companions->update(dt);
m_deployment->update(); m_deployment->update(dt);
bool edgeTriggeredUse = take(m_edgeTriggeredUse); bool edgeTriggeredUse = take(m_edgeTriggeredUse);
@ -871,12 +874,12 @@ void Player::update(uint64_t) {
m_tools->effects(*m_effectEmitter); m_tools->effects(*m_effectEmitter);
m_movementController->tickMaster(); m_movementController->tickMaster(dt);
m_techController->tickMaster(); m_techController->tickMaster(dt);
for (auto& p : m_genericScriptContexts) for (auto& p : m_genericScriptContexts)
p.second->update(WorldTimestep * p.second->updateDelta()); p.second->update(p.second->updateDt(dt));
if (edgeTriggeredUse) { if (edgeTriggeredUse) {
auto anchor = as<LoungeAnchor>(m_movementController->entityAnchor()); auto anchor = as<LoungeAnchor>(m_movementController->entityAnchor());
@ -896,7 +899,7 @@ void Player::update(uint64_t) {
m_techController->setLoadedTech(m_techs->equippedTechs().values()); m_techController->setLoadedTech(m_techs->equippedTechs().values());
if (!isDead()) if (!isDead())
m_statusController->tickMaster(); m_statusController->tickMaster(dt);
if (!modeConfig().hunger) if (!modeConfig().hunger)
m_statusController->resetResource("food"); m_statusController->resetResource("food");
@ -909,7 +912,7 @@ void Player::update(uint64_t) {
m_statusController->setPersistentEffects("hunger", {}); m_statusController->setPersistentEffects("hunger", {});
for (auto& pair : m_delayedRadioMessages) { for (auto& pair : m_delayedRadioMessages) {
if (pair.first.tick()) if (pair.first.tick(dt))
queueRadioMessage(pair.second); queueRadioMessage(pair.second);
} }
m_delayedRadioMessages.filter([](pair<GameTimer, RadioMessage>& pair) { return !pair.first.ready(); }); m_delayedRadioMessages.filter([](pair<GameTimer, RadioMessage>& pair) { return !pair.first.ready(); });
@ -924,7 +927,7 @@ void Player::update(uint64_t) {
m_log->addPlayTime(WorldTimestep); m_log->addPlayTime(WorldTimestep);
if (m_ageItemsTimer.wrapTick(WorldTimestep)) { if (m_ageItemsTimer.wrapTick(dt)) {
auto itemDatabase = Root::singleton().itemDatabase(); auto itemDatabase = Root::singleton().itemDatabase();
m_inventory->forEveryItem([&](InventorySlot const&, ItemPtr& item) { m_inventory->forEveryItem([&](InventorySlot const&, ItemPtr& item) {
itemDatabase->ageItem(item, m_ageItemsTimer.time); itemDatabase->ageItem(item, m_ageItemsTimer.time);
@ -948,9 +951,9 @@ void Player::update(uint64_t) {
} else { } else {
m_netGroup.tickNetInterpolation(WorldTimestep); m_netGroup.tickNetInterpolation(WorldTimestep);
m_movementController->tickSlave(); m_movementController->tickSlave(dt);
m_techController->tickSlave(); m_techController->tickSlave(dt);
m_statusController->tickSlave(); m_statusController->tickSlave(dt);
} }
m_humanoid->setMovingBackwards(false); m_humanoid->setMovingBackwards(false);
@ -967,7 +970,7 @@ void Player::update(uint64_t) {
m_armor->setupHumanoidClothingDrawables(*m_humanoid, forceNude()); m_armor->setupHumanoidClothingDrawables(*m_humanoid, forceNude());
m_tools->suppressItems(!canUseTool()); m_tools->suppressItems(!canUseTool());
m_tools->tick(m_shifting, m_pendingMoves); m_tools->tick(dt, m_shifting, m_pendingMoves);
if (auto overrideFacingDirection = m_tools->setupHumanoidHandItems(*m_humanoid, position(), aimPosition())) if (auto overrideFacingDirection = m_tools->setupHumanoidHandItems(*m_humanoid, position(), aimPosition()))
m_movementController->controlFace(*overrideFacingDirection); m_movementController->controlFace(*overrideFacingDirection);
@ -976,17 +979,22 @@ void Player::update(uint64_t) {
if (m_movementController->facingDirection() == Direction::Left) if (m_movementController->facingDirection() == Direction::Left)
m_effectsAnimator->scaleTransformationGroup("flip", Vec2F(-1, 1)); m_effectsAnimator->scaleTransformationGroup("flip", Vec2F(-1, 1));
if (m_state == State::Walk || m_state == State::Run) {
if ((m_footstepTimer += dt) > m_config->footstepTiming) {
m_footstepPending = true;
m_footstepTimer = 0.0;
}
}
if (isClient) { if (isClient) {
m_effectsAnimator->update(WorldTimestep, &m_effectsAnimatorDynamicTarget); m_effectsAnimator->update(dt, &m_effectsAnimatorDynamicTarget);
m_effectsAnimatorDynamicTarget.updatePosition(position() + m_techController->parentOffset()); m_effectsAnimatorDynamicTarget.updatePosition(position() + m_techController->parentOffset());
} else { } else {
m_effectsAnimator->update(WorldTimestep, nullptr); m_effectsAnimator->update(dt, nullptr);
} }
if (!isTeleporting()) if (!isTeleporting())
processStateChanges(); processStateChanges(dt);
m_damageSources = m_tools->damageSources(); m_damageSources = m_tools->damageSources();
for (auto& damageSource : m_damageSources) { for (auto& damageSource : m_damageSources) {
@ -1009,7 +1017,7 @@ void Player::update(uint64_t) {
m_effectEmitter->setDirection(facingDirection()); m_effectEmitter->setDirection(facingDirection());
m_effectEmitter->tick(*entityMode()); m_effectEmitter->tick(dt, *entityMode());
m_humanoid->setFacingDirection(m_movementController->facingDirection()); m_humanoid->setFacingDirection(m_movementController->facingDirection());
m_humanoid->setMovingBackwards(m_movementController->facingDirection() != m_movementController->movingDirection()); m_humanoid->setMovingBackwards(m_movementController->facingDirection() != m_movementController->movingDirection());
@ -1049,19 +1057,16 @@ void Player::render(RenderCallback* renderCallback) {
renderCallback->addAudio(move(landingNoise)); renderCallback->addAudio(move(landingNoise));
} }
if (m_state == State::Walk || m_state == State::Run) { if (m_footstepPending) {
m_footstepTimer += WorldTimestep;
if (m_footstepTimer > m_config->footstepTiming) {
auto stepNoise = make_shared<AudioInstance>(*footstepAudio); auto stepNoise = make_shared<AudioInstance>(*footstepAudio);
stepNoise->setPosition(position() + feetOffset()); stepNoise->setPosition(position() + feetOffset());
stepNoise->setVolume(1 - Random::randf(0, m_footstepVolumeVariance)); stepNoise->setVolume(1 - Random::randf(0, m_footstepVolumeVariance));
renderCallback->addAudio(move(stepNoise)); renderCallback->addAudio(move(stepNoise));
m_footstepTimer = 0.0;
}
} }
} else { } else {
m_footstepTimer = m_config->footstepTiming; m_footstepTimer = m_config->footstepTiming;
} }
m_footstepPending = false;
m_landingNoisePending = false; m_landingNoisePending = false;
renderCallback->addAudios(m_effectsAnimatorDynamicTarget.pullNewAudios()); renderCallback->addAudios(m_effectsAnimatorDynamicTarget.pullNewAudios());
@ -1653,7 +1658,7 @@ void Player::processControls() {
stopLounging(); stopLounging();
} }
void Player::processStateChanges() { void Player::processStateChanges(float dt) {
if (isMaster()) { if (isMaster()) {
// Set the current player state based on what movement controller tells us // Set the current player state based on what movement controller tells us
@ -1713,7 +1718,7 @@ void Player::processStateChanges() {
} }
} }
m_humanoid->animate(WorldTimestep); m_humanoid->animate(dt);
if (auto techState = m_techController->parentState()) { if (auto techState = m_techController->parentState()) {
if (techState == TechController::ParentState::Stand) { if (techState == TechController::ParentState::Stand) {
@ -2260,10 +2265,10 @@ bool Player::invisible() const {
return m_statusController->statPositive("invisible"); return m_statusController->statPositive("invisible");
} }
void Player::animatePortrait() { void Player::animatePortrait(float dt) {
m_humanoid->animate(WorldTimestep); m_humanoid->animate(dt);
if (m_emoteCooldownTimer) { if (m_emoteCooldownTimer) {
m_emoteCooldownTimer -= WorldTimestep; m_emoteCooldownTimer -= dt;
if (m_emoteCooldownTimer <= 0) { if (m_emoteCooldownTimer <= 0) {
m_emoteCooldownTimer = 0; m_emoteCooldownTimer = 0;
m_emoteState = HumanoidEmote::Idle; m_emoteState = HumanoidEmote::Idle;

View File

@ -182,7 +182,7 @@ public:
Maybe<Json> receiveMessage(ConnectionId sendingConnection, String const& message, JsonArray const& args = {}) override; Maybe<Json> receiveMessage(ConnectionId sendingConnection, String const& message, JsonArray const& args = {}) override;
void update(uint64_t currentStep) override; void update(float dt, uint64_t currentStep) override;
void render(RenderCallback* renderCallback) override; void render(RenderCallback* renderCallback) override;
@ -404,7 +404,7 @@ public:
bool invisible() const; bool invisible() const;
void animatePortrait(); void animatePortrait(float dt);
bool isOutside(); bool isOutside();
@ -494,7 +494,7 @@ private:
void processControls(); void processControls();
// state changes and effect animations (master and slave) that happen AFTER movement/tech controller updates // state changes and effect animations (master and slave) that happen AFTER movement/tech controller updates
void processStateChanges(); void processStateChanges(float dt);
void getNetStates(bool initial); void getNetStates(bool initial);
void setNetStates(); void setNetStates();
@ -504,7 +504,7 @@ private:
List<Particle> particles(); List<Particle> particles();
String getFootstepSound(Vec2I const& sensor) const; String getFootstepSound(Vec2I const& sensor) const;
void tickShared(); void tickShared(float dt);
HumanoidEmote detectEmotes(String const& chatter); HumanoidEmote detectEmotes(String const& chatter);
@ -544,6 +544,7 @@ private:
float m_footstepVolumeVariance; float m_footstepVolumeVariance;
float m_landingVolume; float m_landingVolume;
bool m_landingNoisePending; bool m_landingNoisePending;
bool m_footstepPending;
String m_teleportAnimationType; String m_teleportAnimationType;
NetworkedAnimatorPtr m_effectsAnimator; NetworkedAnimatorPtr m_effectsAnimator;

View File

@ -114,8 +114,8 @@ Maybe<Json> PlayerCompanions::receiveMessage(String const& message, bool localMe
return m_scriptComponent.handleMessage(message, localMessage, args); return m_scriptComponent.handleMessage(message, localMessage, args);
} }
void PlayerCompanions::update() { void PlayerCompanions::update(float dt) {
m_scriptComponent.update(m_scriptComponent.updateDt()); m_scriptComponent.update(m_scriptComponent.updateDt(dt));
} }
LuaCallbacks PlayerCompanions::makeCompanionsCallbacks() { LuaCallbacks PlayerCompanions::makeCompanionsCallbacks() {

View File

@ -48,7 +48,7 @@ public:
void dismissCompanion(String const& category, Uuid const& podUuid); void dismissCompanion(String const& category, Uuid const& podUuid);
Maybe<Json> receiveMessage(String const& message, bool localMessage, JsonArray const& args = {}); Maybe<Json> receiveMessage(String const& message, bool localMessage, JsonArray const& args = {});
void update(); void update(float dt);
private: private:
LuaCallbacks makeCompanionsCallbacks(); LuaCallbacks makeCompanionsCallbacks();

View File

@ -80,8 +80,8 @@ Maybe<Json> PlayerDeployment::receiveMessage(String const& message, bool localMe
return m_scriptComponent.handleMessage(message, localMessage, args); return m_scriptComponent.handleMessage(message, localMessage, args);
} }
void PlayerDeployment::update() { void PlayerDeployment::update(float dt) {
m_scriptComponent.update(m_scriptComponent.updateDt()); m_scriptComponent.update(m_scriptComponent.updateDt(dt));
} }
void PlayerDeployment::render(RenderCallback* renderCallback, Vec2F const& position) { void PlayerDeployment::render(RenderCallback* renderCallback, Vec2F const& position) {

View File

@ -28,7 +28,7 @@ public:
void teleportOut(); void teleportOut();
Maybe<Json> receiveMessage(String const& message, bool localMessage, JsonArray const& args = {}); Maybe<Json> receiveMessage(String const& message, bool localMessage, JsonArray const& args = {});
void update(); void update(float dt);
void render(RenderCallback* renderCallback, Vec2F const& position); void render(RenderCallback* renderCallback, Vec2F const& position);

View File

@ -250,9 +250,11 @@ void Projectile::hitOther(EntityId entity, DamageRequest const&) {
m_scriptComponent.invoke("hit", entity); m_scriptComponent.invoke("hit", entity);
} }
void Projectile::update(uint64_t) { void Projectile::update(float dt, uint64_t) {
m_movementController->setTimestep(dt);
if (isMaster()) { if (isMaster()) {
m_timeToLive -= WorldTimestep; m_timeToLive -= dt;
if (m_timeToLive < 0) if (m_timeToLive < 0)
m_timeToLive = 0; m_timeToLive = 0;
@ -261,17 +263,17 @@ void Projectile::update(uint64_t) {
if (m_referenceVelocity) if (m_referenceVelocity)
m_movementController->setVelocity(m_movementController->velocity() - *m_referenceVelocity); m_movementController->setVelocity(m_movementController->velocity() - *m_referenceVelocity);
m_scriptComponent.update(m_scriptComponent.updateDt()); m_scriptComponent.update(m_scriptComponent.updateDt(dt));
m_movementController->accelerate(m_movementController->velocity().normalized() * m_acceleration); m_movementController->accelerate(m_movementController->velocity().normalized() * m_acceleration);
if (m_referenceVelocity) if (m_referenceVelocity)
m_movementController->setVelocity(m_movementController->velocity() + *m_referenceVelocity); m_movementController->setVelocity(m_movementController->velocity() + *m_referenceVelocity);
m_movementController->tickMaster(); m_movementController->tickMaster(dt);
m_travelLine.min() = m_travelLine.max(); m_travelLine.min() = m_travelLine.max();
m_travelLine.max() = m_movementController->position(); m_travelLine.max() = m_movementController->position();
tickShared(); tickShared(dt);
if (m_trackSourceEntity) { if (m_trackSourceEntity) {
if (auto sourceEntity = world()->entity(m_sourceEntity)) { if (auto sourceEntity = world()->entity(m_sourceEntity)) {
@ -335,13 +337,13 @@ void Projectile::update(uint64_t) {
} }
} else { } else {
m_netGroup.tickNetInterpolation(WorldTimestep); m_netGroup.tickNetInterpolation(WorldTimestep);
m_movementController->tickSlave(); m_movementController->tickSlave(dt);
m_travelLine.min() = m_travelLine.max(); m_travelLine.min() = m_travelLine.max();
m_travelLine.max() = m_movementController->position(); m_travelLine.max() = m_movementController->position();
m_timeToLive -= WorldTimestep; m_timeToLive -= dt;
tickShared(); tickShared(dt);
} }
if (world()->isClient()) if (world()->isClient())
@ -853,19 +855,19 @@ void Projectile::processAction(Json const& action) {
} }
} }
void Projectile::tickShared() { void Projectile::tickShared(float dt) {
if (!m_config->orientationLocked && !m_movementController->stickingDirection()) { if (!m_config->orientationLocked && !m_movementController->stickingDirection()) {
auto apparentVelocity = m_movementController->velocity() - m_referenceVelocity.value(); auto apparentVelocity = m_movementController->velocity() - m_referenceVelocity.value();
if (apparentVelocity != Vec2F()) if (apparentVelocity != Vec2F())
m_movementController->setRotation(apparentVelocity.angle()); m_movementController->setRotation(apparentVelocity.angle());
} }
m_animationTimer += WorldTimestep; m_animationTimer += dt;
setFrame(getFrame()); setFrame(getFrame());
m_effectEmitter->setSourcePosition("normal", position()); m_effectEmitter->setSourcePosition("normal", position());
m_effectEmitter->setDirection(getAngleSide(m_movementController->rotation(), true).second); m_effectEmitter->setDirection(getAngleSide(m_movementController->rotation(), true).second);
m_effectEmitter->tick(*entityMode()); m_effectEmitter->tick(dt, *entityMode());
if (m_collisionEvent.pullOccurred()) { if (m_collisionEvent.pullOccurred()) {
for (auto const& action : m_parameters.getArray("actionOnCollide", m_config->actionOnCollide)) for (auto const& action : m_parameters.getArray("actionOnCollide", m_config->actionOnCollide))
@ -879,7 +881,7 @@ void Projectile::tickShared() {
if (get<0>(periodicAction).wrapTick()) if (get<0>(periodicAction).wrapTick())
processAction(get<2>(periodicAction)); processAction(get<2>(periodicAction));
} else { } else {
if (get<0>(periodicAction).tick()) { if (get<0>(periodicAction).tick(dt)) {
processAction(get<2>(periodicAction)); processAction(get<2>(periodicAction));
periodicActionIt.remove(); periodicActionIt.remove();
} }

View File

@ -56,7 +56,7 @@ public:
List<DamageSource> damageSources() const override; List<DamageSource> damageSources() const override;
void hitOther(EntityId targetEntityId, DamageRequest const& dr) override; void hitOther(EntityId targetEntityId, DamageRequest const& dr) override;
void update(uint64_t currentStep) override; void update(float dt, uint64_t currentStep) override;
void render(RenderCallback* renderCallback) override; void render(RenderCallback* renderCallback) override;
void renderLightSources(RenderCallback* renderCallback) override; void renderLightSources(RenderCallback* renderCallback) override;
@ -117,7 +117,7 @@ private:
String drawableFrame(); String drawableFrame();
void processAction(Json const& action); void processAction(Json const& action);
void tickShared(); void tickShared(float dt);
void setup(); void setup();

View File

@ -342,7 +342,7 @@ Maybe<Json> QuestManager::receiveMessage(String const& message, bool localMessag
return result; return result;
} }
void QuestManager::update() { void QuestManager::update(float dt) {
startInitialQuests(); startInitialQuests();
if (m_trackedQuestId && !isActive(*m_trackedQuestId)) if (m_trackedQuestId && !isActive(*m_trackedQuestId))
@ -381,7 +381,7 @@ void QuestManager::update() {
} }
} }
serverQuests().exec([](QuestPtr const& quest) { quest->update(); }); serverQuests().exec([dt](QuestPtr const& quest) { quest->update(dt); });
} }
List<QuestPtr> QuestManager::serverQuests() const { List<QuestPtr> QuestManager::serverQuests() const {

View File

@ -58,7 +58,7 @@ public:
StringSet interestingObjects(); StringSet interestingObjects();
Maybe<Json> receiveMessage(String const& message, bool localMessage, JsonArray const& args = {}); Maybe<Json> receiveMessage(String const& message, bool localMessage, JsonArray const& args = {});
void update(); void update(float dt);
private: private:
List<QuestPtr> serverQuests() const; List<QuestPtr> serverQuests() const;

View File

@ -216,10 +216,10 @@ Maybe<Json> Quest::receiveMessage(String const& message, bool localMessage, Json
return m_scriptComponent.handleMessage(message, localMessage, args); return m_scriptComponent.handleMessage(message, localMessage, args);
} }
void Quest::update() { void Quest::update(float dt) {
if (!m_inited) if (!m_inited)
return; return;
m_scriptComponent.update(m_scriptComponent.updateDt()); m_scriptComponent.update(m_scriptComponent.updateDt(dt));
} }
void Quest::offer() { void Quest::offer() {

View File

@ -46,7 +46,7 @@ public:
void uninit(); void uninit();
Maybe<Json> receiveMessage(String const& message, bool localMessage, JsonArray const& args = {}); Maybe<Json> receiveMessage(String const& message, bool localMessage, JsonArray const& args = {});
void update(); void update(float dt);
void offer(); void offer();
void declineOffer(); void declineOffer();

View File

@ -115,8 +115,7 @@ void Sky::stateUpdate() {
m_lastWarpPhase = m_warpPhase; m_lastWarpPhase = m_warpPhase;
} }
void Sky::update() { void Sky::update(double dt) {
double dt = WorldTimestep;
if (m_referenceClock) { if (m_referenceClock) {
m_time = m_referenceClock->time(); m_time = m_referenceClock->time();
if (!m_clockTrackingTime) { if (!m_clockTrackingTime) {

View File

@ -36,7 +36,7 @@ public:
// handles flying and warp state transitions // handles flying and warp state transitions
void stateUpdate(); void stateUpdate();
void update(); void update(double dt);
void setType(SkyType type); void setType(SkyType type);
SkyType type() const; SkyType type() const;

View File

@ -73,7 +73,7 @@ void Spawner::activateEmptyRegion(RectF region) {
m_activeSpawnCells[cell] = m_spawnCellLifetime; m_activeSpawnCells[cell] = m_spawnCellLifetime;
} }
void Spawner::update() { void Spawner::update(float dt) {
if (!m_facade) if (!m_facade)
return; return;
@ -82,9 +82,8 @@ void Spawner::update() {
activateRegion(window.padded(m_windowActivationBorder)); activateRegion(window.padded(m_windowActivationBorder));
} }
eraseWhere(m_activeSpawnCells, [](auto& p) { eraseWhere(m_activeSpawnCells, [dt](auto& p) {
p.second -= WorldTimestep; return (p.second -= dt) < 0.0f;
return p.second < 0.0f;
}); });
eraseWhere(m_spawnedEntities, [this](EntityId entityId) { eraseWhere(m_spawnedEntities, [this](EntityId entityId) {

Some files were not shown because too many files have changed in this diff Show More