Add loading icon when swapping character

This commit is contained in:
Kae 2023-08-02 21:28:37 +10:00
parent 994c533a0f
commit c80b2d2dbc
8 changed files with 93 additions and 18 deletions

View File

@ -0,0 +1,34 @@
{
"projectileName" : "opensb:playerloading",
"image" : "/projectiles/invisibleprojectile/invisibleprojectile.png",
"processing" : "?crop=0;0;1;1?multiply=0000?replace;0000=fff?scalenearest=15?blendmult=/animations/chatting/menu.png:1;0;0?multiply=fffffffe",
"damageTeam" : { "type" : "ghostly" },
"damageKind" : "default",
"power" : 0,
"acceleration" : 0,
"speed" : 0,
"timeToLive" : 0,
"periodicActions" : [
{
"time" : 0.00,
"repeat" : false,
"action" : "sound",
"options" : [ "/sfx/objects/mannequin_open.ogg" ]
}
],
"actionOnReap" : [
{
"action" : "sound",
"options" : [ "/sfx/objects/mannequin_close.ogg" ]
}
],
"movementSettings" : {
"collisionEnabled" : true,
"gravityEnabled" : false,
"physicsEffectCategories" : [],
"collisionPoly" : []
}
}

View File

@ -526,7 +526,7 @@ List<Drawable> Humanoid::render(bool withItems, bool withRotation) {
auto backArmDrawable = [&](String const& frameSet, Directives const& directives) -> Drawable {
String image = strf("{}:{}", frameSet, backHand.backFrame);
Drawable backArm = Drawable::makeImage(move(image), 1.0f / TilePixels, true, backArmFrameOffset);
backArm.imagePart().addDirectives(directives);
backArm.imagePart().addDirectives(directives, true);
backArm.rotate(backHand.angle, backArmFrameOffset + m_backArmRotationCenter + m_backArmOffset);
return backArm;
};
@ -710,7 +710,7 @@ List<Drawable> Humanoid::render(bool withItems, bool withRotation) {
auto frontArmDrawable = [&](String const& frameSet, Directives const& directives) -> Drawable {
String image = strf("{}:{}", frameSet, frontHand.frontFrame);
Drawable frontArm = Drawable::makeImage(image, 1.0f / TilePixels, true, frontArmFrameOffset);
frontArm.imagePart().addDirectives(directives);
frontArm.imagePart().addDirectives(directives, true);
frontArm.rotate(frontHand.angle, frontArmFrameOffset + m_frontArmRotationCenter);
return frontArm;
};

View File

@ -358,7 +358,9 @@ void Projectile::render(RenderCallback* renderCallback) {
m_effectEmitter->render(renderCallback);
Drawable drawable = Drawable::makeImage(drawableFrame(), 1.0f / TilePixels, true, Vec2F());
String image = strf("{}:{}{}", m_config->image, m_frame, m_imageSuffix);
Drawable drawable = Drawable::makeImage(image, 1.0f / TilePixels, true, Vec2F());
drawable.imagePart().addDirectives(m_imageDirectives, true);
if (m_config->flippable) {
auto angleSide = getAngleSide(m_movementController->rotation(), true);
if (angleSide.second == Direction::Left)
@ -539,7 +541,8 @@ void Projectile::setFrame(int frame) {
}
String Projectile::drawableFrame() {
return strf("{}:{}{}", m_config->image, m_frame, m_imageDirectives);
String str = strf("{}:{}{}", m_config->image, m_frame, m_imageSuffix);
return m_imageDirectives.addToString(str);
}
bool Projectile::ephemeral() const {
@ -896,7 +899,22 @@ void Projectile::setup() {
m_acceleration = m_parameters.getFloat("acceleration", m_config->acceleration);
m_power = m_parameters.getFloat("power", m_config->power);
m_powerMultiplier = m_parameters.getFloat("powerMultiplier", 1.0f);
m_imageDirectives = m_parameters.getString("processing", "");
{ // it is possible to shove a frame name in processing. I hope nobody actually does this but account for it...
String processing = m_parameters.getString("processing", "");
auto begin = processing.utf8().find_first_of('?');
if (begin == NPos) {
m_imageDirectives = "";
m_imageSuffix = move(processing);
}
else if (begin == 0) {
m_imageDirectives = move(processing);
m_imageSuffix = "";
}
else {
m_imageDirectives = (String)processing.utf8().substr(begin);
m_imageSuffix = processing.utf8().substr(0, begin);
}
}
m_persistentAudioFile = m_parameters.getString("persistentAudio", m_config->persistentAudio);
m_damageKind = m_parameters.getString("damageKind", m_config->damageKind);

View File

@ -137,7 +137,8 @@ private:
float m_initialSpeed;
float m_power;
float m_powerMultiplier;
String m_imageDirectives;
Directives m_imageDirectives;
String m_imageSuffix;
Json m_damageTeam;
String m_damageKind;
DamageType m_damageType;

View File

@ -5,6 +5,7 @@
#include "StarVersion.hpp"
#include "StarRoot.hpp"
#include "StarConfiguration.hpp"
#include "StarProjectileDatabase.hpp"
#include "StarPlayerStorage.hpp"
#include "StarPlayer.hpp"
#include "StarPlayerLog.hpp"
@ -485,7 +486,7 @@ void UniverseClient::stopLua() {
m_scriptContexts.clear();
}
bool UniverseClient::reloadPlayer(Json const& data, Uuid const& uuid, bool resetInterfaces) {
bool UniverseClient::reloadPlayer(Json const& data, Uuid const& uuid, bool resetInterfaces, bool showIndicator) {
auto player = mainPlayer();
bool playerInWorld = player->inWorld();
auto world = as<WorldClient>(player->world());
@ -497,7 +498,20 @@ bool UniverseClient::reloadPlayer(Json const& data, Uuid const& uuid, bool reset
if (m_playerReloadPreCallback)
m_playerReloadPreCallback(resetInterfaces);
ProjectilePtr indicator;
if (playerInWorld) {
if (showIndicator) {
// EntityCreatePacket for player entities can be pretty big.
// We can show a loading projectile to other players while the create packet uploads.
auto projectileDb = Root::singleton().projectileDatabase();
auto config = projectileDb->projectileConfig("opensb:playerloading");
indicator = projectileDb->createProjectile("stationpartsound", config);
indicator->setInitialPosition(player->position());
indicator->setInitialDirection({ 1.0f, 0.0f });
world->addEntity(indicator);
}
world->removeEntity(player->entityId(), false);
} else {
m_respawning = false;
@ -518,6 +532,9 @@ bool UniverseClient::reloadPlayer(Json const& data, Uuid const& uuid, bool reset
world->addEntity(player, entityId);
if (indicator && indicator->inWorld())
world->removeEntity(indicator->entityId(), false);
CelestialCoordinate coordinate = m_systemWorldClient->location();
player->universeMap()->addMappedCoordinate(coordinate);
player->universeMap()->filterMappedObjects(coordinate, m_systemWorldClient->objectKeys());
@ -535,7 +552,7 @@ bool UniverseClient::switchPlayer(Uuid const& uuid) {
if (uuid == mainPlayer()->uuid())
return false;
else if (auto data = m_playerStorage->maybeGetPlayerData(uuid))
return reloadPlayer(*data, uuid, true);
return reloadPlayer(*data, uuid, true, true);
else
return false;
}

View File

@ -91,7 +91,7 @@ public:
void startLua();
void stopLua();
bool reloadPlayer(Json const& data, Uuid const& uuid, bool resetInterfaces = false);
bool reloadPlayer(Json const& data, Uuid const& uuid, bool resetInterfaces = false, bool showIndicator = false);
bool switchPlayer(Uuid const& uuid);
bool switchPlayer(size_t index);
bool switchPlayer(String const& name);

View File

@ -1275,6 +1275,7 @@ void WorldClient::addEntity(EntityPtr const& entity, EntityId entityId) {
if (entity->clientEntityMode() != ClientEntityMode::ClientSlaveOnly) {
entity->init(this, m_entityMap->reserveEntityId(entityId), EntityMode::Master);
m_entityMap->addEntity(entity);
notifyEntityCreate(entity);
} else {
auto entityFactory = Root::singleton().entityFactory();
m_outgoingPackets.append(make_shared<SpawnEntityPacket>(entity->entityType(), entityFactory->netStoreEntity(entity), entity->writeNetState().first));
@ -1358,15 +1359,7 @@ void WorldClient::queueUpdatePackets() {
if (m_currentStep % m_clientConfig.getInt("worldClientStateUpdateDelta") == 0)
m_outgoingPackets.append(make_shared<WorldClientStateUpdatePacket>(m_clientState.writeDelta()));
m_entityMap->forAllEntities([&](EntityPtr const& entity) {
if (entity->isMaster() && !m_masterEntitiesNetVersion.contains(entity->entityId())) {
// Server was unaware of this entity until now
auto firstNetState = entity->writeNetState();
m_masterEntitiesNetVersion[entity->entityId()] = firstNetState.second;
m_outgoingPackets.append(make_shared<EntityCreatePacket>(entity->entityType(),
entityFactory->netStoreEntity(entity), move(firstNetState.first), entity->entityId()));
}
});
m_entityMap->forAllEntities([&](EntityPtr const& entity) { notifyEntityCreate(entity); });
if (m_currentStep % m_interpolationTracker.entityUpdateDelta() == 0) {
auto entityUpdateSet = make_shared<EntityUpdateSetPacket>();
@ -1742,6 +1735,16 @@ void WorldClient::tryGiveMainPlayerItem(ItemPtr item) {
addEntity(ItemDrop::createRandomizedDrop(spill->descriptor(), m_mainPlayer->position()));
}
void WorldClient::notifyEntityCreate(EntityPtr const& entity) {
if (entity->isMaster() && !m_masterEntitiesNetVersion.contains(entity->entityId())) {
// Server was unaware of this entity until now
auto firstNetState = entity->writeNetState();
m_masterEntitiesNetVersion[entity->entityId()] = firstNetState.second;
m_outgoingPackets.append(make_shared<EntityCreatePacket>(entity->entityType(),
Root::singleton().entityFactory()->netStoreEntity(entity), move(firstNetState.first), entity->entityId()));
}
}
Vec2I WorldClient::environmentBiomeTrackPosition() const {
if (!inWorld())
return {};

View File

@ -212,6 +212,8 @@ private:
void clearWorld();
void tryGiveMainPlayerItem(ItemPtr item);
void notifyEntityCreate(EntityPtr const& entity);
// Queues pending (step based) updates to server,
void queueUpdatePackets();
void handleDamageNotifications();