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::Music] = {1.0f, 1.0f, 0};
m_groupVolumes[MixerGroup::Cinematic] = {1.0f, 1.0f, 0};
m_speed = 1.0f;
}
unsigned Mixer::sampleRate() const {
@ -203,6 +205,10 @@ bool Mixer::hasEffect(String const& effectName) {
return m_effects.contains(effectName);
}
void Mixer::setSpeed(float speed) {
m_speed = speed;
}
void Mixer::setVolume(float volume, float rampTime) {
MutexLocker locker(m_mutex);
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)
outBuffer[i] = 0;
float speed = m_speed;
{
MutexLocker locker(m_queueMutex);
// 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)
: audioInstance->m_pitchMultiplier;
if (audioInstance->m_mixerGroup == MixerGroup::Effects)
pitchMultiplier *= speed;
if (audioStopVolEnd == 0.0f && audioInstance->m_stopping)
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);
eraseWhere(m_audios, [&](auto& p) {

View File

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

View File

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

View File

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

View File

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

View File

@ -22,7 +22,7 @@ public:
PanePtr createTooltip(Vec2I const& screenPosition) override;
bool sendEvent(InputEvent const& event) override;
void update() override;
void update(float dt) 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();
}
void AiInterface::update() {
void AiInterface::update(float dt) {
if (!m_client->playerOnOwnShip())
dismiss();
Pane::update();
Pane::update(dt);
m_showCrewButton->setVisibility(m_currentPage == AiPages::StatusPage);
m_showMissionsButton->setVisibility(m_currentPage == AiPages::StatusPage);
m_backButton->setVisibility(m_currentPage != AiPages::StatusPage);
m_staticAnimation.update(WorldTimestep);
m_scanlineAnimation.update(WorldTimestep);
m_staticAnimation.update(dt);
m_scanlineAnimation.update(dt);
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->setTextCharLimit(min(m_textMaxLength, floor(m_textLength)));
@ -129,10 +129,10 @@ void AiInterface::update() {
if (m_chatterSound)
m_chatterSound->stop();
}
m_faceAnimation.second.update(WorldTimestep * m_currentSpeech->speedModifier);
m_faceAnimation.second.update(dt * m_currentSpeech->speedModifier);
} else {
setFaceAnimation("idle");
m_faceAnimation.second.update(WorldTimestep);
m_faceAnimation.second.update(dt);
if (m_chatterSound)
m_chatterSound->stop();
}

View File

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

View File

@ -71,8 +71,8 @@ void BaseScriptPane::dismissed() {
m_script.uninit();
}
void BaseScriptPane::tick() {
Pane::tick();
void BaseScriptPane::tick(float dt) {
Pane::tick(dt);
for (auto p : m_canvasClickCallbacks) {
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.update(m_script.updateDt());
m_script.update(m_script.updateDt(dt));
}
bool BaseScriptPane::sendEvent(InputEvent const& event) {

View File

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

View File

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

View File

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

View File

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

View File

@ -26,7 +26,7 @@ public:
virtual void renderImpl() override;
virtual void hide() override;
virtual void update() override;
virtual void update(float dt) override;
void addLine(String const& text, 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) {
m_bubbles.forEach([this, &world](BubbleState<Bubble>& bubbleState, Bubble& bubble) {
bubble.age += WorldTimestep;
void ChatBubbleManager::update(float dt, WorldClientPtr world) {
m_bubbles.forEach([this, dt, &world](BubbleState<Bubble>& bubbleState, Bubble& bubble) {
bubble.age += dt;
if (auto entity = world->get<ChattyEntity>(bubble.entity)) {
bubble.onscreen = m_camera.worldGeometry().rectIntersectsRect(
m_camera.worldScreenRect(), entity->metaBoundBox().translated(entity->position()));
@ -97,7 +97,7 @@ void ChatBubbleManager::update(WorldClientPtr world) {
});
for (auto& portraitBubble : m_portraitBubbles) {
portraitBubble.age += WorldTimestep;
portraitBubble.age += dt;
if (auto entity = world->entity(portraitBubble.entity)) {
portraitBubble.onscreen = m_camera.worldGeometry().rectIntersectsRect(m_camera.worldScreenRect(), entity->metaBoundBox().translated(entity->position()));
if (auto chatter = as<ChattyEntity>(entity))
@ -125,7 +125,7 @@ void ChatBubbleManager::update(WorldClientPtr world) {
return false;
});
m_bubbles.update();
m_bubbles.update(dt);
}
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 update(WorldClientPtr world);
void update(float dt, WorldClientPtr world);
void render();
private:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -236,7 +236,7 @@ bool InventoryPane::containsNewItems() const {
return false;
}
void InventoryPane::update() {
void InventoryPane::update(float dt) {
auto inventory = m_player->inventory();
auto context = Widget::context();
@ -255,7 +255,7 @@ void InventoryPane::update() {
m_trashSlot->setItem(inventory->itemsAt(TrashSlot()));
m_trashSlot->showLinkIndicator(customBarItems.contains(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{
{"itemName", trashItem->name()},
{"count", trashItem->count()},

View File

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

View File

@ -520,8 +520,9 @@ void MainInterface::handleInteractAction(InteractAction interactAction) {
}
}
void MainInterface::update() {
m_paneManager.update();
void MainInterface::update(float dt) {
m_paneManager.update(dt);
m_cursor.update(dt);
m_questLogInterface->pollDialog(&m_paneManager);
@ -544,7 +545,7 @@ void MainInterface::update() {
// update mouseover target
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) {
return entity != player
&& entity->damageBar() == DamageBarType::Default
@ -578,10 +579,10 @@ void MainInterface::update() {
if (damageBarEntity && damageBarEntity->damageBar() == DamageBarType::Special) {
float targetHealth = damageBarEntity->health() / damageBarEntity->maxHealth();
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;
else
m_specialDamageBarValue += copysign(1.0f, targetHealth - m_specialDamageBarValue) * fillSpeed * WorldTimestep;
m_specialDamageBarValue += copysign(1.0f, targetHealth - m_specialDamageBarValue) * fillSpeed * dt;
} else {
m_specialDamageBarTarget = NullEntityId;
}
@ -696,7 +697,7 @@ void MainInterface::update() {
for (auto it = m_messages.begin(); it != m_messages.end();) {
auto& message = *it;
message->cooldown -= WorldTimestep;
message->cooldown -= dt;
if (message->cooldown < 0)
it = m_messages.erase(it);
else
@ -713,7 +714,7 @@ void MainInterface::update() {
auto worldId = m_client->playerWorld();
if (worldId.is<CelestialWorldId>()) {
if (m_planetNameTimer.tick())
if (m_planetNameTimer.tick(dt))
m_paneManager.dismissRegisteredPane(MainInterfacePanes::PlanetText);
else
m_paneManager.displayRegisteredPane(MainInterfacePanes::PlanetText);
@ -755,8 +756,8 @@ void MainInterface::update() {
updateCursor();
m_nameplatePainter->update(m_client->worldClient(), m_worldPainter->camera(), m_client->worldClient()->interactiveHighlightMode());
m_questIndicatorPainter->update(m_client->worldClient(), m_worldPainter->camera());
m_nameplatePainter->update(dt, m_client->worldClient(), m_worldPainter->camera(), m_client->worldClient()->interactiveHighlightMode());
m_questIndicatorPainter->update(dt, m_client->worldClient(), m_worldPainter->camera());
m_chatBubbleManager->setCamera(m_worldPainter->camera());
if (auto worldClient = m_client->worldClient()) {
@ -781,7 +782,7 @@ void MainInterface::update() {
}
m_chatBubbleManager->addChatActions(chatActions);
m_chatBubbleManager->update(worldClient);
m_chatBubbleManager->update(dt, worldClient);
}
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()));
else
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())
return m_guiContext->applicationController()->setCursorVisible(false);
m_cursor.update(WorldTimestep);
Vec2I cursorPos = m_cursorScreenPos;
Vec2I cursorSize = m_cursor.size();
Vec2I cursorOffset = m_cursor.offset();

View File

@ -91,7 +91,7 @@ public:
void handleInteractAction(InteractAction interactAction);
// 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
// behind interface e.g. chat bubbles

View File

@ -23,7 +23,7 @@ void MainMixer::setWorldPainter(WorldPainterPtr 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 updateGroupVolume = [&](MixerGroup group, bool muted, String const& settingName) {
@ -111,9 +111,9 @@ void MainMixer::update(bool muteSfx, bool muteMusic) {
};
if (Voice* voice = Voice::singletonPtr())
voice->update(attenuationFunction);
voice->update(dt, attenuationFunction);
m_mixer->update(attenuationFunction);
m_mixer->update(dt, attenuationFunction);
} else {
if (m_mixer->hasEffect("lowpass"))
@ -122,9 +122,9 @@ void MainMixer::update(bool muteSfx, bool muteMusic) {
m_mixer->removeEffect("echo", 0);
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;
}
void MainMixer::setSpeed(float speed) {
m_mixer->setSpeed(max(speed, 0.0f));
}
void MainMixer::setVolume(float volume, float rampTime) {
m_mixer->setVolume(volume, rampTime);
}

View File

@ -17,10 +17,11 @@ public:
void setUniverseClient(UniverseClientPtr universeClient);
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;
void setSpeed(float speed);
void setVolume(float volume, float rampTime = 0.0f);
void read(int16_t* sampleData, size_t frameCount, Mixer::ExtraMixFunction = {});

View File

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

View File

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

View File

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

View File

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

View File

@ -28,7 +28,7 @@ NameplatePainter::NameplatePainter() {
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;
Set<EntityId> foundEntities;
@ -70,7 +70,7 @@ void NameplatePainter::update(WorldClientPtr const& world, WorldCamera const& ca
});
m_entitiesWithNametags = move(foundEntities);
m_nametags.update();
m_nametags.update(dt);
}
void NameplatePainter::render() {

View File

@ -15,7 +15,7 @@ class NameplatePainter {
public:
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();
private:

View File

@ -16,7 +16,7 @@ AnimationPtr indicatorAnimation(String 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;
Set<EntityId> foundIndicators;
@ -30,7 +30,7 @@ void QuestIndicatorPainter::update(WorldClientPtr const& world, WorldCamera cons
if (auto currentIndicator = m_indicators.ptr(entity->entityId())) {
currentIndicator->screenPos = screenPos;
if (currentIndicator->indicatorName == indicator->indicatorImage) {
currentIndicator->animation->update(WorldTimestep);
currentIndicator->animation->update(dt);
} else {
currentIndicator->indicatorName = indicator->indicatorImage;
currentIndicator->animation = indicatorAnimation(indicator->indicatorImage);

View File

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

View File

@ -93,12 +93,12 @@ void QuestLogInterface::pollDialog(PaneManager* paneManager) {
void QuestLogInterface::displayed() {
Pane::displayed();
tick();
tick(0);
fetchData();
}
void QuestLogInterface::tick() {
Pane::tick();
void QuestLogInterface::tick(float dt) {
Pane::tick(dt);
auto selected = getSelected();
if (selected && m_manager->hasQuest(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");
verticalLayout->update();
verticalLayout->update(0);
}
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 void displayed() override;
virtual void tick() override;
virtual void tick(float dt) override;
virtual PanePtr createTooltip(Vec2I const& screenPosition) override;
void fetchData();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -47,7 +47,7 @@ public:
void render();
bool handleInputEvent(InputEvent const& event);
void update();
void update(float dt);
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) {
if (SpeakerPtr& speaker = entry.second) {
if (positionalAttenuationFunction) {

View File

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

View File

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

View File

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

View File

@ -720,7 +720,7 @@ void ActorMovementController::clearControls() {
m_controlModifiers = ActorMovementModifiers();
}
void ActorMovementController::tickMaster() {
void ActorMovementController::tickMaster(float dt) {
EntityAnchorConstPtr newAnchor;
if (auto anchorState = m_anchorState.get()) {
if (auto anchorableEntity = as<AnchorableEntity>(world()->entity(anchorState->entityId)))
@ -743,8 +743,8 @@ void ActorMovementController::tickMaster() {
m_groundMovement.set(false);
m_liquidMovement.set(false);
setVelocity((m_entityAnchor->position - MovementController::position()) / WorldTimestep);
MovementController::tickMaster();
setVelocity((m_entityAnchor->position - MovementController::position()) / dt);
MovementController::tickMaster(dt);
setPosition(m_entityAnchor->position);
} else {
auto activeParameters = m_baseParameters.merge(m_controlParameters);
@ -775,7 +775,7 @@ void ActorMovementController::tickMaster() {
if (appliedForceRegion()) {
m_pathController->reset();
} 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); });
auto action = m_pathController->curAction();
@ -811,7 +811,7 @@ void ActorMovementController::tickMaster() {
// MovementController still handles updating liquid percentage and updating force regions
updateLiquidPercentage();
updateForceRegions();
updateForceRegions(dt);
// onGround flag needs to be manually set, won't be set by MovementController::tickMaster
setOnGround(onGround);
clearControls();
@ -894,7 +894,7 @@ void ActorMovementController::tickMaster() {
float minGroundSustain = *activeParameters.groundMovementMinimumSustain;
float maxGroundSustain = *activeParameters.groundMovementMaximumSustain;
float groundCheckDistance = *activeParameters.groundMovementCheckDistance;
m_groundMovementSustainTimer.tick();
m_groundMovementSustainTimer.tick(dt);
if (onGround()) {
m_groundMovementSustainTimer = GameTimer(maxGroundSustain);
} else if (!m_groundMovementSustainTimer.ready() && groundCheckDistance > 0.0f && maxGroundSustain - m_groundMovementSustainTimer.timer > minGroundSustain) {
@ -933,15 +933,15 @@ void ActorMovementController::tickMaster() {
m_groundMovementSustainTimer = GameTimer(0);
} else if (holdJump) {
m_reJumpTimer.tick();
m_reJumpTimer.tick(dt);
if (m_jumpHoldTimer)
m_jumpHoldTimer->tick();
m_jumpHoldTimer->tick(dt);
approachYVelocity(*jumpProfile.jumpSpeed * jumpModifier, *jumpProfile.jumpControlForce * jumpModifier);
} else {
m_jumping.set(false);
m_reJumpTimer.tick();
m_reJumpTimer.tick(dt);
}
if (m_controlMove == Direction::Left) {
@ -1003,7 +1003,7 @@ void ActorMovementController::tickMaster() {
bool falling = (yVelocity() < *activeParameters.fallStatusSpeedMin) && !m_groundMovement.get();
m_falling.set(falling);
MovementController::tickMaster();
MovementController::tickMaster(dt);
m_lastControlJump = m_controlJump;
m_lastControlDown = m_controlDown;
@ -1017,8 +1017,8 @@ void ActorMovementController::tickMaster() {
clearControls();
}
void ActorMovementController::tickSlave() {
MovementController::tickSlave();
void ActorMovementController::tickSlave(float dt) {
MovementController::tickSlave(dt);
m_entityAnchor.reset();
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 speedAlongAngle = angleFactor * movementController.velocity().magnitude();
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;
}
break;

View File

@ -245,11 +245,11 @@ public:
// Clears all control data.
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.
void tickMaster();
void tickMaster(float dt);
void tickSlave();
void tickSlave(float dt);
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) {}
void DamageManager::update() {
void DamageManager::update(float dt) {
float const DefaultDamageTimeout = 1.0f;
auto damageIt = makeSMutableMapIterator(m_recentEntityDamages);
@ -67,7 +67,7 @@ void DamageManager::update() {
auto eventIt = makeSMutableIterator(events);
while (eventIt.hasNext()) {
auto& event = eventIt.next();
event.timeout -= WorldTimestep;
event.timeout -= dt;
auto entityIdTimeoutGroup = event.timeoutGroup.maybe<EntityId>();
if (event.timeout <= 0.0f || (entityIdTimeoutGroup && !m_world->entity(*entityIdTimeoutGroup)))
eventIt.remove();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,9 +2,8 @@
namespace Star {
float GlobalTimescale = 1.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;
EnumMap<Direction> const DirectionNames{

View File

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

View File

@ -176,7 +176,7 @@ RectF ItemDrop::collisionArea() const {
return m_boundBox;
}
void ItemDrop::update(uint64_t) {
void ItemDrop::update(float dt, uint64_t) {
if (isMaster()) {
if (m_owningEntity.get() != NullEntityId) {
auto owningEntity = world()->entity(m_owningEntity.get());
@ -228,9 +228,9 @@ void ItemDrop::update(uint64_t) {
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_ageItemsTimer.update(world()->epochTime());
@ -252,7 +252,7 @@ void ItemDrop::update(uint64_t) {
} else {
m_netGroup.tickNetInterpolation(WorldTimestep);
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;
void update(uint64_t currentStep) override;
void update(float dt, uint64_t currentStep) override;
bool shouldDestroy() const override;

View File

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

View File

@ -88,7 +88,7 @@ public:
bool shouldDestroy() const 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;
@ -137,7 +137,7 @@ public:
private:
Vec2F getAbsolutePosition(Vec2F relativePosition) const;
void updateStatus();
void updateStatus(float dt);
LuaCallbacks makeMonsterCallbacks();
void addChatMessage(String const& message, String const& portrait = "");

View File

@ -169,7 +169,9 @@ DataStream& operator<<(DataStream& ds, MovementParameters const& movementParamet
MovementController::MovementController(MovementParameters const& parameters) {
m_resting = false;
m_timeStep = WorldTimestep;
m_liquidPercentage = 0.0f;
m_liquidId = EmptyLiquidId;
@ -392,15 +394,15 @@ void MovementController::rotate(float rotationRate) {
return;
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) {
setVelocity(velocity() + acceleration * WorldTimestep);
setVelocity(velocity() + acceleration * m_timeStep);
}
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) {
@ -414,7 +416,7 @@ void MovementController::approachVelocity(Vec2F const& targetVelocity, float max
if (diffMagnitude == 0.0f)
return;
float maximumAcceleration = maxControlForce / mass() * WorldTimestep;
float maximumAcceleration = maxControlForce / mass() * m_timeStep;
float clampedMagnitude = clamp(diffMagnitude, 0.0f, maximumAcceleration);
setVelocity(velocity() + diff * (clampedMagnitude / diffMagnitude));
@ -437,7 +439,7 @@ void MovementController::approachVelocityAlongAngle(float angle, float targetVel
if (positiveOnly && diff < 0)
return;
float maximumAcceleration = maxControlForce / mass() * WorldTimestep;
float maximumAcceleration = maxControlForce / mass() * m_timeStep;
float diffMagnitude = std::fabs(diff);
float clampedMagnitude = clamp(diffMagnitude, 0.0f, maximumAcceleration);
@ -464,7 +466,12 @@ void MovementController::uninit() {
updatePositionInterpolators();
}
void MovementController::tickMaster() {
void MovementController::setTimestep(float dt) {
m_timeStep = dt;
}
void MovementController::tickMaster(float dt) {
setTimestep(dt);
auto geometry = world()->geometry();
m_zeroG.set(!*m_parameters.gravityEnabled || *m_parameters.gravityMultiplier == 0 || world()->gravity(position()) == 0);
@ -478,7 +485,7 @@ void MovementController::tickMaster() {
if (surfaceCollision) {
Vec2F surfacePositionDelta = geometry.diff(surfaceCollision->position, m_surfaceMovingCollisionPosition);
setPosition(position() + surfacePositionDelta);
Vec2F newSurfaceVelocity = surfacePositionDelta / WorldTimestep;
Vec2F newSurfaceVelocity = surfacePositionDelta / dt;
setVelocity(velocity() - m_surfaceVelocity + newSurfaceVelocity);
m_surfaceVelocity = newSurfaceVelocity;
} else {
@ -496,7 +503,7 @@ void MovementController::tickMaster() {
// don't integrate velocity when resting
Vec2F relativeVelocity = m_resting ? Vec2F() : velocity();
Vec2F originalMovement = relativeVelocity * WorldTimestep;
Vec2F originalMovement = relativeVelocity * dt;
if (surfaceCollision)
relativeVelocity -= m_surfaceVelocity;
@ -507,7 +514,7 @@ void MovementController::tickMaster() {
unsigned steps;
float maxMovementPerStep = *m_parameters.maxMovementPerStep;
if (maxMovementPerStep > 0.0f)
steps = std::floor(vmag(relativeVelocity) * WorldTimestep / maxMovementPerStep) + 1;
steps = std::floor(vmag(relativeVelocity) * dt / maxMovementPerStep) + 1;
else
steps = 1;
@ -516,8 +523,8 @@ void MovementController::tickMaster() {
steps = 0;
for (unsigned i = 0; i < steps; ++i) {
float dt = WorldTimestep / steps;
Vec2F movement = relativeVelocity * dt;
float dtSteps = dt / steps;
Vec2F movement = relativeVelocity * dtSteps;
if (!*m_parameters.collisionEnabled || collisionPoly().isNull()) {
setPosition(position() + movement);
@ -546,7 +553,7 @@ void MovementController::tickMaster() {
queryBounds.combine(queryBounds.translated(movement));
queryCollisions(queryBounds);
auto result = collisionMove(m_workingCollisions, body, movement, ignorePlatforms, *m_parameters.enableSurfaceSlopeCorrection && !zeroG(),
maximumCorrection, maximumPlatformCorrection, bodyCenter);
maximumCorrection, maximumPlatformCorrection, bodyCenter, dtSteps);
setPosition(position() + result.movement);
@ -572,7 +579,7 @@ void MovementController::tickMaster() {
if (*m_parameters.stickyCollision && result.collisionKind != CollisionKind::Slippery) {
// When sticking, cancel all velocity and apply stickyForce in the
// 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());
break;
} else {
@ -597,14 +604,14 @@ void MovementController::tickMaster() {
// independently).
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)
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)
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)
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 gravity = world()->gravity(position()) * *m_parameters.gravityMultiplier * (1.0f - buoyancy);
Vec2F environmentVelocity;
environmentVelocity[1] -= gravity * WorldTimestep;
environmentVelocity[1] -= gravity * dt;
if (onGround() && *m_parameters.slopeSlidingFactor != 0 && m_surfaceSlope[1] != 0)
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
// not induce oscillation at very high friction and so it cannot be
// 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);
}
setVelocity(newVelocity);
updateForceRegions();
updateForceRegions(dt);
}
void MovementController::tickSlave() {
void MovementController::tickSlave(float dt) {
setTimestep(dt);
if (auto movingCollisionId = m_surfaceMovingCollision.get()) {
if (auto physicsEntity = world()->get<PhysicsEntity>(movingCollisionId->physicsEntityId)) {
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 pos = position();
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,
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;
float const SlideAngle = Constants::pi / 3;
float const SlideCorrectionLimit = 0.2f;
float const SeparationTolerance = 0.001f;
float separationTolerance = 0.001f * (dt * 60.f);
maximumPlatformCorrection *= (dt * 60.f);
if (body.isNull())
return {movement, Vec2F(), {}, false, false, Vec2F(1, 0), CollisionKind::None};
@ -861,7 +870,7 @@ MovementController::CollisionResult MovementController::collisionMove(List<Colli
if (enableSurfaceSlopeCorrection) {
// 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;
checkBody.translate(separation.correction);
maxCollided = maxOrNullCollision(maxCollided, separation.collisionKind);
@ -889,7 +898,7 @@ MovementController::CollisionResult MovementController::collisionMove(List<Colli
totalCorrection = Vec2F();
for (size_t i = 0; i < MaximumSeparationLoops; ++i) {
separation = collisionSeparate(collisionPolys, checkBody, ignorePlatforms,
maximumPlatformCorrection, sortCenter, false, SeparationTolerance);
maximumPlatformCorrection, sortCenter, false, separationTolerance);
totalCorrection += separation.correction;
checkBody.translate(separation.correction);
maxCollided = maxOrNullCollision(maxCollided, separation.collisionKind);
@ -911,7 +920,7 @@ MovementController::CollisionResult MovementController::collisionMove(List<Colli
checkBody = body;
totalCorrection = -movement;
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;
checkBody.translate(separation.correction);
maxCollided = maxOrNullCollision(maxCollided, separation.collisionKind);
@ -931,7 +940,7 @@ MovementController::CollisionResult MovementController::collisionMove(List<Colli
result.movement = movement + totalCorrection;
result.correction = totalCorrection;
result.isStuck = false;
result.onGround = result.correction[1] > SeparationTolerance;
result.onGround = result.correction[1] > separationTolerance;
result.surfaceMovingCollisionId = surfaceMovingCollisionId;
result.collisionKind = maxCollided;
result.groundSlope = Vec2F(1, 0);
@ -944,7 +953,7 @@ MovementController::CollisionResult MovementController::collisionMove(List<Colli
// geometry, rather than off to the side.
float maxSideHorizontalOverlap = 0.0f;
RectF touchingBounds = checkBody.boundBox();
touchingBounds.pad(SeparationTolerance);
touchingBounds.pad(separationTolerance);
for (auto const& cp : collisionPolys) {
if (!cp.polyBounds.intersects(touchingBounds))
continue;
@ -959,7 +968,7 @@ MovementController::CollisionResult MovementController::collisionMove(List<Colli
float t = clamp(side.lineProjection(bodyVertex), 0.0f, 1.0f);
Vec2F nearPoint = side.eval(t);
if (nearPoint[1] > cp.sortPosition[1]) {
if (vmagSquared(bodyVertex - nearPoint) <= square(SeparationTolerance)) {
if (vmagSquared(bodyVertex - nearPoint) <= square(separationTolerance)) {
maxSideHorizontalOverlap = thisSideHorizontalOverlap;
result.groundSlope = side.diff().normalized();
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,
bool ignorePlatforms, float maximumPlatformCorrection, Vec2F const& sortCenter, bool upward, float separationTolerance) {
maximumPlatformCorrection *= WorldTimestep * 60.0f;
CollisionSeparation separation = {};
separation.collisionKind = CollisionKind::None;
bool intersects = false;

View File

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

View File

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

View File

@ -90,7 +90,7 @@ public:
bool shouldDestroy() const 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;
@ -177,7 +177,7 @@ public:
private:
Vec2F getAbsolutePosition(Vec2F relativePosition) const;
void tickShared();
void tickShared(float dt);
LuaCallbacks makeNpcCallbacks();
void setupNetStates();

View File

@ -352,12 +352,12 @@ List<Vec2I> Object::roots() const {
return {};
}
void Object::update(uint64_t) {
void Object::update(float dt, uint64_t) {
if (!inWorld())
return;
if (isMaster()) {
m_tileDamageStatus->recover(m_config->tileDamageParameters, WorldTimestep);
m_tileDamageStatus->recover(m_config->tileDamageParameters, dt);
if (m_liquidCheckTimer.wrapTick())
checkLiquidBroken();
@ -369,24 +369,24 @@ void Object::update(uint64_t) {
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_scriptComponent.update(m_scriptComponent.updateDt());
m_scriptComponent.update(m_scriptComponent.updateDt(dt));
} else {
m_networkedAnimator->update(WorldTimestep, &m_networkedAnimatorDynamicTarget);
m_networkedAnimator->update(dt, &m_networkedAnimatorDynamicTarget);
m_networkedAnimatorDynamicTarget.updatePosition(position() + m_animationPosition);
}
if (m_lightFlickering)
m_lightFlickering->update(WorldTimestep);
m_lightFlickering->update(dt);
for (auto& timer : m_emissionTimers)
timer.tick();
timer.tick(dt);
if (world()->isClient())
m_scriptedAnimator.update();

View File

@ -62,7 +62,7 @@ public:
virtual bool shouldDestroy() const 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;

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);
}
void Plant::update(uint64_t) {
m_windTime += WorldTimestep;
void Plant::update(float dt, uint64_t) {
m_windTime += dt;
m_windTime = std::fmod(m_windTime, 628.32f);
m_windLevel = world()->windLevel(Vec2F(m_tilePosition));
if (isMaster()) {
if (m_tileDamageStatus.damaged())
m_tileDamageStatus.recover(m_tileDamageParameters, WorldTimestep);
m_tileDamageStatus.recover(m_tileDamageParameters, dt);
} else {
if (m_tileDamageStatus.damaged() && !m_tileDamageStatus.damageProtected()) {
float damageEffectPercentage = m_tileDamageStatus.damageEffectPercentage();
m_windTime += damageEffectPercentage * 10 * WorldTimestep;
m_windTime += damageEffectPercentage * 10 * dt;
m_windLevel += damageEffectPercentage * 20;
}

View File

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

View File

@ -176,9 +176,10 @@ RectF PlantDrop::collisionRect() const {
return shape.boundBox();
}
void PlantDrop::update(uint64_t) {
m_time -= WorldTimestep;
void PlantDrop::update(float dt, uint64_t) {
m_time -= dt;
m_movementController.setTimestep(dt);
if (isMaster()) {
if (m_spawnedDropEffects && !m_spawnedDrops.get())
m_spawnedDropEffects = false; // false positive assumption over already having done the effect
@ -187,7 +188,7 @@ void PlantDrop::update(uint64_t) {
m_firstTick = false;
// 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)
m_rotationRate -= rotationAcceleration;
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;
m_movementController.applyParameters(parameters);
m_movementController.tickMaster();
m_movementController.tickMaster(dt);
if (m_movementController.onGround())
m_time = 0;
}
@ -243,7 +244,7 @@ void PlantDrop::update(uint64_t) {
if (m_spawnedDrops.get())
m_firstTick = false;
m_movementController.tickSlave();
m_movementController.tickSlave(dt);
}
}

View File

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

View File

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

View File

@ -182,7 +182,7 @@ public:
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;
@ -404,7 +404,7 @@ public:
bool invisible() const;
void animatePortrait();
void animatePortrait(float dt);
bool isOutside();
@ -494,7 +494,7 @@ private:
void processControls();
// 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 setNetStates();
@ -504,7 +504,7 @@ private:
List<Particle> particles();
String getFootstepSound(Vec2I const& sensor) const;
void tickShared();
void tickShared(float dt);
HumanoidEmote detectEmotes(String const& chatter);
@ -544,6 +544,7 @@ private:
float m_footstepVolumeVariance;
float m_landingVolume;
bool m_landingNoisePending;
bool m_footstepPending;
String m_teleportAnimationType;
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);
}
void PlayerCompanions::update() {
m_scriptComponent.update(m_scriptComponent.updateDt());
void PlayerCompanions::update(float dt) {
m_scriptComponent.update(m_scriptComponent.updateDt(dt));
}
LuaCallbacks PlayerCompanions::makeCompanionsCallbacks() {

View File

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

View File

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

View File

@ -28,7 +28,7 @@ public:
void teleportOut();
Maybe<Json> receiveMessage(String const& message, bool localMessage, JsonArray const& args = {});
void update();
void update(float dt);
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);
}
void Projectile::update(uint64_t) {
void Projectile::update(float dt, uint64_t) {
m_movementController->setTimestep(dt);
if (isMaster()) {
m_timeToLive -= WorldTimestep;
m_timeToLive -= dt;
if (m_timeToLive < 0)
m_timeToLive = 0;
@ -261,17 +263,17 @@ void Projectile::update(uint64_t) {
if (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);
if (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.max() = m_movementController->position();
tickShared();
tickShared(dt);
if (m_trackSourceEntity) {
if (auto sourceEntity = world()->entity(m_sourceEntity)) {
@ -335,13 +337,13 @@ void Projectile::update(uint64_t) {
}
} else {
m_netGroup.tickNetInterpolation(WorldTimestep);
m_movementController->tickSlave();
m_movementController->tickSlave(dt);
m_travelLine.min() = m_travelLine.max();
m_travelLine.max() = m_movementController->position();
m_timeToLive -= WorldTimestep;
m_timeToLive -= dt;
tickShared();
tickShared(dt);
}
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()) {
auto apparentVelocity = m_movementController->velocity() - m_referenceVelocity.value();
if (apparentVelocity != Vec2F())
m_movementController->setRotation(apparentVelocity.angle());
}
m_animationTimer += WorldTimestep;
m_animationTimer += dt;
setFrame(getFrame());
m_effectEmitter->setSourcePosition("normal", position());
m_effectEmitter->setDirection(getAngleSide(m_movementController->rotation(), true).second);
m_effectEmitter->tick(*entityMode());
m_effectEmitter->tick(dt, *entityMode());
if (m_collisionEvent.pullOccurred()) {
for (auto const& action : m_parameters.getArray("actionOnCollide", m_config->actionOnCollide))
@ -879,7 +881,7 @@ void Projectile::tickShared() {
if (get<0>(periodicAction).wrapTick())
processAction(get<2>(periodicAction));
} else {
if (get<0>(periodicAction).tick()) {
if (get<0>(periodicAction).tick(dt)) {
processAction(get<2>(periodicAction));
periodicActionIt.remove();
}

View File

@ -56,7 +56,7 @@ public:
List<DamageSource> damageSources() const 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 renderLightSources(RenderCallback* renderCallback) override;
@ -117,7 +117,7 @@ private:
String drawableFrame();
void processAction(Json const& action);
void tickShared();
void tickShared(float dt);
void setup();

View File

@ -342,7 +342,7 @@ Maybe<Json> QuestManager::receiveMessage(String const& message, bool localMessag
return result;
}
void QuestManager::update() {
void QuestManager::update(float dt) {
startInitialQuests();
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 {

View File

@ -58,7 +58,7 @@ public:
StringSet interestingObjects();
Maybe<Json> receiveMessage(String const& message, bool localMessage, JsonArray const& args = {});
void update();
void update(float dt);
private:
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);
}
void Quest::update() {
void Quest::update(float dt) {
if (!m_inited)
return;
m_scriptComponent.update(m_scriptComponent.updateDt());
m_scriptComponent.update(m_scriptComponent.updateDt(dt));
}
void Quest::offer() {

View File

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

View File

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

View File

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

View File

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

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