From acb1b5f0025a01e827a18159aeef9a95cbbebdf4 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 13 Apr 2013 22:54:32 -0700 Subject: [PATCH 01/99] Try to handle NiBSAnimationNode This is still incomplete guess work. Currently it seems as though flag 0x20 specifies whether the controllers auto-play (if on), rather than follow the object's animation time. --- components/nifogre/ogrenifloader.cpp | 26 ++++++++++++++++---------- components/nifogre/skeleton.cpp | 1 + 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index a26f43131..ec53b79aa 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -492,7 +492,7 @@ class NIFObjectLoader static void createObjects(const std::string &name, const std::string &group, Ogre::SceneManager *sceneMgr, const Nif::Node *node, - ObjectList &objectlist, int flags) + ObjectList &objectlist, int flags, int animflags) { // Do not create objects for the collision shape (includes all children) if(node->recType == Nif::RC_RootCollisionNode) @@ -503,7 +503,10 @@ class NIFObjectLoader if (node->name.find("marker") != std::string::npos) return; - flags |= node->flags; + if(node->recType == Nif::RC_NiBSAnimationNode) + animflags |= node->flags; + else + flags |= node->flags; Nif::ExtraPtr e = node->extra; while(!e.empty()) @@ -539,9 +542,10 @@ class NIFObjectLoader int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); - Ogre::ControllerValueRealPtr srcval; /* Filled in later */ + Ogre::ControllerValueRealPtr srcval((animflags&0x20) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr())); - Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, false)); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, (animflags&0x20))); objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } @@ -552,9 +556,10 @@ class NIFObjectLoader { int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); - Ogre::ControllerValueRealPtr srcval; /* Filled in later */ + Ogre::ControllerValueRealPtr srcval((animflags&0x20) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); - Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, false)); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, (animflags&0x20))); objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } @@ -599,9 +604,10 @@ class NIFObjectLoader const Nif::NiUVController *uv = static_cast(ctrl.getPtr()); const Ogre::MaterialPtr &material = entity->getSubEntity(0)->getMaterial(); - Ogre::ControllerValueRealPtr srcval(Ogre::ControllerManager::getSingleton().getFrameTimeSource()); + Ogre::ControllerValueRealPtr srcval((animflags&0x20) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr())); - Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, true)); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, (animflags&0x20))); objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } @@ -627,7 +633,7 @@ class NIFObjectLoader for(size_t i = 0;i < children.length();i++) { if(!children[i].empty()) - createObjects(name, group, sceneMgr, children[i].getPtr(), objectlist, flags); + createObjects(name, group, sceneMgr, children[i].getPtr(), objectlist, flags, animflags); } } } @@ -674,7 +680,7 @@ public: // Create a base skeleton entity if this NIF needs one createSkelBase(name, group, sceneMgr, node, objectlist); } - createObjects(name, group, sceneMgr, node, objectlist, flags); + createObjects(name, group, sceneMgr, node, objectlist, flags, 0); } }; diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index e97e91ef0..28df4894d 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -164,6 +164,7 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */ node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */ node->recType == Nif::RC_NiTriShape || /* Handled in the mesh loader */ + node->recType == Nif::RC_NiBSAnimationNode || /* Handled in the object loader */ node->recType == Nif::RC_NiCamera || node->recType == Nif::RC_NiAutoNormalParticles || node->recType == Nif::RC_NiRotatingParticles From f4695ec4ac487812b2b9120e1ebd90726ba9fd58 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 14 Apr 2013 00:06:40 -0700 Subject: [PATCH 02/99] Cleanup NIFObjectLoader some --- components/nifogre/ogrenifloader.cpp | 213 +++++++++++++-------------- 1 file changed, 106 insertions(+), 107 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index ec53b79aa..631d39728 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -331,9 +331,9 @@ public: -/** Manual resource loader for NIF objects (meshes, particle systems, etc). - * This is the main class responsible for translating the internal NIF - * structures into something Ogre can use. +/** Object creator for NIFs. This is the main class responsible for creating + * "live" Ogre objects (entities, particle systems, controllers, etc) from + * their NIF equivalents. */ class NIFObjectLoader { @@ -349,6 +349,57 @@ class NIFObjectLoader } + static void createEntity(const std::string &name, const std::string &group, + Ogre::SceneManager *sceneMgr, ObjectList &objectlist, + const Nif::Node *node, int flags, int animflags) + { + const Nif::NiTriShape *shape = static_cast(node); + + std::string fullname = name+"@index="+Ogre::StringConverter::toString(shape->recIndex); + if(shape->name.length() > 0) + fullname += "@shape="+shape->name; + Misc::StringUtils::toLower(fullname); + + Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); + if(meshMgr.getByName(fullname).isNull()) + NIFMeshLoader::createMesh(name, fullname, group, shape->recIndex); + + Ogre::Entity *entity = sceneMgr->createEntity(fullname); + entity->setVisible(!(flags&0x01)); + + objectlist.mEntities.push_back(entity); + if(objectlist.mSkelBase) + { + if(entity->hasSkeleton()) + entity->shareSkeletonInstanceWith(objectlist.mSkelBase); + else + { + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), entity); + } + } + + Nif::ControllerPtr ctrl = node->controller; + while(!ctrl.empty()) + { + if(ctrl->recType == Nif::RC_NiUVController) + { + const Nif::NiUVController *uv = static_cast(ctrl.getPtr()); + + const Ogre::MaterialPtr &material = entity->getSubEntity(0)->getMaterial(); + Ogre::ControllerValueRealPtr srcval((animflags&0x20) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, (animflags&0x20))); + + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + ctrl = ctrl->next; + } + } + + static void createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, const Nif::NiParticleSystemController *partctrl) { Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif"); @@ -416,9 +467,9 @@ class NIFObjectLoader } } - static Ogre::ParticleSystem *createParticleSystem(const std::string &name, const std::string &group, - Ogre::SceneManager *sceneMgr, Ogre::Entity *entitybase, - const Nif::Node *partnode) + static void createParticleSystem(const std::string &name, const std::string &group, + Ogre::SceneManager *sceneMgr, ObjectList &objectlist, + const Nif::Node *partnode, int flags, int animflags) { const Nif::NiAutoNormalParticlesData *particledata = NULL; if(partnode->recType == Nif::RC_NiAutoNormalParticles) @@ -426,67 +477,63 @@ class NIFObjectLoader else if(partnode->recType == Nif::RC_NiRotatingParticles) particledata = static_cast(partnode)->data.getPtr(); + std::string fullname = name+"@index="+Ogre::StringConverter::toString(partnode->recIndex); + if(partnode->name.length() > 0) + fullname += "@type="+partnode->name; + Misc::StringUtils::toLower(fullname); + Ogre::ParticleSystem *partsys = sceneMgr->createParticleSystem(); - try { - std::string fullname = name+"@index="+Ogre::StringConverter::toString(partnode->recIndex); - if(partnode->name.length() > 0) - fullname += "@type="+partnode->name; - Misc::StringUtils::toLower(fullname); - const Nif::NiTexturingProperty *texprop = NULL; - const Nif::NiMaterialProperty *matprop = NULL; - const Nif::NiAlphaProperty *alphaprop = NULL; - const Nif::NiVertexColorProperty *vertprop = NULL; - const Nif::NiZBufferProperty *zprop = NULL; - const Nif::NiSpecularProperty *specprop = NULL; - const Nif::NiWireframeProperty *wireprop = NULL; - bool needTangents = false; + const Nif::NiTexturingProperty *texprop = NULL; + const Nif::NiMaterialProperty *matprop = NULL; + const Nif::NiAlphaProperty *alphaprop = NULL; + const Nif::NiVertexColorProperty *vertprop = NULL; + const Nif::NiZBufferProperty *zprop = NULL; + const Nif::NiSpecularProperty *specprop = NULL; + const Nif::NiWireframeProperty *wireprop = NULL; + bool needTangents = false; - partnode->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop); - partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group, - texprop, matprop, alphaprop, - vertprop, zprop, specprop, - wireprop, needTangents)); + partnode->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop); + partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group, + texprop, matprop, alphaprop, + vertprop, zprop, specprop, + wireprop, needTangents)); - partsys->setDefaultDimensions(particledata->particleRadius*2.0f, - particledata->particleRadius*2.0f); - partsys->setCullIndividually(false); - partsys->setParticleQuota(particledata->numParticles); - // TODO: There is probably a field or flag to specify this, as some - // particle effects have it and some don't. - partsys->setKeepParticlesInLocalSpace(true); + partsys->setDefaultDimensions(particledata->particleRadius*2.0f, + particledata->particleRadius*2.0f); + partsys->setCullIndividually(false); + partsys->setParticleQuota(particledata->numParticles); + // TODO: There is probably a field or flag to specify this, as some + // particle effects have it and some don't. + partsys->setKeepParticlesInLocalSpace(true); - Nif::ControllerPtr ctrl = partnode->controller; - while(!ctrl.empty()) + Nif::ControllerPtr ctrl = partnode->controller; + while(!ctrl.empty()) + { + if(ctrl->recType == Nif::RC_NiParticleSystemController) { - if(ctrl->recType == Nif::RC_NiParticleSystemController) + const Nif::NiParticleSystemController *partctrl = static_cast(ctrl.getPtr()); + + createParticleEmitterAffectors(partsys, partctrl); + if(!partctrl->emitter.empty() && !partsys->isAttached()) { - const Nif::NiParticleSystemController *partctrl = static_cast(ctrl.getPtr()); - - createParticleEmitterAffectors(partsys, partctrl); - if(!partctrl->emitter.empty() && !partsys->isAttached()) - { - int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); - Ogre::Bone *trgtbone = entitybase->getSkeleton()->getBone(trgtid); - entitybase->attachObjectToBone(trgtbone->getName(), partsys); - } + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys); } - ctrl = ctrl->next; - } - - if(!partsys->isAttached()) - { - int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex); - Ogre::Bone *trgtbone = entitybase->getSkeleton()->getBone(trgtid); - entitybase->attachObjectToBone(trgtbone->getName(), partsys); } + ctrl = ctrl->next; } - catch(std::exception &e) { - std::cerr<< "Particles exception: "<destroyParticleSystem(partsys); - partsys = NULL; - }; - return partsys; + + if(!partsys->isAttached()) + { + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys); + } + + partsys->setVisible(!(flags&0x01)); + objectlist.mParticles.push_back(partsys); } @@ -569,61 +616,13 @@ class NIFObjectLoader if(node->recType == Nif::RC_NiTriShape && !(flags&0x80000000)) { - const Nif::NiTriShape *shape = static_cast(node); - - std::string fullname = name+"@index="+Ogre::StringConverter::toString(shape->recIndex); - if(shape->name.length() > 0) - fullname += "@shape="+shape->name; - Misc::StringUtils::toLower(fullname); - - Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); - if(meshMgr.getByName(fullname).isNull()) - NIFMeshLoader::createMesh(name, fullname, group, shape->recIndex); - - Ogre::Entity *entity = sceneMgr->createEntity(fullname); - entity->setVisible(!(flags&0x01)); - - objectlist.mEntities.push_back(entity); - if(objectlist.mSkelBase) - { - if(entity->hasSkeleton()) - entity->shareSkeletonInstanceWith(objectlist.mSkelBase); - else - { - int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex); - Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); - objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), entity); - } - } - - Nif::ControllerPtr ctrl = node->controller; - while(!ctrl.empty()) - { - if(ctrl->recType == Nif::RC_NiUVController) - { - const Nif::NiUVController *uv = static_cast(ctrl.getPtr()); - - const Ogre::MaterialPtr &material = entity->getSubEntity(0)->getMaterial(); - Ogre::ControllerValueRealPtr srcval((animflags&0x20) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : - Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr())); - Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, (animflags&0x20))); - - objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); - } - ctrl = ctrl->next; - } + createEntity(name, group, sceneMgr, objectlist, node, flags, animflags); } if((node->recType == Nif::RC_NiAutoNormalParticles || node->recType == Nif::RC_NiRotatingParticles) && !(flags&0x40000000)) { - Ogre::ParticleSystem *partsys = createParticleSystem(name, group, sceneMgr, objectlist.mSkelBase, node); - if(partsys != NULL) - { - partsys->setVisible(!(flags&0x01)); - objectlist.mParticles.push_back(partsys); - } + createParticleSystem(name, group, sceneMgr, objectlist, node, flags, animflags); } const Nif::NiNode *ninode = dynamic_cast(node); From bf8bc989fc0184f59aa40f9d3a028d4fad6f749b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 14 Apr 2013 12:52:20 -0700 Subject: [PATCH 03/99] Recognize and partly handle NiBSParticleNode --- components/nif/niffile.cpp | 2 +- components/nif/record.hpp | 1 + components/nifogre/ogrenifloader.cpp | 12 +++++++----- components/nifogre/skeleton.cpp | 1 + 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 3b41e96a7..2c4f3506e 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -208,7 +208,7 @@ static const RecordFactoryEntry recordFactories [] = { { "NiNode", &construct , RC_NiNode }, { "AvoidNode", &construct , RC_NiNode }, - { "NiBSParticleNode", &construct , RC_NiNode }, + { "NiBSParticleNode", &construct , RC_NiBSParticleNode }, { "NiBSAnimationNode", &construct , RC_NiBSAnimationNode }, { "NiBillboardNode", &construct , RC_NiNode }, { "NiTriShape", &construct , RC_NiTriShape }, diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 361af3f64..97b10503e 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -39,6 +39,7 @@ enum RecordType RC_NiTriShape, RC_NiRotatingParticles, RC_NiAutoNormalParticles, + RC_NiBSParticleNode, RC_NiCamera, RC_NiTexturingProperty, RC_NiMaterialProperty, diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 631d39728..1ed5e24a3 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -469,7 +469,7 @@ class NIFObjectLoader static void createParticleSystem(const std::string &name, const std::string &group, Ogre::SceneManager *sceneMgr, ObjectList &objectlist, - const Nif::Node *partnode, int flags, int animflags) + const Nif::Node *partnode, int flags, int partflags) { const Nif::NiAutoNormalParticlesData *particledata = NULL; if(partnode->recType == Nif::RC_NiAutoNormalParticles) @@ -539,7 +539,7 @@ class NIFObjectLoader static void createObjects(const std::string &name, const std::string &group, Ogre::SceneManager *sceneMgr, const Nif::Node *node, - ObjectList &objectlist, int flags, int animflags) + ObjectList &objectlist, int flags, int animflags, int partflags) { // Do not create objects for the collision shape (includes all children) if(node->recType == Nif::RC_RootCollisionNode) @@ -552,6 +552,8 @@ class NIFObjectLoader if(node->recType == Nif::RC_NiBSAnimationNode) animflags |= node->flags; + else if(node->recType == Nif::RC_NiBSParticleNode) + partflags |= node->flags; else flags |= node->flags; @@ -622,7 +624,7 @@ class NIFObjectLoader if((node->recType == Nif::RC_NiAutoNormalParticles || node->recType == Nif::RC_NiRotatingParticles) && !(flags&0x40000000)) { - createParticleSystem(name, group, sceneMgr, objectlist, node, flags, animflags); + createParticleSystem(name, group, sceneMgr, objectlist, node, flags, partflags); } const Nif::NiNode *ninode = dynamic_cast(node); @@ -632,7 +634,7 @@ class NIFObjectLoader for(size_t i = 0;i < children.length();i++) { if(!children[i].empty()) - createObjects(name, group, sceneMgr, children[i].getPtr(), objectlist, flags, animflags); + createObjects(name, group, sceneMgr, children[i].getPtr(), objectlist, flags, animflags, partflags); } } } @@ -679,7 +681,7 @@ public: // Create a base skeleton entity if this NIF needs one createSkelBase(name, group, sceneMgr, node, objectlist); } - createObjects(name, group, sceneMgr, node, objectlist, flags, 0); + createObjects(name, group, sceneMgr, node, objectlist, flags, 0, 0); } }; diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index 28df4894d..75bc90715 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -165,6 +165,7 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */ node->recType == Nif::RC_NiTriShape || /* Handled in the mesh loader */ node->recType == Nif::RC_NiBSAnimationNode || /* Handled in the object loader */ + node->recType == Nif::RC_NiBSParticleNode || node->recType == Nif::RC_NiCamera || node->recType == Nif::RC_NiAutoNormalParticles || node->recType == Nif::RC_NiRotatingParticles From 7000a1aa3eba373f33741e7f208e1af8bd6c45e7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 14 Apr 2013 13:50:55 -0700 Subject: [PATCH 04/99] Add a ParticleSystemController --- components/nifogre/ogrenifloader.cpp | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 1ed5e24a3..69f14b663 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -329,6 +329,35 @@ public: typedef DefaultFunction Function; }; +class ParticleSystemController +{ +public: + class Value : public Ogre::ControllerValue + { + private: + Ogre::ParticleSystem *mParticleSys; + float mEmitStart; + float mEmitStop; + + public: + Value(Ogre::ParticleSystem *psys, const Nif::NiParticleSystemController *pctrl) + : mParticleSys(psys) + , mEmitStart(pctrl->startTime) + , mEmitStop(pctrl->stopTime) + { + } + + Ogre::Real getValue() const + { return 0.0f; } + + void setValue(Ogre::Real value) + { + mParticleSys->setEmitting(value >= mEmitStart && value < mEmitStop); + } + }; + + typedef DefaultFunction Function; +}; /** Object creator for NIFs. This is the main class responsible for creating @@ -521,6 +550,13 @@ class NIFObjectLoader Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys); } + + Ogre::ControllerValueRealPtr srcval((partflags&0x20) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW ParticleSystemController::Value(partsys, partctrl)); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW ParticleSystemController::Function(partctrl, (partflags&0x20))); + + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } ctrl = ctrl->next; } From f9dee25fd1c0218fd40ac11e2b3406bbb70283b5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 14 Apr 2013 16:56:35 -0700 Subject: [PATCH 05/99] Store the base skeleton entity in MWRender::Animation --- apps/openmw/mwrender/animation.cpp | 21 ++++++++++++--------- apps/openmw/mwrender/animation.hpp | 3 ++- apps/openmw/mwrender/npcanimation.cpp | 5 ++--- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index a849b20ef..d6f762ab7 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -33,6 +33,7 @@ Animation::Animation(const MWWorld::Ptr &ptr) : mPtr(ptr) , mController(NULL) , mInsert(NULL) + , mSkelBase(NULL) , mAccumRoot(NULL) , mNonAccumRoot(NULL) , mAccumulate(Ogre::Vector3::ZERO) @@ -65,7 +66,7 @@ Animation::~Animation() void Animation::setAnimationSources(const std::vector &names) { - if(!mObjectList.mSkelBase) + if(!mSkelBase) return; Ogre::SceneManager *sceneMgr = mInsert->getCreator(); @@ -81,7 +82,7 @@ void Animation::setAnimationSources(const std::vector &names) mAnimationSources.clear(); Ogre::SharedPtr > ctrlval(OGRE_NEW AnimationValue(this)); - Ogre::SkeletonInstance *skelinst = mObjectList.mSkelBase->getSkeleton(); + Ogre::SkeletonInstance *skelinst = mSkelBase->getSkeleton(); std::vector::const_iterator nameiter; for(nameiter = names.begin();nameiter != names.end();nameiter++) { @@ -127,7 +128,7 @@ void Animation::setAnimationSources(const std::vector &names) if(!mNonAccumRoot) { mAccumRoot = mInsert; - mNonAccumRoot = mObjectList.mSkelBase->getSkeleton()->getBone(bone->getName()); + mNonAccumRoot = mSkelBase->getSkeleton()->getBone(bone->getName()); } for(int i = 0;i < skel->getNumAnimations();i++) @@ -152,7 +153,9 @@ void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model mObjectList = NifOgre::Loader::createObjects(mInsert, model); if(mObjectList.mSkelBase) { - Ogre::AnimationStateSet *aset = mObjectList.mSkelBase->getAllAnimationStates(); + mSkelBase = mObjectList.mSkelBase; + + Ogre::AnimationStateSet *aset = mSkelBase->getAllAnimationStates(); Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); while(asiter.hasMoreElements()) { @@ -164,7 +167,7 @@ void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model // Set the bones as manually controlled since we're applying the // transformations manually (needed if we want to apply an animation // from one skeleton onto another). - Ogre::SkeletonInstance *skelinst = mObjectList.mSkelBase->getSkeleton(); + Ogre::SkeletonInstance *skelinst = mSkelBase->getSkeleton(); Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); while(boneiter.hasMoreElements()) boneiter.getNext()->setManuallyControlled(true); @@ -182,9 +185,9 @@ void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model Ogre::Node *Animation::getNode(const std::string &name) { - if(mObjectList.mSkelBase) + if(mSkelBase) { - Ogre::SkeletonInstance *skel = mObjectList.mSkelBase->getSkeleton(); + Ogre::SkeletonInstance *skel = mSkelBase->getSkeleton(); if(skel->hasBone(name)) return skel->getBone(name); } @@ -508,11 +511,11 @@ Ogre::Vector3 Animation::runAnimation(float timepassed) for(size_t i = 0;i < mCurrentControllers->size();i++) (*mCurrentControllers)[i].update(); - if(mObjectList.mSkelBase) + if(mSkelBase) { // HACK: Dirty the animation state set so that Ogre will apply the // transformations to entities this skeleton instance is shared with. - mObjectList.mSkelBase->getAllAnimationStates()->_notifyDirty(); + mSkelBase->getAllAnimationStates()->_notifyDirty(); } return movement; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 029c56523..32344a2c0 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -42,7 +42,8 @@ protected: MWWorld::Ptr mPtr; MWMechanics::CharacterController *mController; - Ogre::SceneNode* mInsert; + Ogre::SceneNode *mInsert; + Ogre::Entity *mSkelBase; NifOgre::ObjectList mObjectList; std::map mTextKeys; Ogre::Node *mAccumRoot; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 96220f47d..a2446b867 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -359,8 +359,7 @@ void NpcAnimation::updateParts(bool forceupdate) NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename) { - NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mObjectList.mSkelBase, bonename, - mInsert, model); + NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); for(size_t i = 0;i < objects.mEntities.size();i++) { objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); @@ -408,7 +407,7 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) mTimeToChange -= timepassed; Ogre::Vector3 ret = Animation::runAnimation(timepassed); - const Ogre::SkeletonInstance *skelsrc = mObjectList.mSkelBase->getSkeleton(); + const Ogre::SkeletonInstance *skelsrc = mSkelBase->getSkeleton(); for(size_t i = 0;i < sPartListSize;i++) { Ogre::Entity *ent = mObjectParts[i].mSkelBase; From 69084139aaf2c4711061dd62a84666d6f34c6d10 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 14 Apr 2013 17:58:21 -0700 Subject: [PATCH 06/99] Use an array of objectlists, instead of one base objectlist and an array of 'animation sources' --- apps/openmw/mwrender/activatoranimation.cpp | 10 +- apps/openmw/mwrender/animation.cpp | 136 +++++++------------- apps/openmw/mwrender/animation.hpp | 14 +- apps/openmw/mwrender/creatureanimation.cpp | 21 ++- apps/openmw/mwrender/npcanimation.cpp | 27 ++-- 5 files changed, 75 insertions(+), 133 deletions(-) diff --git a/apps/openmw/mwrender/activatoranimation.cpp b/apps/openmw/mwrender/activatoranimation.cpp index 4630208b4..27ddce339 100644 --- a/apps/openmw/mwrender/activatoranimation.cpp +++ b/apps/openmw/mwrender/activatoranimation.cpp @@ -27,9 +27,9 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) std::string mesh = "meshes\\" + ref->mBase->mModel; createObjectList(mPtr.getRefData().getBaseNode(), mesh); - for(size_t i = 0;i < mObjectList.mEntities.size();i++) + for(size_t i = 0;i < mObjectLists[0].mEntities.size();i++) { - Ogre::Entity *ent = mObjectList.mEntities[i]; + Ogre::Entity *ent = mObjectLists[0].mEntities[i]; ent->setVisibilityFlags(RV_Misc); for(unsigned int j=0; j < ent->getNumSubEntities(); ++j) @@ -38,14 +38,12 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); } } - for(size_t i = 0;i < mObjectList.mParticles.size();i++) + for(size_t i = 0;i < mObjectLists[0].mParticles.size();i++) { - Ogre::ParticleSystem *part = mObjectList.mParticles[i]; + Ogre::ParticleSystem *part = mObjectLists[0].mParticles[i]; part->setVisibilityFlags(RV_Misc); - part->setRenderQueueGroup(RQG_Alpha); } - setAnimationSource(mesh); } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index d6f762ab7..30bcd3c4c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -55,68 +55,45 @@ Animation::~Animation() if(mInsert) { Ogre::SceneManager *sceneMgr = mInsert->getCreator(); - destroyObjectList(sceneMgr, mObjectList); - - for(size_t i = 0;i < mAnimationSources.size();i++) - destroyObjectList(sceneMgr, mAnimationSources[i]); - mAnimationSources.clear(); + for(size_t i = 0;i < mObjectLists.size();i++) + destroyObjectList(sceneMgr, mObjectLists[i]); + mObjectLists.clear(); } } -void Animation::setAnimationSources(const std::vector &names) +void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model) { - if(!mSkelBase) - return; - Ogre::SceneManager *sceneMgr = mInsert->getCreator(); + assert(!mInsert); + mInsert = node->createChildSceneNode(); + assert(mInsert); - mCurrentControllers = &mObjectList.mControllers; - mCurrentAnim = NULL; - mCurrentKeys = NULL; - mAnimVelocity = 0.0f; - mAccumRoot = NULL; - mNonAccumRoot = NULL; - mTextKeys.clear(); - for(size_t i = 0;i < mAnimationSources.size();i++) - destroyObjectList(sceneMgr, mAnimationSources[i]); - mAnimationSources.clear(); + mObjectLists.resize(1); - Ogre::SharedPtr > ctrlval(OGRE_NEW AnimationValue(this)); - Ogre::SkeletonInstance *skelinst = mSkelBase->getSkeleton(); - std::vector::const_iterator nameiter; - for(nameiter = names.begin();nameiter != names.end();nameiter++) + mObjectLists[0] = NifOgre::Loader::createObjects(mInsert, model); + if(mObjectLists[0].mSkelBase) { - mAnimationSources.push_back(NifOgre::Loader::createObjectBase(sceneMgr, *nameiter)); - if(!mAnimationSources.back().mSkelBase) + mSkelBase = mObjectLists[0].mSkelBase; + + Ogre::AnimationStateSet *aset = mObjectLists[0].mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); + while(asiter.hasMoreElements()) { - std::cerr<< "Failed to get skeleton source "<<*nameiter < *dstval = dynamic_cast*>(objects.mControllers[i].getDestination().getPointer()); - if(!dstval) continue; - - const Ogre::String &trgtname = dstval->getNode()->getName(); - if(!skelinst->hasBone(trgtname)) continue; - - Ogre::Bone *bone = skelinst->getBone(trgtname); - dstval->setNode(bone); + Ogre::AnimationState *state = asiter.getNext(); + state->setEnabled(false); + state->setLoop(false); } - for(size_t i = 0;i < objects.mControllers.size();i++) - { - if(objects.mControllers[i].getSource().isNull()) - objects.mControllers[i].setSource(ctrlval); - } + // Set the bones as manually controlled since we're applying the + // transformations manually (needed if we want to apply an animation + // from one skeleton onto another). + Ogre::SkeletonInstance *skelinst = mObjectLists[0].mSkelBase->getSkeleton(); + Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); + while(boneiter.hasMoreElements()) + boneiter.getNext()->setManuallyControlled(true); - Ogre::Entity *ent = objects.mSkelBase; - Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(ent->getSkeleton()->getName()); - Ogre::Skeleton::BoneIterator boneiter = skel->getBoneIterator(); + Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(skelinst->getName()); + boneiter = skel->getBoneIterator(); while(boneiter.hasMoreElements()) { Ogre::Bone *bone = boneiter.getNext(); @@ -143,43 +120,14 @@ void Animation::setAnimationSources(const std::vector &names) break; } } -} - -void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model) -{ - mInsert = node->createChildSceneNode(); - assert(mInsert); - - mObjectList = NifOgre::Loader::createObjects(mInsert, model); - if(mObjectList.mSkelBase) - { - mSkelBase = mObjectList.mSkelBase; - - Ogre::AnimationStateSet *aset = mSkelBase->getAllAnimationStates(); - Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); - while(asiter.hasMoreElements()) - { - Ogre::AnimationState *state = asiter.getNext(); - state->setEnabled(false); - state->setLoop(false); - } - - // Set the bones as manually controlled since we're applying the - // transformations manually (needed if we want to apply an animation - // from one skeleton onto another). - Ogre::SkeletonInstance *skelinst = mSkelBase->getSkeleton(); - Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); - while(boneiter.hasMoreElements()) - boneiter.getNext()->setManuallyControlled(true); - } Ogre::SharedPtr > ctrlval(OGRE_NEW AnimationValue(this)); - for(size_t i = 0;i < mObjectList.mControllers.size();i++) + for(size_t i = 0;i < mObjectLists[0].mControllers.size();i++) { - if(mObjectList.mControllers[i].getSource().isNull()) - mObjectList.mControllers[i].setSource(ctrlval); + if(mObjectLists[0].mControllers[i].getSource().isNull()) + mObjectLists[0].mControllers[i].setSource(ctrlval); } - mCurrentControllers = &mObjectList.mControllers; + mCurrentControllers = &mObjectLists[0].mControllers; } @@ -197,9 +145,9 @@ Ogre::Node *Animation::getNode(const std::string &name) bool Animation::hasAnimation(const std::string &anim) { - for(std::vector::const_iterator iter(mAnimationSources.begin());iter != mAnimationSources.end();iter++) + for(std::vector::const_iterator iter(mObjectLists.begin());iter != mObjectLists.end();iter++) { - if(iter->mSkelBase->hasAnimationState(anim)) + if(iter->mSkelBase && iter->mSkelBase->hasAnimationState(anim)) return true; } return false; @@ -449,7 +397,7 @@ void Animation::play(const std::string &groupname, const std::string &start, con try { bool found = false; /* Look in reverse; last-inserted source has priority. */ - for(std::vector::reverse_iterator iter(mAnimationSources.rbegin());iter != mAnimationSources.rend();iter++) + for(std::vector::reverse_iterator iter(mObjectLists.rbegin());iter != mObjectLists.rend();iter++) { if(iter->mSkelBase->hasAnimationState(groupname)) { @@ -513,9 +461,19 @@ Ogre::Vector3 Animation::runAnimation(float timepassed) if(mSkelBase) { - // HACK: Dirty the animation state set so that Ogre will apply the - // transformations to entities this skeleton instance is shared with. - mSkelBase->getAllAnimationStates()->_notifyDirty(); + const Ogre::SkeletonInstance *skelsrc = mSkelBase->getSkeleton(); + for(size_t i = 0;i < mObjectLists.size();i++) + { + Ogre::Entity *ent = mObjectLists[i].mSkelBase; + if(!ent) continue; + + Ogre::SkeletonInstance *skeldst = ent->getSkeleton(); + if(skelsrc != skeldst) + updateSkeletonInstance(skelsrc, skeldst); + // HACK: Dirty the animation state set so that Ogre will apply the + // transformations to entities this skeleton instance is shared with. + ent->getAllAnimationStates()->_notifyDirty(); + } } return movement; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 32344a2c0..3c7433db9 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -44,15 +44,13 @@ protected: Ogre::SceneNode *mInsert; Ogre::Entity *mSkelBase; - NifOgre::ObjectList mObjectList; + std::vector mObjectLists; std::map mTextKeys; Ogre::Node *mAccumRoot; Ogre::Bone *mNonAccumRoot; Ogre::Vector3 mAccumulate; Ogre::Vector3 mLastPosition; - std::vector mAnimationSources; - std::vector > *mCurrentControllers; NifOgre::TextKeyMap *mCurrentKeys; NifOgre::TextKeyMap::const_iterator mNextKey; @@ -83,16 +81,6 @@ protected: bool handleEvent(float time, const std::string &evt); - /* Specifies a list of skeleton names to use as animation sources. */ - void setAnimationSources(const std::vector &names); - - /* Specifies a single skeleton name to use as an animation source. */ - void setAnimationSource(const std::string &name) - { - std::vector names(1, name); - setAnimationSources(names); - } - void createObjectList(Ogre::SceneNode *node, const std::string &model); static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index c714a2372..7084974b8 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -26,10 +26,15 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) { std::string model = "meshes\\"+ref->mBase->mModel; - createObjectList(mPtr.getRefData().getBaseNode(), model); - for(size_t i = 0;i < mObjectList.mEntities.size();i++) + std::vector names; + if((ref->mBase->mFlags&ESM::Creature::Biped)) + names.push_back("meshes\\base_anim.nif"); + names.push_back(model); + + createObjectList(mPtr.getRefData().getBaseNode(), model/*names*/); + for(size_t i = 0;i < mObjectLists[0].mEntities.size();i++) { - Ogre::Entity *ent = mObjectList.mEntities[i]; + Ogre::Entity *ent = mObjectLists[0].mEntities[i]; ent->setVisibilityFlags(RV_Actors); for(unsigned int j=0; j < ent->getNumSubEntities(); ++j) @@ -38,19 +43,13 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); } } - for(size_t i = 0;i < mObjectList.mParticles.size();i++) + for(size_t i = 0;i < mObjectLists[0].mParticles.size();i++) { - Ogre::ParticleSystem *part = mObjectList.mParticles[i]; + Ogre::ParticleSystem *part = mObjectLists[0].mParticles[i]; part->setVisibilityFlags(RV_Actors); part->setRenderQueueGroup(RQG_Alpha); } - - std::vector names; - if((ref->mBase->mFlags&ESM::Creature::Biped)) - names.push_back("meshes\\base_anim.nif"); - names.push_back(model); - setAnimationSources(names); } } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index a2446b867..a713ae79e 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -96,10 +96,18 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - createObjectList(node, smodel); - for(size_t i = 0;i < mObjectList.mEntities.size();i++) + std::vector skelnames(1, smodel); + if(!mNpc->isMale() && !isBeast) + skelnames.push_back("meshes\\base_anim_female.nif"); + else if(mBodyPrefix.find("argonian") != std::string::npos) + skelnames.push_back("meshes\\argonian_swimkna.nif"); + if(mNpc->mModel.length() > 0) + skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel)); + + createObjectList(node, smodel/*skelnames*/); + for(size_t i = 0;i < mObjectLists[0].mEntities.size();i++) { - Ogre::Entity *base = mObjectList.mEntities[i]; + Ogre::Entity *base = mObjectLists[0].mEntities[i]; base->getUserObjectBindings().setUserAny(Ogre::Any(-1)); if (mVisibilityFlags != 0) @@ -111,9 +119,9 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); } } - for(size_t i = 0;i < mObjectList.mParticles.size();i++) + for(size_t i = 0;i < mObjectLists[0].mParticles.size();i++) { - Ogre::ParticleSystem *part = mObjectList.mParticles[i]; + Ogre::ParticleSystem *part = mObjectLists[0].mParticles[i]; part->getUserObjectBindings().setUserAny(Ogre::Any(-1)); if(mVisibilityFlags != 0) @@ -121,15 +129,6 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor part->setRenderQueueGroup(RQG_Alpha); } - std::vector skelnames(1, smodel); - if(!mNpc->isMale() && !isBeast) - skelnames.push_back("meshes\\base_anim_female.nif"); - else if(mBodyPrefix.find("argonian") != std::string::npos) - skelnames.push_back("meshes\\argonian_swimkna.nif"); - if(mNpc->mModel.length() > 0) - skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel)); - setAnimationSources(skelnames); - forceUpdate(); } From 1b1f9f7921d91bc2c2c853cfc0b0f5c71fba0574 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 15 Apr 2013 18:55:28 -0700 Subject: [PATCH 07/99] Allow multiple ObjectLists to be created for Animations. addObjectList may not currently be called outside of the related constructor. --- apps/openmw/mwrender/activatoranimation.cpp | 13 ++-- apps/openmw/mwrender/animation.cpp | 80 +++++++++++++-------- apps/openmw/mwrender/animation.hpp | 2 +- apps/openmw/mwrender/creatureanimation.cpp | 17 +++-- apps/openmw/mwrender/npcanimation.cpp | 39 +++------- components/nifogre/ogrenifloader.cpp | 7 +- components/nifogre/ogrenifloader.hpp | 2 +- 7 files changed, 82 insertions(+), 78 deletions(-) diff --git a/apps/openmw/mwrender/activatoranimation.cpp b/apps/openmw/mwrender/activatoranimation.cpp index 27ddce339..1f9a2e23c 100644 --- a/apps/openmw/mwrender/activatoranimation.cpp +++ b/apps/openmw/mwrender/activatoranimation.cpp @@ -24,12 +24,13 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) assert (ref->mBase != NULL); if(!ref->mBase->mModel.empty()) { - std::string mesh = "meshes\\" + ref->mBase->mModel; + const std::string name = "meshes\\"+ref->mBase->mModel; - createObjectList(mPtr.getRefData().getBaseNode(), mesh); - for(size_t i = 0;i < mObjectLists[0].mEntities.size();i++) + addObjectList(mPtr.getRefData().getBaseNode(), name, false); + const NifOgre::ObjectList &objlist = mObjectLists.back(); + for(size_t i = 0;i < objlist.mEntities.size();i++) { - Ogre::Entity *ent = mObjectLists[0].mEntities[i]; + Ogre::Entity *ent = objlist.mEntities[i]; ent->setVisibilityFlags(RV_Misc); for(unsigned int j=0; j < ent->getNumSubEntities(); ++j) @@ -38,9 +39,9 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); } } - for(size_t i = 0;i < mObjectLists[0].mParticles.size();i++) + for(size_t i = 0;i < objlist.mParticles.size();i++) { - Ogre::ParticleSystem *part = mObjectLists[0].mParticles[i]; + Ogre::ParticleSystem *part = objlist.mParticles[i]; part->setVisibilityFlags(RV_Misc); part->setRenderQueueGroup(RQG_Alpha); } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 30bcd3c4c..979046ea0 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -36,7 +36,7 @@ Animation::Animation(const MWWorld::Ptr &ptr) , mSkelBase(NULL) , mAccumRoot(NULL) , mNonAccumRoot(NULL) - , mAccumulate(Ogre::Vector3::ZERO) + , mAccumulate(0.0f) , mLastPosition(0.0f) , mCurrentControllers(NULL) , mCurrentKeys(NULL) @@ -62,20 +62,24 @@ Animation::~Animation() } -void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model) +void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, bool baseonly) { - assert(!mInsert); - mInsert = node->createChildSceneNode(); - assert(mInsert); - - mObjectLists.resize(1); - - mObjectLists[0] = NifOgre::Loader::createObjects(mInsert, model); - if(mObjectLists[0].mSkelBase) + if(!mInsert) { - mSkelBase = mObjectLists[0].mSkelBase; + mInsert = node->createChildSceneNode(); + assert(mInsert); + } + Ogre::SharedPtr > ctrlval(OGRE_NEW AnimationValue(this)); - Ogre::AnimationStateSet *aset = mObjectLists[0].mSkelBase->getAllAnimationStates(); + mObjectLists.push_back(!baseonly ? NifOgre::Loader::createObjects(mInsert, model) : + NifOgre::Loader::createObjectBase(mInsert, model)); + NifOgre::ObjectList &objlist = mObjectLists.back(); + if(objlist.mSkelBase) + { + if(!mSkelBase) + mSkelBase = objlist.mSkelBase; + + Ogre::AnimationStateSet *aset = objlist.mSkelBase->getAllAnimationStates(); Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); while(asiter.hasMoreElements()) { @@ -87,11 +91,28 @@ void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model // Set the bones as manually controlled since we're applying the // transformations manually (needed if we want to apply an animation // from one skeleton onto another). - Ogre::SkeletonInstance *skelinst = mObjectLists[0].mSkelBase->getSkeleton(); + Ogre::SkeletonInstance *skelinst = objlist.mSkelBase->getSkeleton(); Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); while(boneiter.hasMoreElements()) boneiter.getNext()->setManuallyControlled(true); + if(mSkelBase != objlist.mSkelBase) + { + Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); + for(size_t i = 0;i < objlist.mControllers.size();i++) + { + NifOgre::NodeTargetValue *dstval; + dstval = dynamic_cast*>(objlist.mControllers[i].getDestination().getPointer()); + if(!dstval) continue; + + const Ogre::String &trgtname = dstval->getNode()->getName(); + if(!baseinst->hasBone(trgtname)) continue; + + Ogre::Bone *bone = baseinst->getBone(trgtname); + dstval->setNode(bone); + } + } + Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(skelinst->getName()); boneiter = skel->getBoneIterator(); while(boneiter.hasMoreElements()) @@ -120,14 +141,14 @@ void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model break; } } - - Ogre::SharedPtr > ctrlval(OGRE_NEW AnimationValue(this)); - for(size_t i = 0;i < mObjectLists[0].mControllers.size();i++) + for(size_t i = 0;i < objlist.mControllers.size();i++) { - if(mObjectLists[0].mControllers[i].getSource().isNull()) - mObjectLists[0].mControllers[i].setSource(ctrlval); + if(objlist.mControllers[i].getSource().isNull()) + objlist.mControllers[i].setSource(ctrlval); } - mCurrentControllers = &mObjectLists[0].mControllers; + + if(!mCurrentControllers || (*mCurrentControllers).size() == 0) + mCurrentControllers = &objlist.mControllers; } @@ -399,9 +420,10 @@ void Animation::play(const std::string &groupname, const std::string &start, con /* Look in reverse; last-inserted source has priority. */ for(std::vector::reverse_iterator iter(mObjectLists.rbegin());iter != mObjectLists.rend();iter++) { - if(iter->mSkelBase->hasAnimationState(groupname)) + if(iter->mSkelBase && iter->mSkelBase->hasAnimationState(groupname)) { - mCurrentAnim = iter->mSkelBase->getSkeleton()->getAnimation(groupname); + Ogre::SkeletonInstance *skel = iter->mSkelBase->getSkeleton(); + mCurrentAnim = skel->getAnimation(groupname); mCurrentKeys = &mTextKeys[groupname]; mCurrentControllers = &iter->mControllers; mAnimVelocity = 0.0f; @@ -456,20 +478,22 @@ Ogre::Vector3 Animation::runAnimation(float timepassed) if(!handleEvent(time, evt)) break; } - for(size_t i = 0;i < mCurrentControllers->size();i++) + + for(size_t i = 0;i < (*mCurrentControllers).size();i++) (*mCurrentControllers)[i].update(); if(mSkelBase) { - const Ogre::SkeletonInstance *skelsrc = mSkelBase->getSkeleton(); - for(size_t i = 0;i < mObjectLists.size();i++) + const Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); + for(std::vector::iterator iter(mObjectLists.begin());iter != mObjectLists.end();iter++) { - Ogre::Entity *ent = mObjectLists[i].mSkelBase; + Ogre::Entity *ent = iter->mSkelBase; if(!ent) continue; - Ogre::SkeletonInstance *skeldst = ent->getSkeleton(); - if(skelsrc != skeldst) - updateSkeletonInstance(skelsrc, skeldst); + Ogre::SkeletonInstance *inst = ent->getSkeleton(); + if(baseinst != inst) + updateSkeletonInstance(baseinst, inst); + // HACK: Dirty the animation state set so that Ogre will apply the // transformations to entities this skeleton instance is shared with. ent->getAllAnimationStates()->_notifyDirty(); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 3c7433db9..79262f3c5 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -81,7 +81,7 @@ protected: bool handleEvent(float time, const std::string &evt); - void createObjectList(Ogre::SceneNode *node, const std::string &model); + void addObjectList(Ogre::SceneNode *node, const std::string &model, bool baseonly); static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); public: diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 7084974b8..a48b8da9f 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -26,15 +26,15 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) { std::string model = "meshes\\"+ref->mBase->mModel; - std::vector names; if((ref->mBase->mFlags&ESM::Creature::Biped)) - names.push_back("meshes\\base_anim.nif"); - names.push_back(model); + addObjectList(mPtr.getRefData().getBaseNode(), "meshes\\base_anim.nif", true); - createObjectList(mPtr.getRefData().getBaseNode(), model/*names*/); - for(size_t i = 0;i < mObjectLists[0].mEntities.size();i++) + addObjectList(mPtr.getRefData().getBaseNode(), model, false); + + const NifOgre::ObjectList &objlist = mObjectLists.back(); + for(size_t i = 0;i < objlist.mEntities.size();i++) { - Ogre::Entity *ent = mObjectLists[0].mEntities[i]; + Ogre::Entity *ent = objlist.mEntities[i]; ent->setVisibilityFlags(RV_Actors); for(unsigned int j=0; j < ent->getNumSubEntities(); ++j) @@ -43,11 +43,10 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); } } - for(size_t i = 0;i < mObjectLists[0].mParticles.size();i++) + for(size_t i = 0;i < objlist.mParticles.size();i++) { - Ogre::ParticleSystem *part = mObjectLists[0].mParticles[i]; + Ogre::ParticleSystem *part = objlist.mParticles[i]; part->setVisibilityFlags(RV_Actors); - part->setRenderQueueGroup(RQG_Alpha); } } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index a713ae79e..3ef357440 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -96,38 +96,13 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - std::vector skelnames(1, smodel); + addObjectList(node, smodel, true); if(!mNpc->isMale() && !isBeast) - skelnames.push_back("meshes\\base_anim_female.nif"); + addObjectList(node, "meshes\\base_anim_female.nif", true); else if(mBodyPrefix.find("argonian") != std::string::npos) - skelnames.push_back("meshes\\argonian_swimkna.nif"); + addObjectList(node, "meshes\\argonian_swimkna.nif", true); if(mNpc->mModel.length() > 0) - skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel)); - - createObjectList(node, smodel/*skelnames*/); - for(size_t i = 0;i < mObjectLists[0].mEntities.size();i++) - { - Ogre::Entity *base = mObjectLists[0].mEntities[i]; - - base->getUserObjectBindings().setUserAny(Ogre::Any(-1)); - if (mVisibilityFlags != 0) - base->setVisibilityFlags(mVisibilityFlags); - - for(unsigned int j=0; j < base->getNumSubEntities(); ++j) - { - Ogre::SubEntity* subEnt = base->getSubEntity(j); - subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); - } - } - for(size_t i = 0;i < mObjectLists[0].mParticles.size();i++) - { - Ogre::ParticleSystem *part = mObjectLists[0].mParticles[i]; - - part->getUserObjectBindings().setUserAny(Ogre::Any(-1)); - if(mVisibilityFlags != 0) - part->setVisibilityFlags(mVisibilityFlags); - part->setRenderQueueGroup(RQG_Alpha); - } + addObjectList(node, "meshes\\"+mNpc->mModel, true); forceUpdate(); } @@ -406,14 +381,16 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) mTimeToChange -= timepassed; Ogre::Vector3 ret = Animation::runAnimation(timepassed); - const Ogre::SkeletonInstance *skelsrc = mSkelBase->getSkeleton(); + + Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); for(size_t i = 0;i < sPartListSize;i++) { Ogre::Entity *ent = mObjectParts[i].mSkelBase; if(!ent) continue; - updateSkeletonInstance(skelsrc, ent->getSkeleton()); + updateSkeletonInstance(baseinst, ent->getSkeleton()); ent->getAllAnimationStates()->_notifyDirty(); } + return ret; } diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 69f14b663..04cdb142f 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -800,12 +800,15 @@ ObjectList Loader::createObjects(Ogre::Entity *parent, const std::string &bonena } -ObjectList Loader::createObjectBase(Ogre::SceneManager *sceneMgr, std::string name, const std::string &group) +ObjectList Loader::createObjectBase(Ogre::SceneNode *parentNode, std::string name, const std::string &group) { ObjectList objectlist; Misc::StringUtils::toLower(name); - NIFObjectLoader::load(sceneMgr, objectlist, name, group, 0xC0000000); + NIFObjectLoader::load(parentNode->getCreator(), objectlist, name, group, 0xC0000000); + + if(objectlist.mSkelBase) + parentNode->attachObject(objectlist.mSkelBase); return objectlist; } diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index fa5182aea..e3bb55064 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -69,7 +69,7 @@ public: std::string name, const std::string &group="General"); - static ObjectList createObjectBase(Ogre::SceneManager *sceneMgr, + static ObjectList createObjectBase(Ogre::SceneNode *parentNode, std::string name, const std::string &group="General"); }; From 4ce98e9bd65ff2035e2f36e89a90cb6f892472fe Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 15 Apr 2013 20:37:10 -0700 Subject: [PATCH 08/99] Store iterators for start and stop keys --- apps/openmw/mwrender/animation.cpp | 51 ++++++++++++------------------ apps/openmw/mwrender/animation.hpp | 7 ++-- 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 979046ea0..11876a247 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -38,11 +38,10 @@ Animation::Animation(const MWWorld::Ptr &ptr) , mNonAccumRoot(NULL) , mAccumulate(0.0f) , mLastPosition(0.0f) + , mCurrentAnim(NULL) , mCurrentControllers(NULL) , mCurrentKeys(NULL) - , mCurrentAnim(NULL) , mCurrentTime(0.0f) - , mStopTime(0.0f) , mPlaying(false) , mLooping(false) , mAnimVelocity(0.0f) @@ -313,35 +312,26 @@ Ogre::Vector3 Animation::updatePosition() void Animation::reset(const std::string &start, const std::string &stop) { - mNextKey = mCurrentKeys->begin(); + mStartKey = mCurrentKeys->begin(); - while(mNextKey != mCurrentKeys->end() && mNextKey->second != start) - mNextKey++; - if(mNextKey != mCurrentKeys->end()) - mCurrentTime = mNextKey->first; + while(mStartKey != mCurrentKeys->end() && mStartKey->second != start) + mStartKey++; + if(mStartKey != mCurrentKeys->end()) + mCurrentTime = mStartKey->first; else { - mNextKey = mCurrentKeys->begin(); - while(mNextKey != mCurrentKeys->end() && mNextKey->second != "start") - mNextKey++; - if(mNextKey != mCurrentKeys->end()) - mCurrentTime = mNextKey->first; - else - { - mNextKey = mCurrentKeys->begin(); - mCurrentTime = 0.0f; - } + mStartKey = mCurrentKeys->begin(); + mCurrentTime = mStartKey->first; } + mNextKey = mStartKey; if(stop.length() > 0) { - NifOgre::TextKeyMap::const_iterator stopKey = mNextKey; - while(stopKey != mCurrentKeys->end() && stopKey->second != stop) - stopKey++; - if(stopKey != mCurrentKeys->end()) - mStopTime = stopKey->first; - else - mStopTime = mCurrentAnim->getLength(); + mStopKey = mStartKey; + while(mStopKey != mCurrentKeys->end() && mStopKey->second != stop) + mStopKey++; + if(mStopKey == mCurrentKeys->end()) + mStopKey--; } if(mNonAccumRoot) @@ -390,7 +380,7 @@ bool Animation::handleEvent(float time, const std::string &evt) { if(mLooping) { - reset("loop start", ""); + reset("loop start"); if(mCurrentTime >= time) return false; } @@ -400,7 +390,7 @@ bool Animation::handleEvent(float time, const std::string &evt) { if(mLooping) { - reset("loop start", ""); + reset("loop start"); if(mCurrentTime >= time) return false; return true; @@ -455,13 +445,11 @@ Ogre::Vector3 Animation::runAnimation(float timepassed) while(mCurrentAnim && mPlaying) { float targetTime = mCurrentTime + timepassed; - if(mNextKey == mCurrentKeys->end() || mNextKey->first > targetTime) + if(mNextKey->first > targetTime) { - mCurrentTime = std::min(mStopTime, targetTime); + mCurrentTime = targetTime; if(mNonAccumRoot) movement += updatePosition(); - mPlaying = (mLooping || mStopTime > mCurrentTime); - timepassed = targetTime - mCurrentTime; break; } @@ -472,7 +460,8 @@ Ogre::Vector3 Animation::runAnimation(float timepassed) mCurrentTime = time; if(mNonAccumRoot) movement += updatePosition(); - mPlaying = (mLooping || mStopTime > mCurrentTime); + + mPlaying = (mLooping || mStopKey->first > mCurrentTime); timepassed = targetTime - mCurrentTime; if(!handleEvent(time, evt)) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 79262f3c5..f8ddceeaa 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -50,13 +50,14 @@ protected: Ogre::Bone *mNonAccumRoot; Ogre::Vector3 mAccumulate; Ogre::Vector3 mLastPosition; + Ogre::Animation *mCurrentAnim; std::vector > *mCurrentControllers; NifOgre::TextKeyMap *mCurrentKeys; + NifOgre::TextKeyMap::const_iterator mStartKey; + NifOgre::TextKeyMap::const_iterator mStopKey; NifOgre::TextKeyMap::const_iterator mNextKey; - Ogre::Animation *mCurrentAnim; float mCurrentTime; - float mStopTime; bool mPlaying; bool mLooping; @@ -77,7 +78,7 @@ protected: * moving anything, and set the end time to the specified stop marker. If * the marker is not found, it resets to the beginning or end respectively. */ - void reset(const std::string &start, const std::string &stop); + void reset(const std::string &start, const std::string &stop=std::string()); bool handleEvent(float time, const std::string &evt); From 36170c5374402f06b49bcf63d7c856572072ebda Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 16 Apr 2013 01:20:32 -0700 Subject: [PATCH 09/99] Use flag enums instead of hard-coded hex values --- components/nif/node.hpp | 18 +++++++------ components/nifbullet/bulletnifloader.cpp | 6 ++--- components/nifogre/ogrenifloader.cpp | 32 +++++++++++++----------- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 9c345baab..917bc8add 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -128,13 +128,17 @@ struct NiNode : Node NodeList children; NodeList effects; - /* Known NiNode flags: - 0x01 hidden - 0x02 use mesh for collision - 0x04 use bounding box for collision (?) - 0x08 unknown, but common - 0x20, 0x40, 0x80 unknown - */ + enum Flags { + Flag_Hidden = 0x0001, + Flag_MeshCollision = 0x0002, + Flag_BBoxCollision = 0x0004 + }; + enum BSAnimFlags { + AnimFlag_AutoPlay = 0x0020 + }; + enum BSParticleFlags { + ParticleFlag_AutoPlay = 0x0020 + }; void read(NIFStream *nif) { diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 6bd43f6e3..a3eff95c3 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -255,9 +255,9 @@ void ManualBulletShapeLoader::handleNiTriShape(btTriangleMesh* mesh, const Nif:: assert(shape != NULL); // Interpret flags - bool hidden = (flags & 0x01) != 0; // Not displayed - bool collide = (flags & 0x02) != 0; // Use mesh for collision - bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision + bool hidden = (flags&Nif::NiNode::Flag_Hidden) != 0; + bool collide = (flags&Nif::NiNode::Flag_MeshCollision) != 0; + bool bbcollide = (flags&Nif::NiNode::Flag_BBoxCollision) != 0; // If the object was marked "NCO" earlier, it shouldn't collide with // anything. So don't do anything. diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 04cdb142f..2e77adb08 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -394,7 +394,7 @@ class NIFObjectLoader NIFMeshLoader::createMesh(name, fullname, group, shape->recIndex); Ogre::Entity *entity = sceneMgr->createEntity(fullname); - entity->setVisible(!(flags&0x01)); + entity->setVisible(!(flags&Nif::NiNode::Flag_Hidden)); objectlist.mEntities.push_back(entity); if(objectlist.mSkelBase) @@ -417,10 +417,11 @@ class NIFObjectLoader const Nif::NiUVController *uv = static_cast(ctrl.getPtr()); const Ogre::MaterialPtr &material = entity->getSubEntity(0)->getMaterial(); - Ogre::ControllerValueRealPtr srcval((animflags&0x20) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : - Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr())); - Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, (animflags&0x20))); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } @@ -551,10 +552,11 @@ class NIFObjectLoader objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys); } - Ogre::ControllerValueRealPtr srcval((partflags&0x20) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : - Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr srcval((partflags&Nif::NiNode::ParticleFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW ParticleSystemController::Value(partsys, partctrl)); - Ogre::ControllerFunctionRealPtr func(OGRE_NEW ParticleSystemController::Function(partctrl, (partflags&0x20))); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW ParticleSystemController::Function(partctrl, (partflags&Nif::NiNode::ParticleFlag_AutoPlay))); objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } @@ -568,7 +570,7 @@ class NIFObjectLoader objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys); } - partsys->setVisible(!(flags&0x01)); + partsys->setVisible(!(flags&Nif::NiNode::Flag_Hidden)); objectlist.mParticles.push_back(partsys); } @@ -627,10 +629,11 @@ class NIFObjectLoader int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); - Ogre::ControllerValueRealPtr srcval((animflags&0x20) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : - Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr())); - Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, (animflags&0x20))); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } @@ -641,10 +644,11 @@ class NIFObjectLoader { int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); - Ogre::ControllerValueRealPtr srcval((animflags&0x20) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : - Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); - Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, (animflags&0x20))); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } From 60fadaeaf0368df6c6bafafbabe24a233a74d124 Mon Sep 17 00:00:00 2001 From: Britt Mathis Date: Tue, 16 Apr 2013 20:16:22 -0400 Subject: [PATCH 10/99] Cleaned up includes in implementation files --- apps/openmw/mwgui/alchemywindow.cpp | 2 -- apps/openmw/mwgui/alchemywindow.hpp | 1 - apps/openmw/mwgui/birth.cpp | 2 -- apps/openmw/mwgui/charactercreation.cpp | 2 -- apps/openmw/mwgui/class.cpp | 5 ----- apps/openmw/mwgui/companionwindow.cpp | 1 - apps/openmw/mwgui/confirmationdialog.cpp | 5 ----- apps/openmw/mwgui/console.cpp | 6 ------ apps/openmw/mwgui/container.cpp | 12 ------------ apps/openmw/mwgui/countdialog.cpp | 3 --- apps/openmw/mwgui/cursor.cpp | 2 -- apps/openmw/mwgui/dialogue.cpp | 9 +-------- apps/openmw/mwgui/formatting.cpp | 1 - apps/openmw/mwgui/hud.cpp | 10 ---------- apps/openmw/mwgui/inventorywindow.cpp | 13 ------------- apps/openmw/mwgui/journalwindow.cpp | 3 --- apps/openmw/mwgui/levelupdialog.cpp | 2 -- apps/openmw/mwgui/loadingscreen.cpp | 9 --------- apps/openmw/mwgui/mainmenu.cpp | 1 - apps/openmw/mwgui/mapwindow.cpp | 4 ---- apps/openmw/mwgui/merchantrepair.cpp | 3 --- apps/openmw/mwgui/quickkeysmenu.cpp | 4 ---- apps/openmw/mwgui/race.cpp | 6 ------ apps/openmw/mwgui/review.cpp | 6 ------ apps/openmw/mwgui/settingswindow.cpp | 6 ------ apps/openmw/mwgui/soulgemdialog.cpp | 1 - apps/openmw/mwgui/spellbuyingwindow.cpp | 4 ---- apps/openmw/mwgui/spellcreationdialog.cpp | 8 -------- apps/openmw/mwgui/spellicons.cpp | 5 ----- apps/openmw/mwgui/spellwindow.cpp | 8 -------- apps/openmw/mwgui/statswindow.cpp | 6 ------ apps/openmw/mwgui/tooltips.cpp | 7 ------- apps/openmw/mwgui/tradewindow.cpp | 1 - apps/openmw/mwgui/travelwindow.cpp | 7 ------- apps/openmw/mwgui/waitdialog.cpp | 4 ---- apps/openmw/mwgui/widgets.cpp | 2 -- apps/openmw/mwgui/windowbase.cpp | 2 -- apps/openmw/mwgui/windowmanagerimp.cpp | 23 +---------------------- apps/openmw/mwgui/windowpinnablebase.cpp | 2 -- 39 files changed, 2 insertions(+), 196 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index a6121c234..24a4e205b 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -8,8 +8,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/manualref.hpp" -#include "../mwworld/containerstore.hpp" namespace { diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index 655a832c1..9189e4a63 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -10,7 +10,6 @@ namespace MWGui { - class AlchemyWindow : public WindowBase, public ContainerBase { public: diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 133bbd32f..9c3ebdc7d 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -3,8 +3,6 @@ #include #include -#include "../mwworld/esmstore.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 0dd6502c6..28f6d13ee 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -5,8 +5,6 @@ #include "class.hpp" #include "birth.hpp" #include "review.hpp" -#include "dialogue.hpp" -#include "mode.hpp" #include "inventorywindow.hpp" #include #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 6edad9e83..e4d32c933 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -1,11 +1,6 @@ #include "class.hpp" -#include - #include -#include - -#include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index 0a20c471a..22afc42f6 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -2,7 +2,6 @@ #include -#include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 904468f88..f431f2f64 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -1,10 +1,5 @@ #include "confirmationdialog.hpp" -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - namespace MWGui { ConfirmationDialog::ConfirmationDialog() : diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 1aebe57da..bd1b0fe7a 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -1,13 +1,7 @@ - #include "console.hpp" -#include -#include - #include -#include "../mwworld/esmstore.hpp" - #include "../mwscript/extensions.hpp" #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 8ee4754a3..d15a5acd3 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -1,11 +1,5 @@ #include "container.hpp" -#include -#include -#include -#include -#include - #include #include "../mwbase/environment.hpp" @@ -13,15 +7,9 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/manualref.hpp" -#include "../mwworld/containerstore.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "../mwclass/container.hpp" - -#include "widgets.hpp" #include "countdialog.hpp" #include "tradewindow.hpp" #include "inventorywindow.hpp" diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index d017cc198..fe5122455 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -2,9 +2,6 @@ #include -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - namespace MWGui { CountDialog::CountDialog() : diff --git a/apps/openmw/mwgui/cursor.cpp b/apps/openmw/mwgui/cursor.cpp index b0d164bed..c069eca15 100644 --- a/apps/openmw/mwgui/cursor.cpp +++ b/apps/openmw/mwgui/cursor.cpp @@ -2,13 +2,11 @@ #include #include -#include #include #include #include - namespace MWGui { diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index f3c11752e..392f64126 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -1,18 +1,11 @@ #include "dialogue.hpp" -#include -#include - -#include #include -#include "../mwworld/esmstore.hpp" - #include "../mwbase/environment.hpp" -#include "../mwbase/dialoguemanager.hpp" -#include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/world.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 7f28e9e17..b21b903bd 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -3,7 +3,6 @@ #include #include "../mwscript/interpretercontext.hpp" -#include "../mwworld/ptr.hpp" #include #include diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 84526a28d..988bcfc24 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -1,24 +1,14 @@ #include "hud.hpp" -#include - -#include -#include - #include #include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "../mwgui/widgets.hpp" - #include "inventorywindow.hpp" -#include "container.hpp" #include "console.hpp" #include "spellicons.hpp" diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 40c228fd5..e5e20b778 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -1,28 +1,15 @@ #include "inventorywindow.hpp" -#include -#include -#include -#include - #include -#include - #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/containerstore.hpp" -#include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/manualref.hpp" -#include "../mwworld/actiontake.hpp" #include "../mwworld/inventorystore.hpp" -#include "widgets.hpp" #include "bookwindow.hpp" #include "scrollwindow.hpp" #include "spellwindow.hpp" diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 35a687cc4..23588a4af 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -4,9 +4,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwbase/windowmanager.hpp" - -#include "../mwdialogue/journalentry.hpp" namespace { diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index fc1317e90..5857db4d2 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -8,12 +8,10 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/esmstore.hpp" #include "../mwworld/fallback.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" -#include "../mwmechanics/stat.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 858c3f36e..3b341574d 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -1,25 +1,16 @@ #include "loadingscreen.hpp" #include -#include #include #include -#include - - -#include #include #include "../mwbase/environment.hpp" -#include "../mwbase/inputmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include - - namespace MWGui { diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 5402d3542..ebd31d92c 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -3,7 +3,6 @@ #include #include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 17834be93..1ac1c2448 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -2,12 +2,8 @@ #include -#include -#include #include -#include - #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 53148cb3f..a98051c28 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -9,10 +9,7 @@ #include "../mwbase/soundmanager.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/containerstore.hpp" -#include "../mwworld/class.hpp" -#include "list.hpp" #include "inventorywindow.hpp" #include "tradewindow.hpp" diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 2deb37d30..a44f1791c 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -2,13 +2,9 @@ #include -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/actionequip.hpp" -#include "../mwmechanics/spells.hpp" -#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellsuccess.hpp" #include "../mwgui/inventorywindow.hpp" #include "../mwgui/bookwindow.hpp" diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 9a0d8a029..c031ad6b6 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -1,19 +1,13 @@ #include "race.hpp" -#include -#include - #include #include #include -#include "../mwworld/esmstore.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "widgets.hpp" #include "tooltips.hpp" using namespace MWGui; diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 562bf9774..31f5d737b 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -1,17 +1,11 @@ #include "review.hpp" -#include - -#include #include -#include "../mwworld/esmstore.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "widgets.hpp" #include "tooltips.hpp" #undef min diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 93b82e374..39ee4e01d 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -1,24 +1,18 @@ #include "settingswindow.hpp" #include -#include #include -#include #include #include #include -#include - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/inputmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwrender/renderingmanager.hpp" - #include "confirmationdialog.hpp" namespace diff --git a/apps/openmw/mwgui/soulgemdialog.cpp b/apps/openmw/mwgui/soulgemdialog.cpp index 4530a13d0..b95eec0b6 100644 --- a/apps/openmw/mwgui/soulgemdialog.cpp +++ b/apps/openmw/mwgui/soulgemdialog.cpp @@ -1,6 +1,5 @@ #include "soulgemdialog.hpp" -#include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "messagebox.hpp" diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 7d634df8d..cb2007565 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -1,7 +1,5 @@ #include "spellbuyingwindow.hpp" -#include - #include #include "../mwbase/environment.hpp" @@ -11,9 +9,7 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/manualref.hpp" -#include "../mwmechanics/spells.hpp" #include "../mwmechanics/creaturestats.hpp" #include "inventorywindow.hpp" diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index e1df9f9cd..45cf1b0aa 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -4,22 +4,14 @@ #include "../mwbase/windowmanager.hpp" -#include "../mwbase/world.hpp" -#include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/esmstore.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/class.hpp" -#include "../mwmechanics/spells.hpp" -#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellsuccess.hpp" - #include "tooltips.hpp" -#include "widgets.hpp" #include "class.hpp" #include "inventorywindow.hpp" #include "tradewindow.hpp" diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index 206db51ed..e762cc610 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -1,9 +1,5 @@ #include "spellicons.hpp" -#include -#include -#include - #include #include "../mwbase/world.hpp" @@ -14,7 +10,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwmechanics/activespells.hpp" #include "../mwmechanics/creaturestats.hpp" #include "tooltips.hpp" diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index d07f9ca54..bd1fef12b 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -1,22 +1,14 @@ #include "spellwindow.hpp" -#include #include #include -#include "../mwworld/esmstore.hpp" - -#include "../mwbase/world.hpp" -#include "../mwbase/environment.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/actionequip.hpp" -#include "../mwmechanics/spells.hpp" -#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellsuccess.hpp" #include "spellicons.hpp" diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index a00ec167d..3439ff31c 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -1,14 +1,9 @@ #include "statswindow.hpp" -#include -#include -#include - #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" @@ -18,7 +13,6 @@ #include "tooltips.hpp" - using namespace MWGui; const int StatsWindow::sLineHeight = 18; diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 2250ffd0b..b72d27aea 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -2,18 +2,11 @@ #include -#include - -#include - #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/class.hpp" - #include "mapwindow.hpp" -#include "widgets.hpp" #include "inventorywindow.hpp" using namespace MWGui; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index f3f226974..23bb6554f 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -9,7 +9,6 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" -#include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" #include "../mwmechanics/creaturestats.hpp" diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 8eaa0d8c6..780ffa056 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -1,22 +1,15 @@ #include "travelwindow.hpp" -#include - #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/manualref.hpp" - -#include "../mwmechanics/spells.hpp" -#include "../mwmechanics/creaturestats.hpp" #include "inventorywindow.hpp" #include "tradewindow.hpp" diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index a84b9203a..ad2b4710c 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -1,7 +1,5 @@ #include "waitdialog.hpp" -#include - #include #include @@ -11,9 +9,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/timestamp.hpp" #include "../mwworld/player.hpp" -#include "../mwworld/ptr.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/creaturestats.hpp" diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index dd3206ed6..e6c8b1d77 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -9,8 +9,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/esmstore.hpp" - #undef min #undef max diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index c41bcb7ce..cc74579ab 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -1,7 +1,5 @@ #include "windowbase.hpp" -#include - #include "../mwbase/windowmanager.hpp" using namespace MWGui; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index f2ded8813..154234bee 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1,39 +1,20 @@ #include "windowmanagerimp.hpp" -#include -#include - -#include - #include #include -#include -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/inputmanager.hpp" -#include "../mwworld/ptr.hpp" -#include "../mwworld/cellstore.hpp" - #include "console.hpp" #include "journalwindow.hpp" #include "charactercreation.hpp" -#include "textinput.hpp" -#include "review.hpp" #include "dialogue.hpp" #include "dialoguehistory.hpp" -#include "mapwindow.hpp" #include "statswindow.hpp" #include "messagebox.hpp" -#include "container.hpp" -#include "inventorywindow.hpp" #include "tooltips.hpp" #include "scrollwindow.hpp" #include "bookwindow.hpp" -#include "list.hpp" #include "hud.hpp" #include "mainmenu.hpp" #include "countdialog.hpp" @@ -48,17 +29,15 @@ #include "loadingscreen.hpp" #include "levelupdialog.hpp" #include "waitdialog.hpp" -#include "spellcreationdialog.hpp" #include "enchantingdialog.hpp" #include "trainingwindow.hpp" -#include "imagebutton.hpp" #include "exposedwindow.hpp" #include "cursor.hpp" -#include "spellicons.hpp" #include "merchantrepair.hpp" #include "repair.hpp" #include "soulgemdialog.hpp" #include "companionwindow.hpp" +#include "inventorywindow.hpp" using namespace MWGui; diff --git a/apps/openmw/mwgui/windowpinnablebase.cpp b/apps/openmw/mwgui/windowpinnablebase.cpp index 56868306a..53ba1b9fd 100644 --- a/apps/openmw/mwgui/windowpinnablebase.cpp +++ b/apps/openmw/mwgui/windowpinnablebase.cpp @@ -1,7 +1,5 @@ #include "windowpinnablebase.hpp" -#include "../mwbase/windowmanager.hpp" - #include "exposedwindow.hpp" using namespace MWGui; From 3def2a0f453932c8fed8e83bc2654082e6ab0c46 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 16 Apr 2013 19:16:45 -0700 Subject: [PATCH 11/99] Treat the particle random values as the max differential --- components/nifogre/ogrenifloader.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 2e77adb08..c5cc45ae9 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -433,11 +433,11 @@ class NIFObjectLoader static void createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, const Nif::NiParticleSystemController *partctrl) { Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif"); - emitter->setParticleVelocity(partctrl->velocity-partctrl->velocityRandom, - partctrl->velocity+partctrl->velocityRandom); + emitter->setParticleVelocity(partctrl->velocity - partctrl->velocityRandom*0.5f, + partctrl->velocity + partctrl->velocityRandom*0.5f); emitter->setEmissionRate(partctrl->emitRate); - emitter->setTimeToLive(partctrl->lifetime-partctrl->lifetimeRandom, - partctrl->lifetime+partctrl->lifetimeRandom); + emitter->setTimeToLive(partctrl->lifetime - partctrl->lifetimeRandom*0.5f, + partctrl->lifetime + partctrl->lifetimeRandom*0.5f); emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x)); emitter->setParameter("height", Ogre::StringConverter::toString(partctrl->offsetRandom.y)); emitter->setParameter("depth", Ogre::StringConverter::toString(partctrl->offsetRandom.z)); From c1ec16986bc396bc611154d992cad25fe3dea503 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 17 Apr 2013 12:04:05 +0200 Subject: [PATCH 12/99] add cmake fix for compiling with binutils >= 2.23 which requires explicit linking for dl and Xt --- apps/launcher/CMakeLists.txt | 6 ++++++ apps/openmw/CMakeLists.txt | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index eb93d71e7..0c93474da 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -102,3 +102,9 @@ if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(omwlauncher gcov) endif() + +# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream +if (UNIX AND NOT APPLE) +target_link_libraries(omwlauncher dl Xt) +endif() + diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 3334e7865..f4fdcb390 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -122,6 +122,12 @@ if (UNIX AND NOT APPLE) target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT}) endif() +# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream +if (UNIX AND NOT APPLE) +target_link_libraries(openmw dl Xt) +endif() + + if(APPLE) find_library(CARBON_FRAMEWORK Carbon) find_library(COCOA_FRAMEWORK Cocoa) From 547f77031db127fae46b374c94fccc854d08b067 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 17 Apr 2013 17:05:54 +0200 Subject: [PATCH 13/99] Fix crash when disabling objects in a previously loaded cell, then coming back to that cell and leaving again --- apps/openmw/mwrender/actors.cpp | 4 ++++ apps/openmw/mwworld/cellfunctors.hpp | 6 ++++-- apps/openmw/mwworld/scene.cpp | 29 ++++++++++++++-------------- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 644d3613b..566b6fa81 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -94,6 +94,9 @@ void Actors::insertActivator (const MWWorld::Ptr& ptr) bool Actors::deleteObject (const MWWorld::Ptr& ptr) { + if (mAllActors.find(ptr) == mAllActors.end()) + return false; + mRendering->removeWaterRippleEmitter (ptr); delete mAllActors[ptr]; @@ -139,6 +142,7 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store) Ogre::SceneNode *base = celliter->second; base->removeAndDestroyAllChildren(); mRend.getScene()->destroySceneNode(base); + mCellSceneNodes.erase(celliter); } } diff --git a/apps/openmw/mwworld/cellfunctors.hpp b/apps/openmw/mwworld/cellfunctors.hpp index 8bba898ce..4b1f70096 100644 --- a/apps/openmw/mwworld/cellfunctors.hpp +++ b/apps/openmw/mwworld/cellfunctors.hpp @@ -13,8 +13,8 @@ namespace ESM namespace MWWorld { - /// List all (Ogre-)handles. - struct ListHandles + /// List all (Ogre-)handles, then reset RefData::mBaseNode to 0. + struct ListAndResetHandles { std::vector mHandles; @@ -23,6 +23,8 @@ namespace MWWorld Ogre::SceneNode* handle = data.getBaseNode(); if (handle) mHandles.push_back (handle); + + data.setBaseNode(0); return true; } }; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 439f76131..b9b0aa19c 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -76,27 +76,28 @@ namespace MWWorld void Scene::unloadCell (CellStoreCollection::iterator iter) { std::cout << "Unloading cell\n"; - ListHandles functor; + ListAndResetHandles functor; - (*iter)->forEach(functor); + (*iter)->forEach(functor); { // silence annoying g++ warning for (std::vector::const_iterator iter2 (functor.mHandles.begin()); - iter2!=functor.mHandles.end(); ++iter2){ - Ogre::SceneNode* node = *iter2; + iter2!=functor.mHandles.end(); ++iter2) + { + Ogre::SceneNode* node = *iter2; mPhysics->removeObject (node->getName()); } + } - if ((*iter)->mCell->isExterior()) - { - ESM::Land* land = - MWBase::Environment::get().getWorld()->getStore().get().search( - (*iter)->mCell->getGridX(), - (*iter)->mCell->getGridY() - ); - if (land) - mPhysics->removeHeightField( (*iter)->mCell->getGridX(), (*iter)->mCell->getGridY() ); - } + if ((*iter)->mCell->isExterior()) + { + ESM::Land* land = + MWBase::Environment::get().getWorld()->getStore().get().search( + (*iter)->mCell->getGridX(), + (*iter)->mCell->getGridY() + ); + if (land) + mPhysics->removeHeightField( (*iter)->mCell->getGridX(), (*iter)->mCell->getGridY() ); } mRendering.removeCell(*iter); From 0341a9e7781342e321744e1976f2a1918521806d Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 17 Apr 2013 20:57:22 +0200 Subject: [PATCH 14/99] Fix a gcc 4.8 warning --- apps/openmw/mwrender/videoplayer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 2cbc85cd3..87ae8175d 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -788,8 +788,8 @@ void VideoState::decode_thread_loop(VideoState *self) // main decode loop while(!self->quit) { - if((self->audio_st >= 0 && self->audioq.size > MAX_AUDIOQ_SIZE) || - (self->video_st >= 0 && self->videoq.size > MAX_VIDEOQ_SIZE)) + if((self->audio_st && self->audioq.size > MAX_AUDIOQ_SIZE) || + (self->video_st && self->videoq.size > MAX_VIDEOQ_SIZE)) { boost::this_thread::sleep(boost::posix_time::milliseconds(10)); continue; From c519fc360ddaeb13872fd761c267123440a88b7e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Apr 2013 00:19:34 +0200 Subject: [PATCH 15/99] Move away from fixed record names for body parts --- apps/esmtool/record.cpp | 2 +- apps/openmw/mwgui/race.cpp | 132 +++++++------------------- apps/openmw/mwgui/race.hpp | 7 +- apps/openmw/mwrender/npcanimation.cpp | 109 +++++++++++++-------- components/esm/loadarmo.hpp | 4 +- components/esm/loadbody.cpp | 4 +- components/esm/loadbody.hpp | 6 +- 7 files changed, 118 insertions(+), 146 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index e16ade6e2..b7cbbc4ea 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -439,7 +439,7 @@ void Record::print() template<> void Record::print() { - std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Race: " << mData.mRace << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Type: " << meshTypeLabel(mData.mData.mType) << " (" << (int)mData.mData.mType << ")" << std::endl; diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index c031ad6b6..25bae999b 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -24,36 +24,6 @@ int wrap(int index, int max) else return index; } - -int countParts(const std::string &part, const std::string &race, bool male) -{ - /// \todo loop through the whole store for appropriate bodyparts instead of looking for fixed IDs - const MWWorld::Store &store = - MWBase::Environment::get().getWorld()->getStore().get(); - - std::string prefix = - "b_n_" + race + ((male) ? "_m_" : "_f_") + part; - - std::string suffix; - suffix.reserve(prefix.size() + 3); - - int count = -1; - do { - ++count; - suffix = "_" + (boost::format("%02d") % (count + 1)).str(); - } - while (store.search(prefix + suffix) != 0); - - if (count == 0 && part == "hair") { - count = -1; - do { - ++count; - suffix = (boost::format("%02d") % (count + 1)).str(); - } - while (store.search(prefix + suffix) != 0); - } - return count; -} } RaceDialog::RaceDialog() @@ -61,8 +31,6 @@ RaceDialog::RaceDialog() , mGenderIndex(0) , mFaceIndex(0) , mHairIndex(0) - , mFaceCount(10) - , mHairCount(14) , mCurrentAngle(0) { // Centre dialog @@ -227,67 +195,28 @@ void RaceDialog::onSelectNextGender(MyGUI::Widget*) void RaceDialog::onSelectPreviousFace(MyGUI::Widget*) { - do - mFaceIndex = wrap(mFaceIndex - 1, mFaceCount); - while (!isFacePlayable()); + mFaceIndex = wrap(mFaceIndex - 1, mAvailableHeads.size()); updatePreview(); } void RaceDialog::onSelectNextFace(MyGUI::Widget*) { - do - mFaceIndex = wrap(mFaceIndex + 1, mFaceCount); - while (!isFacePlayable()); + mFaceIndex = wrap(mFaceIndex + 1, mAvailableHeads.size()); updatePreview(); } void RaceDialog::onSelectPreviousHair(MyGUI::Widget*) { - do - mHairIndex = wrap(mHairIndex - 1, mHairCount); - while (!isHairPlayable()); + mHairIndex = wrap(mHairIndex - 1, mAvailableHairs.size()); updatePreview(); } void RaceDialog::onSelectNextHair(MyGUI::Widget*) { - do - mHairIndex = wrap(mHairIndex + 1, mHairCount); - while (!isHairPlayable()); + mHairIndex = wrap(mHairIndex + 1, mAvailableHairs.size()); updatePreview(); } -bool RaceDialog::isFacePlayable() -{ - std::string prefix = - "b_n_" + mCurrentRaceId + ((mGenderIndex == 0) ? "_m_" : "_f_"); - - std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str(); - - const MWWorld::Store &parts = - MWBase::Environment::get().getWorld()->getStore().get(); - - if (parts.search(prefix + "head_" + headIndex) == 0) - return !(parts.find(prefix + "head" + headIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); - else - return !(parts.find(prefix + "head_" + headIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); -} - -bool RaceDialog::isHairPlayable() -{ - std::string prefix = - "b_n_" + mCurrentRaceId + ((mGenderIndex == 0) ? "_m_" : "_f_"); - - std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str(); - - const MWWorld::Store &parts = - MWBase::Environment::get().getWorld()->getStore().get(); - if (parts.search(prefix + "hair_" + hairIndex) == 0) - return !(parts.find(prefix + "hair" + hairIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); - else - return !(parts.find(prefix + "hair_" + hairIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); -} - void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index) { if (_index == MyGUI::ITEM_NONE) @@ -308,18 +237,41 @@ void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index) updateSpellPowers(); } +void RaceDialog::getBodyParts (int part, std::vector& out) +{ + out.clear(); + const MWWorld::Store &store = + MWBase::Environment::get().getWorld()->getStore().get(); + + for (MWWorld::Store::iterator it = store.begin(); it != store.end(); ++it) + { + const ESM::BodyPart& bodypart = *it; + if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) + continue; + if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) + continue; + if (bodypart.mData.mPart != static_cast(part)) + continue; + if (mGenderIndex != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) + continue; + bool firstPerson = (bodypart.mId.size() >= 3) + && bodypart.mId[bodypart.mId.size()-3] == '1' + && bodypart.mId[bodypart.mId.size()-2] == 's' + && bodypart.mId[bodypart.mId.size()-1] == 't'; + if (firstPerson) + continue; + if (Misc::StringUtils::ciEqual(bodypart.mRace, mCurrentRaceId)) + out.push_back(bodypart.mId); + } +} + void RaceDialog::recountParts() { - mFaceCount = countParts("head", mCurrentRaceId, mGenderIndex == 0); - mHairCount = countParts("hair", mCurrentRaceId, mGenderIndex == 0); + getBodyParts(ESM::BodyPart::MP_Hair, mAvailableHairs); + getBodyParts(ESM::BodyPart::MP_Head, mAvailableHeads); mFaceIndex = 0; mHairIndex = 0; - - while (!isHairPlayable()) - mHairIndex = wrap(mHairIndex + 1, mHairCount); - while (!isFacePlayable()) - mFaceIndex = wrap(mFaceIndex + 1, mFaceCount); } // update widget content @@ -330,21 +282,9 @@ void RaceDialog::updatePreview() record.mRace = mCurrentRaceId; record.setIsMale(mGenderIndex == 0); - std::string prefix = - "b_n_" + mCurrentRaceId + ((record.isMale()) ? "_m_" : "_f_"); + record.mHead = mAvailableHeads[mFaceIndex]; + record.mHair = mAvailableHairs[mHairIndex]; - std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str(); - std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str(); - - record.mHead = prefix + "head_" + headIndex; - record.mHair = prefix + "hair_" + hairIndex; - - const MWWorld::Store &parts = - MWBase::Environment::get().getWorld()->getStore().get(); - - if (parts.search(record.mHair) == 0) { - record.mHair = prefix + "hair" + hairIndex; - } mPreview->setPrototype(record); } diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 893c4c90b..1d48c67cd 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -76,8 +76,10 @@ namespace MWGui void updatePreview(); void recountParts(); - bool isHairPlayable(); - bool isFacePlayable(); + void getBodyParts (int part, std::vector& out); + + std::vector mAvailableHeads; + std::vector mAvailableHairs; MyGUI::ImageBox* mPreviewImage; MyGUI::ListBox* mRaceList; @@ -90,7 +92,6 @@ namespace MWGui std::vector mSpellPowerItems; int mGenderIndex, mFaceIndex, mHairIndex; - int mFaceCount, mHairCount; std::string mCurrentRaceId; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 96220f47d..ecc0869cc 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -305,56 +305,83 @@ void NpcAnimation::updateParts(bool forceupdate) if(mViewMode == VM_HeadOnly) return; - static const struct { - ESM::PartReferenceType type; - const char name[2][12]; - } PartTypeList[] = { - { ESM::PRT_Neck, { "neck", "" } }, - { ESM::PRT_Cuirass, { "chest", "" } }, - { ESM::PRT_Groin, { "groin", "" } }, - { ESM::PRT_RHand, { "hand", "hands" } }, - { ESM::PRT_LHand, { "hand", "hands" } }, - { ESM::PRT_RWrist, { "wrist", "" } }, - { ESM::PRT_LWrist, { "wrist", "" } }, - { ESM::PRT_RForearm, { "forearm", "" } }, - { ESM::PRT_LForearm, { "forearm", "" } }, - { ESM::PRT_RUpperarm, { "upper arm", "" } }, - { ESM::PRT_LUpperarm, { "upper arm", "" } }, - { ESM::PRT_RFoot, { "foot", "feet" } }, - { ESM::PRT_LFoot, { "foot", "feet" } }, - { ESM::PRT_RAnkle, { "ankle", "" } }, - { ESM::PRT_LAnkle, { "ankle", "" } }, - { ESM::PRT_RKnee, { "knee", "" } }, - { ESM::PRT_LKnee, { "knee", "" } }, - { ESM::PRT_RLeg, { "upper leg", "" } }, - { ESM::PRT_LLeg, { "upper leg", "" } }, - { ESM::PRT_Tail, { "tail", "" } } - }; + std::map bodypartMap; + bodypartMap[ESM::PRT_Neck] = ESM::BodyPart::MP_Neck; + bodypartMap[ESM::PRT_Cuirass] = ESM::BodyPart::MP_Chest; + bodypartMap[ESM::PRT_Groin] = ESM::BodyPart::MP_Groin; + bodypartMap[ESM::PRT_RHand] = ESM::BodyPart::MP_Hand; + bodypartMap[ESM::PRT_LHand] = ESM::BodyPart::MP_Hand; + bodypartMap[ESM::PRT_RWrist] = ESM::BodyPart::MP_Wrist; + bodypartMap[ESM::PRT_LWrist] = ESM::BodyPart::MP_Wrist; + bodypartMap[ESM::PRT_RForearm] = ESM::BodyPart::MP_Forearm; + bodypartMap[ESM::PRT_LForearm] = ESM::BodyPart::MP_Forearm; + bodypartMap[ESM::PRT_RUpperarm] = ESM::BodyPart::MP_Upperarm; + bodypartMap[ESM::PRT_LUpperarm] = ESM::BodyPart::MP_Upperarm; + bodypartMap[ESM::PRT_RFoot] = ESM::BodyPart::MP_Foot; + bodypartMap[ESM::PRT_LFoot] = ESM::BodyPart::MP_Foot; + bodypartMap[ESM::PRT_RAnkle] = ESM::BodyPart::MP_Ankle; + bodypartMap[ESM::PRT_LAnkle] = ESM::BodyPart::MP_Ankle; + bodypartMap[ESM::PRT_RKnee] = ESM::BodyPart::MP_Knee; + bodypartMap[ESM::PRT_LKnee] = ESM::BodyPart::MP_Knee; + bodypartMap[ESM::PRT_RLeg] = ESM::BodyPart::MP_Upperleg; + bodypartMap[ESM::PRT_LLeg] = ESM::BodyPart::MP_Upperleg; + bodypartMap[ESM::PRT_Tail] = ESM::BodyPart::MP_Tail; - const char *ext = (mViewMode == VM_FirstPerson) ? ".1st" : ""; const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - for(size_t i = 0;i < sizeof(PartTypeList)/sizeof(PartTypeList[0]);i++) + + const int Flag_Female = 0x01; + const int Flag_FirstPerson = 0x02; + + int flags = 0; + if (!mNpc->isMale()) + flags |= Flag_Female; + if (mViewMode == VM_FirstPerson) + flags |= Flag_FirstPerson; + + // Remember body parts so we only have to search through the store once for each race/gender/viewmode combination + static std::map< std::pair , std::vector > sRaceMapping; + std::string race = Misc::StringUtils::lowerCase(mNpc->mRace); + std::pair thisCombination = std::make_pair(race, flags); + if (sRaceMapping.find(thisCombination) == sRaceMapping.end()) { - if(mPartPriorities[PartTypeList[i].type] < 1) + sRaceMapping[thisCombination].resize(ESM::PRT_Count); + for (int i=0; i &partStore = store.get(); + + for (MWWorld::Store::iterator it = partStore.begin(); it != partStore.end(); ++it) { - const ESM::BodyPart *part = NULL; - const MWWorld::Store &partStore = store.get(); - - if(!mNpc->isMale()) + const ESM::BodyPart& bodypart = *it; + if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) + continue; + if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) { - part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]+ext); - if(part == 0) - part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]+ext); + continue; } - if(part == 0) - part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[0]+ext); - if(part == 0) - part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[1]+ext); + if (!mNpc->isMale() != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) + continue; + if (!Misc::StringUtils::ciEqual(bodypart.mRace, mNpc->mRace)) + continue; - if(part) - addOrReplaceIndividualPart(PartTypeList[i].type, -1,1, "meshes\\"+part->mModel); + bool firstPerson = (bodypart.mId.size() >= 3) + && bodypart.mId[bodypart.mId.size()-3] == '1' + && bodypart.mId[bodypart.mId.size()-2] == 's' + && bodypart.mId[bodypart.mId.size()-1] == 't'; + if (firstPerson != (mViewMode == VM_FirstPerson)) + continue; + for (std::map::iterator bIt = bodypartMap.begin(); bIt != bodypartMap.end(); ++bIt ) + if (bIt->second == bodypart.mData.mPart) + sRaceMapping[thisCombination][bIt->first] = &*it; } } + + for (int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part) + { + const ESM::BodyPart* bodypart = sRaceMapping[thisCombination][part]; + if (mPartPriorities[part] < 1 && bodypart) + addOrReplaceIndividualPart(part, -1,1, "meshes\\"+bodypart->mModel); + } } NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename) diff --git a/components/esm/loadarmo.hpp b/components/esm/loadarmo.hpp index a94ae6735..c18b4486d 100644 --- a/components/esm/loadarmo.hpp +++ b/components/esm/loadarmo.hpp @@ -38,7 +38,9 @@ enum PartReferenceType PRT_RPauldron = 23, PRT_LPauldron = 24, PRT_Weapon = 25, - PRT_Tail = 26 + PRT_Tail = 26, + + PRT_Count = 27 }; // Reference to body parts diff --git a/components/esm/loadbody.cpp b/components/esm/loadbody.cpp index 831ad8b64..e95a8a860 100644 --- a/components/esm/loadbody.cpp +++ b/components/esm/loadbody.cpp @@ -9,13 +9,13 @@ namespace ESM void BodyPart::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); - mName = esm.getHNString("FNAM"); + mRace = esm.getHNString("FNAM"); esm.getHNT(mData, "BYDT", 4); } void BodyPart::save(ESMWriter &esm) { esm.writeHNCString("MODL", mModel); - esm.writeHNCString("FNAM", mName); + esm.writeHNCString("FNAM", mRace); esm.writeHNT("BYDT", mData, 4); } diff --git a/components/esm/loadbody.hpp b/components/esm/loadbody.hpp index c467b3625..3ad9b1b95 100644 --- a/components/esm/loadbody.hpp +++ b/components/esm/loadbody.hpp @@ -27,7 +27,9 @@ struct BodyPart MP_Knee = 11, MP_Upperleg = 12, MP_Clavicle = 13, - MP_Tail = 14 + MP_Tail = 14, + + MP_Count = 15 }; enum Flags @@ -52,7 +54,7 @@ struct BodyPart }; BYDTstruct mData; - std::string mId, mModel, mName; + std::string mId, mModel, mRace; void load(ESMReader &esm); void save(ESMWriter &esm); From 7eee86ab66f8b7fbd33e375418b6bdf44122dedf Mon Sep 17 00:00:00 2001 From: Britt Mathis Date: Wed, 17 Apr 2013 18:56:48 -0400 Subject: [PATCH 16/99] No more using namespace --- apps/openmw/mwgui/birth.cpp | 418 ++--- apps/openmw/mwgui/bookwindow.cpp | 261 +-- apps/openmw/mwgui/charactercreation.cpp | 1255 ++++++------- apps/openmw/mwgui/class.cpp | 1599 ++++++++-------- apps/openmw/mwgui/console.cpp | 31 +- apps/openmw/mwgui/container.cpp | 1220 ++++++------ apps/openmw/mwgui/dialogue.cpp | 912 ++++----- apps/openmw/mwgui/dialoguehistory.cpp | 104 +- apps/openmw/mwgui/formatting.cpp | 545 +++--- apps/openmw/mwgui/hud.cpp | 959 +++++----- apps/openmw/mwgui/list.cpp | 296 +-- apps/openmw/mwgui/mapwindow.cpp | 793 ++++---- apps/openmw/mwgui/messagebox.cpp | 747 ++++---- apps/openmw/mwgui/race.cpp | 822 ++++----- apps/openmw/mwgui/review.cpp | 658 +++---- apps/openmw/mwgui/scrollwindow.cpp | 128 +- apps/openmw/mwgui/statswindow.cpp | 1021 +++++----- apps/openmw/mwgui/textinput.cpp | 121 +- apps/openmw/mwgui/tooltips.cpp | 1416 +++++++------- apps/openmw/mwgui/widgets.cpp | 1676 ++++++++--------- apps/openmw/mwgui/windowmanagerimp.cpp | 2145 +++++++++++----------- apps/openmw/mwgui/windowpinnablebase.cpp | 39 +- 22 files changed, 8609 insertions(+), 8557 deletions(-) diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 9c3ebdc7d..965606709 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -9,237 +9,239 @@ #include "widgets.hpp" -using namespace MWGui; -using namespace Widgets; - namespace { -bool sortBirthSigns(const std::pair& left, const std::pair& right) -{ - return left.second->mName.compare (right.second->mName) < 0; -} + bool sortBirthSigns(const std::pair& left, const std::pair& right) + { + return left.second->mName.compare (right.second->mName) < 0; + } } -BirthDialog::BirthDialog() - : WindowModal("openmw_chargen_birth.layout") +namespace MWGui { - // Centre dialog - center(); - getWidget(mSpellArea, "SpellArea"); + BirthDialog::BirthDialog() + : WindowModal("openmw_chargen_birth.layout") + { + // Centre dialog + center(); - getWidget(mBirthImage, "BirthsignImage"); + getWidget(mSpellArea, "SpellArea"); - getWidget(mBirthList, "BirthsignList"); - mBirthList->setScrollVisible(true); - mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); - mBirthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); - mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + getWidget(mBirthImage, "BirthsignImage"); - MyGUI::Button* backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onBackClicked); + getWidget(mBirthList, "BirthsignList"); + mBirthList->setScrollVisible(true); + mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + mBirthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked); + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onBackClicked); - updateBirths(); - updateSpells(); -} - -void BirthDialog::setNextButtonShow(bool shown) -{ - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - - if (shown) - okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); - else + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); -} + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked); -void BirthDialog::open() -{ - WindowModal::open(); - updateBirths(); - updateSpells(); -} + updateBirths(); + updateSpells(); + } - -void BirthDialog::setBirthId(const std::string &birthId) -{ - mCurrentBirthId = birthId; - mBirthList->setIndexSelected(MyGUI::ITEM_NONE); - size_t count = mBirthList->getItemCount(); - for (size_t i = 0; i < count; ++i) + void BirthDialog::setNextButtonShow(bool shown) { - if (boost::iequals(*mBirthList->getItemDataAt(i), birthId)) + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } + + void BirthDialog::open() + { + WindowModal::open(); + updateBirths(); + updateSpells(); + } + + + void BirthDialog::setBirthId(const std::string &birthId) + { + mCurrentBirthId = birthId; + mBirthList->setIndexSelected(MyGUI::ITEM_NONE); + size_t count = mBirthList->getItemCount(); + for (size_t i = 0; i < count; ++i) { - mBirthList->setIndexSelected(i); - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - break; - } - } - - updateSpells(); -} - -// widget controls - -void BirthDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE) - return; - eventDone(this); -} - -void BirthDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} - -void BirthDialog::onSelectBirth(MyGUI::ListBox* _sender, size_t _index) -{ - if (_index == MyGUI::ITEM_NONE) - return; - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - - const std::string *birthId = mBirthList->getItemDataAt(_index); - if (boost::iequals(mCurrentBirthId, *birthId)) - return; - - mCurrentBirthId = *birthId; - updateSpells(); -} - -// update widget content - -void BirthDialog::updateBirths() -{ - mBirthList->removeAllItems(); - - const MWWorld::Store &signs = - MWBase::Environment::get().getWorld()->getStore().get(); - - // sort by name - std::vector < std::pair > birthSigns; - - MWWorld::Store::iterator it = signs.begin(); - for (; it != signs.end(); ++it) - { - birthSigns.push_back(std::make_pair(it->mId, &(*it))); - } - std::sort(birthSigns.begin(), birthSigns.end(), sortBirthSigns); - - int index = 0; - for (std::vector >::const_iterator it2 = birthSigns.begin(); - it2 != birthSigns.end(); ++it2, ++index) - { - mBirthList->addItem(it2->second->mName, it2->first); - if (mCurrentBirthId.empty()) - { - mBirthList->setIndexSelected(index); - mCurrentBirthId = it2->first; - } - else if (boost::iequals(it2->first, mCurrentBirthId)) - { - mBirthList->setIndexSelected(index); - } - } -} - -void BirthDialog::updateSpells() -{ - for (std::vector::iterator it = mSpellItems.begin(); it != mSpellItems.end(); ++it) - { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - mSpellItems.clear(); - - if (mCurrentBirthId.empty()) - return; - - MWSpellPtr spellWidget; - const int lineHeight = 18; - MyGUI::IntCoord coord(0, 0, mSpellArea->getWidth(), 18); - - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); - - const ESM::BirthSign *birth = - store.get().find(mCurrentBirthId); - - std::string texturePath = std::string("textures\\") + birth->mTexture; - fixTexturePath(texturePath); - mBirthImage->setImageTexture(texturePath); - - std::vector abilities, powers, spells; - - std::vector::const_iterator it = birth->mPowers.mList.begin(); - std::vector::const_iterator end = birth->mPowers.mList.end(); - for (; it != end; ++it) - { - const std::string &spellId = *it; - const ESM::Spell *spell = store.get().search(spellId); - if (!spell) - continue; // Skip spells which cannot be found - ESM::Spell::SpellType type = static_cast(spell->mData.mType); - if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power) - continue; // We only want spell, ability and powers. - - if (type == ESM::Spell::ST_Ability) - abilities.push_back(spellId); - else if (type == ESM::Spell::ST_Power) - powers.push_back(spellId); - else if (type == ESM::Spell::ST_Spell) - spells.push_back(spellId); - } - - int i = 0; - - struct { - const std::vector &spells; - const char *label; - } - categories[3] = { - {abilities, "sBirthsignmenu1"}, - {powers, "sPowers"}, - {spells, "sBirthsignmenu2"} - }; - - for (int category = 0; category < 3; ++category) - { - if (!categories[category].spells.empty()) - { - MyGUI::TextBox* label = mSpellArea->createWidget("SandBrightText", coord, MyGUI::Align::Default, std::string("Label")); - label->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(categories[category].label, "")); - mSpellItems.push_back(label); - coord.top += lineHeight; - - std::vector::const_iterator end = categories[category].spells.end(); - for (std::vector::const_iterator it = categories[category].spells.begin(); it != end; ++it) + if (boost::iequals(*mBirthList->getItemDataAt(i), birthId)) { - const std::string &spellId = *it; - spellWidget = mSpellArea->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + boost::lexical_cast(i)); - spellWidget->setSpellId(spellId); + mBirthList->setIndexSelected(i); + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + break; + } + } - mSpellItems.push_back(spellWidget); - coord.top += lineHeight; + updateSpells(); + } - MyGUI::IntCoord spellCoord = coord; - spellCoord.height = 24; // TODO: This should be fetched from the skin somehow, or perhaps a widget in the layout as a template? - spellWidget->createEffectWidgets(mSpellItems, mSpellArea, spellCoord, (category == 0) ? MWEffectList::EF_Constant : 0); - coord.top = spellCoord.top; + // widget controls - ++i; + void BirthDialog::onOkClicked(MyGUI::Widget* _sender) + { + if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE) + return; + eventDone(this); + } + + void BirthDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } + + void BirthDialog::onSelectBirth(MyGUI::ListBox* _sender, size_t _index) + { + if (_index == MyGUI::ITEM_NONE) + return; + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + + const std::string *birthId = mBirthList->getItemDataAt(_index); + if (boost::iequals(mCurrentBirthId, *birthId)) + return; + + mCurrentBirthId = *birthId; + updateSpells(); + } + + // update widget content + + void BirthDialog::updateBirths() + { + mBirthList->removeAllItems(); + + const MWWorld::Store &signs = + MWBase::Environment::get().getWorld()->getStore().get(); + + // sort by name + std::vector < std::pair > birthSigns; + + MWWorld::Store::iterator it = signs.begin(); + for (; it != signs.end(); ++it) + { + birthSigns.push_back(std::make_pair(it->mId, &(*it))); + } + std::sort(birthSigns.begin(), birthSigns.end(), sortBirthSigns); + + int index = 0; + for (std::vector >::const_iterator it2 = birthSigns.begin(); + it2 != birthSigns.end(); ++it2, ++index) + { + mBirthList->addItem(it2->second->mName, it2->first); + if (mCurrentBirthId.empty()) + { + mBirthList->setIndexSelected(index); + mCurrentBirthId = it2->first; + } + else if (boost::iequals(it2->first, mCurrentBirthId)) + { + mBirthList->setIndexSelected(index); } } } + + void BirthDialog::updateSpells() + { + for (std::vector::iterator it = mSpellItems.begin(); it != mSpellItems.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mSpellItems.clear(); + + if (mCurrentBirthId.empty()) + return; + + Widgets::MWSpellPtr spellWidget; + const int lineHeight = 18; + MyGUI::IntCoord coord(0, 0, mSpellArea->getWidth(), 18); + + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::BirthSign *birth = + store.get().find(mCurrentBirthId); + + std::string texturePath = std::string("textures\\") + birth->mTexture; + Widgets::fixTexturePath(texturePath); + mBirthImage->setImageTexture(texturePath); + + std::vector abilities, powers, spells; + + std::vector::const_iterator it = birth->mPowers.mList.begin(); + std::vector::const_iterator end = birth->mPowers.mList.end(); + for (; it != end; ++it) + { + const std::string &spellId = *it; + const ESM::Spell *spell = store.get().search(spellId); + if (!spell) + continue; // Skip spells which cannot be found + ESM::Spell::SpellType type = static_cast(spell->mData.mType); + if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power) + continue; // We only want spell, ability and powers. + + if (type == ESM::Spell::ST_Ability) + abilities.push_back(spellId); + else if (type == ESM::Spell::ST_Power) + powers.push_back(spellId); + else if (type == ESM::Spell::ST_Spell) + spells.push_back(spellId); + } + + int i = 0; + + struct { + const std::vector &spells; + const char *label; + } + categories[3] = { + {abilities, "sBirthsignmenu1"}, + {powers, "sPowers"}, + {spells, "sBirthsignmenu2"} + }; + + for (int category = 0; category < 3; ++category) + { + if (!categories[category].spells.empty()) + { + MyGUI::TextBox* label = mSpellArea->createWidget("SandBrightText", coord, MyGUI::Align::Default, std::string("Label")); + label->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(categories[category].label, "")); + mSpellItems.push_back(label); + coord.top += lineHeight; + + std::vector::const_iterator end = categories[category].spells.end(); + for (std::vector::const_iterator it = categories[category].spells.begin(); it != end; ++it) + { + const std::string &spellId = *it; + spellWidget = mSpellArea->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + boost::lexical_cast(i)); + spellWidget->setSpellId(spellId); + + mSpellItems.push_back(spellWidget); + coord.top += lineHeight; + + MyGUI::IntCoord spellCoord = coord; + spellCoord.height = 24; // TODO: This should be fetched from the skin somehow, or perhaps a widget in the layout as a template? + spellWidget->createEffectWidgets(mSpellItems, mSpellArea, spellCoord, (category == 0) ? Widgets::MWEffectList::EF_Constant : 0); + coord.top = spellCoord.top; + + ++i; + } + } + } + } + } diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index ce74c4859..6d44e9d2c 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -12,148 +12,151 @@ #include "formatting.hpp" -using namespace MWGui; - -BookWindow::BookWindow () - : WindowBase("openmw_book.layout") - , mTakeButtonShow(true) - , mTakeButtonAllowed(true) +namespace MWGui { - getWidget(mCloseButton, "CloseButton"); - mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onCloseButtonClicked); - getWidget(mTakeButton, "TakeButton"); - mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onTakeButtonClicked); - - getWidget(mNextPageButton, "NextPageBTN"); - mNextPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onNextPageButtonClicked); - - getWidget(mPrevPageButton, "PrevPageBTN"); - mPrevPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onPrevPageButtonClicked); - - getWidget(mLeftPageNumber, "LeftPageNumber"); - getWidget(mRightPageNumber, "RightPageNumber"); - - getWidget(mLeftPage, "LeftPage"); - getWidget(mRightPage, "RightPage"); - - center(); -} - -void BookWindow::clearPages() -{ - for (std::vector::iterator it=mPages.begin(); - it!=mPages.end(); ++it) + BookWindow::BookWindow () + : WindowBase("openmw_book.layout") + , mTakeButtonShow(true) + , mTakeButtonAllowed(true) { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - mPages.clear(); -} + getWidget(mCloseButton, "CloseButton"); + mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onCloseButtonClicked); -void BookWindow::open (MWWorld::Ptr book) -{ - mBook = book; + getWidget(mTakeButton, "TakeButton"); + mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onTakeButtonClicked); - clearPages(); - mCurrentPage = 0; + getWidget(mNextPageButton, "NextPageBTN"); + mNextPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onNextPageButtonClicked); - MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); + getWidget(mPrevPageButton, "PrevPageBTN"); + mPrevPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onPrevPageButtonClicked); - MWWorld::LiveCellRef *ref = mBook.get(); + getWidget(mLeftPageNumber, "LeftPageNumber"); + getWidget(mRightPageNumber, "RightPageNumber"); - BookTextParser parser; - std::vector results = parser.split(ref->mBase->mText, mLeftPage->getSize().width, mLeftPage->getSize().height); + getWidget(mLeftPage, "LeftPage"); + getWidget(mRightPage, "RightPage"); - int i=0; - for (std::vector::iterator it=results.begin(); - it!=results.end(); ++it) - { - MyGUI::Widget* parent; - if (i%2 == 0) - parent = mLeftPage; - else - parent = mRightPage; - - MyGUI::Widget* pageWidget = parent->createWidgetReal("", MyGUI::FloatCoord(0.0,0.0,1.0,1.0), MyGUI::Align::Default, "BookPage" + boost::lexical_cast(i)); - parser.parse(*it, pageWidget, mLeftPage->getSize().width); - mPages.push_back(pageWidget); - ++i; + center(); } - updatePages(); - - setTakeButtonShow(true); -} - -void BookWindow::setTakeButtonShow(bool show) -{ - mTakeButtonShow = show; - mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); -} - -void BookWindow::setInventoryAllowed(bool allowed) -{ - mTakeButtonAllowed = allowed; - mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); -} - -void BookWindow::onCloseButtonClicked (MyGUI::Widget* sender) -{ - // no 3d sounds because the object could be in a container. - MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); - - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); -} - -void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender) -{ - MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); - - MWWorld::ActionTake take(mBook); - take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); -} - -void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender) -{ - if ((mCurrentPage+1)*2 < mPages.size()) + void BookWindow::clearPages() { - MWBase::Environment::get().getSoundManager()->playSound ("book page2", 1.0, 1.0); - - ++mCurrentPage; - - updatePages(); - } -} - -void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender) -{ - if (mCurrentPage > 0) - { - MWBase::Environment::get().getSoundManager()->playSound ("book page", 1.0, 1.0); - - --mCurrentPage; - - updatePages(); - } -} - -void BookWindow::updatePages() -{ - mLeftPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 1) ); - mRightPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 2) ); - - unsigned int i=0; - for (std::vector::iterator it = mPages.begin(); - it != mPages.end(); ++it) - { - if (mCurrentPage*2 == i || mCurrentPage*2+1 == i) - (*it)->setVisible(true); - else + for (std::vector::iterator it=mPages.begin(); + it!=mPages.end(); ++it) { - (*it)->setVisible(false); + MyGUI::Gui::getInstance().destroyWidget(*it); } - ++i; + mPages.clear(); } + + void BookWindow::open (MWWorld::Ptr book) + { + mBook = book; + + clearPages(); + mCurrentPage = 0; + + MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); + + MWWorld::LiveCellRef *ref = mBook.get(); + + BookTextParser parser; + std::vector results = parser.split(ref->mBase->mText, mLeftPage->getSize().width, mLeftPage->getSize().height); + + int i=0; + for (std::vector::iterator it=results.begin(); + it!=results.end(); ++it) + { + MyGUI::Widget* parent; + if (i%2 == 0) + parent = mLeftPage; + else + parent = mRightPage; + + MyGUI::Widget* pageWidget = parent->createWidgetReal("", MyGUI::FloatCoord(0.0,0.0,1.0,1.0), MyGUI::Align::Default, "BookPage" + boost::lexical_cast(i)); + parser.parse(*it, pageWidget, mLeftPage->getSize().width); + mPages.push_back(pageWidget); + ++i; + } + + updatePages(); + + setTakeButtonShow(true); + } + + void BookWindow::setTakeButtonShow(bool show) + { + mTakeButtonShow = show; + mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); + } + + void BookWindow::setInventoryAllowed(bool allowed) + { + mTakeButtonAllowed = allowed; + mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); + } + + void BookWindow::onCloseButtonClicked (MyGUI::Widget* sender) + { + // no 3d sounds because the object could be in a container. + MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); + + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); + } + + void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender) + { + MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + + MWWorld::ActionTake take(mBook); + take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); + } + + void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender) + { + if ((mCurrentPage+1)*2 < mPages.size()) + { + MWBase::Environment::get().getSoundManager()->playSound ("book page2", 1.0, 1.0); + + ++mCurrentPage; + + updatePages(); + } + } + + void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender) + { + if (mCurrentPage > 0) + { + MWBase::Environment::get().getSoundManager()->playSound ("book page", 1.0, 1.0); + + --mCurrentPage; + + updatePages(); + } + } + + void BookWindow::updatePages() + { + mLeftPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 1) ); + mRightPageNumber->setCaption( boost::lexical_cast(mCurrentPage*2 + 2) ); + + unsigned int i=0; + for (std::vector::iterator it = mPages.begin(); + it != mPages.end(); ++it) + { + if (mCurrentPage*2 == i || mCurrentPage*2+1 == i) + (*it)->setVisible(true); + else + { + (*it)->setVisible(false); + } + ++i; + } + } + } diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 28f6d13ee..fc8c24484 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -43,682 +43,685 @@ namespace }; } -using namespace MWGui; - -CharacterCreation::CharacterCreation() - : mNameDialog(0) - , mRaceDialog(0) - , mClassChoiceDialog(0) - , mGenerateClassQuestionDialog(0) - , mGenerateClassResultDialog(0) - , mPickClassDialog(0) - , mCreateClassDialog(0) - , mBirthSignDialog(0) - , mReviewDialog(0) - , mGenerateClassStep(0) +namespace MWGui { - mCreationStage = CSE_NotStarted; -} -void CharacterCreation::setValue (const std::string& id, const MWMechanics::Stat& value) -{ - if (mReviewDialog) + CharacterCreation::CharacterCreation() + : mNameDialog(0) + , mRaceDialog(0) + , mClassChoiceDialog(0) + , mGenerateClassQuestionDialog(0) + , mGenerateClassResultDialog(0) + , mPickClassDialog(0) + , mCreateClassDialog(0) + , mBirthSignDialog(0) + , mReviewDialog(0) + , mGenerateClassStep(0) { - static const char *ids[] = + mCreationStage = CSE_NotStarted; + } + + void CharacterCreation::setValue (const std::string& id, const MWMechanics::Stat& value) + { + if (mReviewDialog) { - "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", - "AttribVal6", "AttribVal7", "AttribVal8", - 0 + static const char *ids[] = + { + "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", + "AttribVal6", "AttribVal7", "AttribVal8", + 0 + }; + + for (int i=0; ids[i]; ++i) + { + if (ids[i]==id) + mReviewDialog->setAttribute(ESM::Attribute::AttributeID(i), value); + } + } + } + + void CharacterCreation::setValue (const std::string& id, const MWMechanics::DynamicStat& value) + { + if (mReviewDialog) + { + if (id == "HBar") + { + mReviewDialog->setHealth (value); + } + else if (id == "MBar") + { + mReviewDialog->setMagicka (value); + } + else if (id == "FBar") + { + mReviewDialog->setFatigue (value); + } + } + } + + void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) + { + if (mReviewDialog) + mReviewDialog->setSkillValue(parSkill, value); + } + + void CharacterCreation::configureSkills (const SkillList& major, const SkillList& minor) + { + if (mReviewDialog) + mReviewDialog->configureSkills(major, minor); + } + + void CharacterCreation::spawnDialog(const char id) + { + switch (id) + { + case GM_Name: + MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog); + mNameDialog = 0; + mNameDialog = new TextInputDialog(); + mNameDialog->setTextLabel(MWBase::Environment::get().getWindowManager()->getGameSettingString("sName", "Name")); + mNameDialog->setTextInput(mPlayerName); + mNameDialog->setNextButtonShow(mCreationStage >= CSE_NameChosen); + mNameDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onNameDialogDone); + mNameDialog->setVisible(true); + break; + + case GM_Race: + MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); + mRaceDialog = 0; + mRaceDialog = new RaceDialog(); + mRaceDialog->setNextButtonShow(mCreationStage >= CSE_RaceChosen); + mRaceDialog->setRaceId(mPlayerRaceId); + mRaceDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogDone); + mRaceDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogBack); + mRaceDialog->setVisible(true); + if (mCreationStage < CSE_NameChosen) + mCreationStage = CSE_NameChosen; + break; + + case GM_Class: + MWBase::Environment::get().getWindowManager()->removeDialog(mClassChoiceDialog); + mClassChoiceDialog = 0; + mClassChoiceDialog = new ClassChoiceDialog(); + mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice); + mClassChoiceDialog->setVisible(true); + if (mCreationStage < CSE_RaceChosen) + mCreationStage = CSE_RaceChosen; + break; + + case GM_ClassPick: + MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); + mPickClassDialog = 0; + mPickClassDialog = new PickClassDialog(); + mPickClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); + mPickClassDialog->setClassId(mPlayerClass.mName); + mPickClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogDone); + mPickClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogBack); + mPickClassDialog->setVisible(true); + if (mCreationStage < CSE_RaceChosen) + mCreationStage = CSE_RaceChosen; + break; + + case GM_Birth: + MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; + mBirthSignDialog = new BirthDialog(); + mBirthSignDialog->setNextButtonShow(mCreationStage >= CSE_BirthSignChosen); + mBirthSignDialog->setBirthId(mPlayerBirthSignId); + mBirthSignDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone); + mBirthSignDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogBack); + mBirthSignDialog->setVisible(true); + if (mCreationStage < CSE_ClassChosen) + mCreationStage = CSE_ClassChosen; + break; + + case GM_ClassCreate: + MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog); + mCreateClassDialog = 0; + mCreateClassDialog = new CreateClassDialog(); + mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); + mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); + mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack); + mCreateClassDialog->setVisible(true); + if (mCreationStage < CSE_RaceChosen) + mCreationStage = CSE_RaceChosen; + break; + case GM_ClassGenerate: + mGenerateClassStep = 0; + mGenerateClass = ""; + mGenerateClassSpecializations[0] = 0; + mGenerateClassSpecializations[1] = 0; + mGenerateClassSpecializations[2] = 0; + showClassQuestionDialog(); + if (mCreationStage < CSE_RaceChosen) + mCreationStage = CSE_RaceChosen; + break; + case GM_Review: + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); + mReviewDialog = 0; + mReviewDialog = new ReviewDialog(); + mReviewDialog->setPlayerName(mPlayerName); + mReviewDialog->setRace(mPlayerRaceId); + mReviewDialog->setClass(mPlayerClass); + mReviewDialog->setBirthSign(mPlayerBirthSignId); + + mReviewDialog->setHealth(mPlayerHealth); + mReviewDialog->setMagicka(mPlayerMagicka); + mReviewDialog->setFatigue(mPlayerFatigue); + + { + std::map > attributes = MWBase::Environment::get().getWindowManager()->getPlayerAttributeValues(); + for (std::map >::iterator it = attributes.begin(); + it != attributes.end(); ++it) + { + mReviewDialog->setAttribute(static_cast (it->first), it->second); + } + } + + { + std::map > skills = MWBase::Environment::get().getWindowManager()->getPlayerSkillValues(); + for (std::map >::iterator it = skills.begin(); + it != skills.end(); ++it) + { + mReviewDialog->setSkillValue(static_cast (it->first), it->second); + } + mReviewDialog->configureSkills(MWBase::Environment::get().getWindowManager()->getPlayerMajorSkills(), MWBase::Environment::get().getWindowManager()->getPlayerMinorSkills()); + } + + mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone); + mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack); + mReviewDialog->eventActivateDialog += MyGUI::newDelegate(this, &CharacterCreation::onReviewActivateDialog); + mReviewDialog->setVisible(true); + if (mCreationStage < CSE_BirthSignChosen) + mCreationStage = CSE_BirthSignChosen; + break; + } + } + + void CharacterCreation::setPlayerHealth (const MWMechanics::DynamicStat& value) + { + mPlayerHealth = value; + } + + void CharacterCreation::setPlayerMagicka (const MWMechanics::DynamicStat& value) + { + mPlayerMagicka = value; + } + + void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& value) + { + mPlayerFatigue = value; + } + + void CharacterCreation::onReviewDialogDone(WindowBase* parWindow) + { + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); + mReviewDialog = 0; + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } + + void CharacterCreation::onReviewDialogBack() + { + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); + mReviewDialog = 0; + + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); + } + + void CharacterCreation::onReviewActivateDialog(int parDialog) + { + MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); + mReviewDialog = 0; + mCreationStage = CSE_ReviewNext; + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + + switch(parDialog) + { + case ReviewDialog::NAME_DIALOG: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name); + break; + case ReviewDialog::RACE_DIALOG: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); + break; + case ReviewDialog::CLASS_DIALOG: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + break; + case ReviewDialog::BIRTHSIGN_DIALOG: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); }; - - for (int i=0; ids[i]; ++i) - { - if (ids[i]==id) - mReviewDialog->setAttribute(ESM::Attribute::AttributeID(i), value); - } } -} -void CharacterCreation::setValue (const std::string& id, const MWMechanics::DynamicStat& value) -{ - if (mReviewDialog) + void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) { - if (id == "HBar") + if (mPickClassDialog) { - mReviewDialog->setHealth (value); - } - else if (id == "MBar") - { - mReviewDialog->setMagicka (value); - } - else if (id == "FBar") - { - mReviewDialog->setFatigue (value); - } - } -} + const std::string &classId = mPickClassDialog->getClassId(); + if (!classId.empty()) + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); -void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) -{ - if (mReviewDialog) - mReviewDialog->setSkillValue(parSkill, value); -} - -void CharacterCreation::configureSkills (const SkillList& major, const SkillList& minor) -{ - if (mReviewDialog) - mReviewDialog->configureSkills(major, minor); -} - -void CharacterCreation::spawnDialog(const char id) -{ - switch (id) - { - case GM_Name: - MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog); - mNameDialog = 0; - mNameDialog = new TextInputDialog(); - mNameDialog->setTextLabel(MWBase::Environment::get().getWindowManager()->getGameSettingString("sName", "Name")); - mNameDialog->setTextInput(mPlayerName); - mNameDialog->setNextButtonShow(mCreationStage >= CSE_NameChosen); - mNameDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onNameDialogDone); - mNameDialog->setVisible(true); - break; - - case GM_Race: - MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); - mRaceDialog = 0; - mRaceDialog = new RaceDialog(); - mRaceDialog->setNextButtonShow(mCreationStage >= CSE_RaceChosen); - mRaceDialog->setRaceId(mPlayerRaceId); - mRaceDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogDone); - mRaceDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogBack); - mRaceDialog->setVisible(true); - if (mCreationStage < CSE_NameChosen) - mCreationStage = CSE_NameChosen; - break; - - case GM_Class: - MWBase::Environment::get().getWindowManager()->removeDialog(mClassChoiceDialog); - mClassChoiceDialog = 0; - mClassChoiceDialog = new ClassChoiceDialog(); - mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice); - mClassChoiceDialog->setVisible(true); - if (mCreationStage < CSE_RaceChosen) - mCreationStage = CSE_RaceChosen; - break; - - case GM_ClassPick: + const ESM::Class *klass = + MWBase::Environment::get().getWorld()->getStore().get().find(classId); + if (klass) + { + mPlayerClass = *klass; + MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); + } MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); mPickClassDialog = 0; - mPickClassDialog = new PickClassDialog(); - mPickClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); - mPickClassDialog->setClassId(mPlayerClass.mName); - mPickClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogDone); - mPickClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogBack); - mPickClassDialog->setVisible(true); - if (mCreationStage < CSE_RaceChosen) - mCreationStage = CSE_RaceChosen; - break; + } - case GM_Birth: + //TODO This bit gets repeated a few times; wrap it in a function + if (mCreationStage == CSE_ReviewNext) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else if (mCreationStage >= CSE_ClassChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); + } + else + { + mCreationStage = CSE_ClassChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } + } + + void CharacterCreation::onPickClassDialogBack() + { + if (mPickClassDialog) + { + const std::string classId = mPickClassDialog->getClassId(); + if (!classId.empty()) + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); + MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); + mPickClassDialog = 0; + } + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + } + + void CharacterCreation::onClassChoice(int _index) + { + MWBase::Environment::get().getWindowManager()->removeDialog(mClassChoiceDialog); + mClassChoiceDialog = 0; + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + + switch(_index) + { + case ClassChoiceDialog::Class_Generate: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassGenerate); + break; + case ClassChoiceDialog::Class_Pick: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassPick); + break; + case ClassChoiceDialog::Class_Create: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassCreate); + break; + case ClassChoiceDialog::Class_Back: + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); + break; + + }; + } + + void CharacterCreation::onNameDialogDone(WindowBase* parWindow) + { + if (mNameDialog) + { + mPlayerName = mNameDialog->getTextInput(); + MWBase::Environment::get().getWindowManager()->setValue("name", mPlayerName); + MWBase::Environment::get().getMechanicsManager()->setPlayerName(mPlayerName); + MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog); + mNameDialog = 0; + } + + if (mCreationStage == CSE_ReviewNext) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else if (mCreationStage >= CSE_NameChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); + } + else + { + mCreationStage = CSE_NameChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } + } + + void CharacterCreation::onRaceDialogBack() + { + if (mRaceDialog) + { + const ESM::NPC &data = mRaceDialog->getResult(); + mPlayerRaceId = data.mRace; + if (!mPlayerRaceId.empty()) { + MWBase::Environment::get().getMechanicsManager()->setPlayerRace( + data.mRace, + data.isMale(), + data.mHead, + data.mHair + ); + } + MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); + mRaceDialog = 0; + } + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name); + } + + void CharacterCreation::onRaceDialogDone(WindowBase* parWindow) + { + if (mRaceDialog) + { + const ESM::NPC &data = mRaceDialog->getResult(); + mPlayerRaceId = data.mRace; + if (!mPlayerRaceId.empty()) { + MWBase::Environment::get().getMechanicsManager()->setPlayerRace( + data.mRace, + data.isMale(), + data.mHead, + data.mHair + ); + } + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->rebuildAvatar(); + + MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); + mRaceDialog = 0; + } + + if (mCreationStage == CSE_ReviewNext) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else if (mCreationStage >= CSE_RaceChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + } + else + { + mCreationStage = CSE_RaceChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } + } + + void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) + { + if (mBirthSignDialog) + { + mPlayerBirthSignId = mBirthSignDialog->getBirthId(); + if (!mPlayerBirthSignId.empty()) + MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mPlayerBirthSignId); MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); mBirthSignDialog = 0; - mBirthSignDialog = new BirthDialog(); - mBirthSignDialog->setNextButtonShow(mCreationStage >= CSE_BirthSignChosen); - mBirthSignDialog->setBirthId(mPlayerBirthSignId); - mBirthSignDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone); - mBirthSignDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogBack); - mBirthSignDialog->setVisible(true); - if (mCreationStage < CSE_ClassChosen) - mCreationStage = CSE_ClassChosen; - break; + } + + if (mCreationStage >= CSE_BirthSignChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else + { + mCreationStage = CSE_BirthSignChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } + } + + void CharacterCreation::onBirthSignDialogBack() + { + if (mBirthSignDialog) + { + MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mBirthSignDialog->getBirthId()); + MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; + } + + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + } + + void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) + { + if (mCreateClassDialog) + { + ESM::Class klass; + klass.mName = mCreateClassDialog->getName(); + klass.mDescription = mCreateClassDialog->getDescription(); + klass.mData.mSpecialization = mCreateClassDialog->getSpecializationId(); + klass.mData.mIsPlayable = 0x1; + + std::vector attributes = mCreateClassDialog->getFavoriteAttributes(); + assert(attributes.size() == 2); + klass.mData.mAttribute[0] = attributes[0]; + klass.mData.mAttribute[1] = attributes[1]; + + std::vector majorSkills = mCreateClassDialog->getMajorSkills(); + std::vector minorSkills = mCreateClassDialog->getMinorSkills(); + assert(majorSkills.size() >= sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0])); + assert(minorSkills.size() >= sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0])); + for (size_t i = 0; i < sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0]); ++i) + { + klass.mData.mSkills[i][1] = majorSkills[i]; + klass.mData.mSkills[i][0] = minorSkills[i]; + } + + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(klass); + mPlayerClass = klass; + MWBase::Environment::get().getWindowManager()->setPlayerClass(klass); - case GM_ClassCreate: MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog); mCreateClassDialog = 0; - mCreateClassDialog = new CreateClassDialog(); - mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); - mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); - mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack); - mCreateClassDialog->setVisible(true); - if (mCreationStage < CSE_RaceChosen) - mCreationStage = CSE_RaceChosen; - break; - case GM_ClassGenerate: - mGenerateClassStep = 0; - mGenerateClass = ""; - mGenerateClassSpecializations[0] = 0; - mGenerateClassSpecializations[1] = 0; - mGenerateClassSpecializations[2] = 0; - showClassQuestionDialog(); - if (mCreationStage < CSE_RaceChosen) - mCreationStage = CSE_RaceChosen; - break; - case GM_Review: - MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); - mReviewDialog = 0; - mReviewDialog = new ReviewDialog(); - mReviewDialog->setPlayerName(mPlayerName); - mReviewDialog->setRace(mPlayerRaceId); - mReviewDialog->setClass(mPlayerClass); - mReviewDialog->setBirthSign(mPlayerBirthSignId); + } - mReviewDialog->setHealth(mPlayerHealth); - mReviewDialog->setMagicka(mPlayerMagicka); - mReviewDialog->setFatigue(mPlayerFatigue); - - { - std::map > attributes = MWBase::Environment::get().getWindowManager()->getPlayerAttributeValues(); - for (std::map >::iterator it = attributes.begin(); - it != attributes.end(); ++it) - { - mReviewDialog->setAttribute(static_cast (it->first), it->second); - } - } - - { - std::map > skills = MWBase::Environment::get().getWindowManager()->getPlayerSkillValues(); - for (std::map >::iterator it = skills.begin(); - it != skills.end(); ++it) - { - mReviewDialog->setSkillValue(static_cast (it->first), it->second); - } - mReviewDialog->configureSkills(MWBase::Environment::get().getWindowManager()->getPlayerMajorSkills(), MWBase::Environment::get().getWindowManager()->getPlayerMinorSkills()); - } - - mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone); - mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack); - mReviewDialog->eventActivateDialog += MyGUI::newDelegate(this, &CharacterCreation::onReviewActivateDialog); - mReviewDialog->setVisible(true); - if (mCreationStage < CSE_BirthSignChosen) - mCreationStage = CSE_BirthSignChosen; - break; - } -} - -void CharacterCreation::setPlayerHealth (const MWMechanics::DynamicStat& value) -{ - mPlayerHealth = value; -} - -void CharacterCreation::setPlayerMagicka (const MWMechanics::DynamicStat& value) -{ - mPlayerMagicka = value; -} - -void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& value) -{ - mPlayerFatigue = value; -} - -void CharacterCreation::onReviewDialogDone(WindowBase* parWindow) -{ - MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); - mReviewDialog = 0; - - MWBase::Environment::get().getWindowManager()->popGuiMode(); -} - -void CharacterCreation::onReviewDialogBack() -{ - MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); - mReviewDialog = 0; - - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); -} - -void CharacterCreation::onReviewActivateDialog(int parDialog) -{ - MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); - mReviewDialog = 0; - mCreationStage = CSE_ReviewNext; - - MWBase::Environment::get().getWindowManager()->popGuiMode(); - - switch(parDialog) - { - case ReviewDialog::NAME_DIALOG: - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name); - break; - case ReviewDialog::RACE_DIALOG: - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); - break; - case ReviewDialog::CLASS_DIALOG: - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); - break; - case ReviewDialog::BIRTHSIGN_DIALOG: + if (mCreationStage == CSE_ReviewNext) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else if (mCreationStage >= CSE_ClassChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); - }; -} - -void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) -{ - if (mPickClassDialog) - { - const std::string &classId = mPickClassDialog->getClassId(); - if (!classId.empty()) - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); - - const ESM::Class *klass = - MWBase::Environment::get().getWorld()->getStore().get().find(classId); - if (klass) + } + else { - mPlayerClass = *klass; - MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); + mCreationStage = CSE_ClassChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); } - MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); - mPickClassDialog = 0; } - //TODO This bit gets repeated a few times; wrap it in a function - if (mCreationStage == CSE_ReviewNext) + void CharacterCreation::onCreateClassDialogBack() { - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); - } - else if (mCreationStage >= CSE_ClassChosen) - { - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); - } - else - { - mCreationStage = CSE_ClassChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); - } -} - -void CharacterCreation::onPickClassDialogBack() -{ - if (mPickClassDialog) - { - const std::string classId = mPickClassDialog->getClassId(); - if (!classId.empty()) - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); - MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog); - mPickClassDialog = 0; - } - - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); -} - -void CharacterCreation::onClassChoice(int _index) -{ - MWBase::Environment::get().getWindowManager()->removeDialog(mClassChoiceDialog); - mClassChoiceDialog = 0; - - MWBase::Environment::get().getWindowManager()->popGuiMode(); - - switch(_index) - { - case ClassChoiceDialog::Class_Generate: - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassGenerate); - break; - case ClassChoiceDialog::Class_Pick: - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassPick); - break; - case ClassChoiceDialog::Class_Create: - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_ClassCreate); - break; - case ClassChoiceDialog::Class_Back: - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); - break; - - }; -} - -void CharacterCreation::onNameDialogDone(WindowBase* parWindow) -{ - if (mNameDialog) - { - mPlayerName = mNameDialog->getTextInput(); - MWBase::Environment::get().getWindowManager()->setValue("name", mPlayerName); - MWBase::Environment::get().getMechanicsManager()->setPlayerName(mPlayerName); - MWBase::Environment::get().getWindowManager()->removeDialog(mNameDialog); - mNameDialog = 0; - } - - if (mCreationStage == CSE_ReviewNext) - { - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); - } - else if (mCreationStage >= CSE_NameChosen) - { - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); - } - else - { - mCreationStage = CSE_NameChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); - } -} - -void CharacterCreation::onRaceDialogBack() -{ - if (mRaceDialog) - { - const ESM::NPC &data = mRaceDialog->getResult(); - mPlayerRaceId = data.mRace; - if (!mPlayerRaceId.empty()) { - MWBase::Environment::get().getMechanicsManager()->setPlayerRace( - data.mRace, - data.isMale(), - data.mHead, - data.mHair - ); - } - MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); - mRaceDialog = 0; - } - - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name); -} - -void CharacterCreation::onRaceDialogDone(WindowBase* parWindow) -{ - if (mRaceDialog) - { - const ESM::NPC &data = mRaceDialog->getResult(); - mPlayerRaceId = data.mRace; - if (!mPlayerRaceId.empty()) { - MWBase::Environment::get().getMechanicsManager()->setPlayerRace( - data.mRace, - data.isMale(), - data.mHead, - data.mHair - ); - } - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->rebuildAvatar(); - - MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog); - mRaceDialog = 0; - } - - if (mCreationStage == CSE_ReviewNext) - { - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); - } - else if (mCreationStage >= CSE_RaceChosen) - { - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); - } - else - { - mCreationStage = CSE_RaceChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); - } -} - -void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) -{ - if (mBirthSignDialog) - { - mPlayerBirthSignId = mBirthSignDialog->getBirthId(); - if (!mPlayerBirthSignId.empty()) - MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mPlayerBirthSignId); - MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); - mBirthSignDialog = 0; - } - - if (mCreationStage >= CSE_BirthSignChosen) - { - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); - } - else - { - mCreationStage = CSE_BirthSignChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); - } -} - -void CharacterCreation::onBirthSignDialogBack() -{ - if (mBirthSignDialog) - { - MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mBirthSignDialog->getBirthId()); - MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog); - mBirthSignDialog = 0; - } - - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); -} - -void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) -{ - if (mCreateClassDialog) - { - ESM::Class klass; - klass.mName = mCreateClassDialog->getName(); - klass.mDescription = mCreateClassDialog->getDescription(); - klass.mData.mSpecialization = mCreateClassDialog->getSpecializationId(); - klass.mData.mIsPlayable = 0x1; - - std::vector attributes = mCreateClassDialog->getFavoriteAttributes(); - assert(attributes.size() == 2); - klass.mData.mAttribute[0] = attributes[0]; - klass.mData.mAttribute[1] = attributes[1]; - - std::vector majorSkills = mCreateClassDialog->getMajorSkills(); - std::vector minorSkills = mCreateClassDialog->getMinorSkills(); - assert(majorSkills.size() >= sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0])); - assert(minorSkills.size() >= sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0])); - for (size_t i = 0; i < sizeof(klass.mData.mSkills)/sizeof(klass.mData.mSkills[0]); ++i) - { - klass.mData.mSkills[i][1] = majorSkills[i]; - klass.mData.mSkills[i][0] = minorSkills[i]; - } - - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(klass); - mPlayerClass = klass; - MWBase::Environment::get().getWindowManager()->setPlayerClass(klass); - MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog); mCreateClassDialog = 0; - } - if (mCreationStage == CSE_ReviewNext) - { - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); - } - else if (mCreationStage >= CSE_ClassChosen) - { - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); - } - else - { - mCreationStage = CSE_ClassChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); - } -} - -void CharacterCreation::onCreateClassDialogBack() -{ - MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog); - mCreateClassDialog = 0; - - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); -} - -void CharacterCreation::onClassQuestionChosen(int _index) -{ - MWBase::Environment::get().getSoundManager()->stopSay(); - - MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassQuestionDialog); - mGenerateClassQuestionDialog = 0; - - if (_index < 0 || _index >= 3) - { MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); - return; } - ESM::Class::Specialization specialization = mSpecializations[_index]; - if (specialization == ESM::Class::Stealth) - ++mGenerateClassSpecializations[0]; - else if (specialization == ESM::Class::Combat) - ++mGenerateClassSpecializations[1]; - else if (specialization == ESM::Class::Magic) - ++mGenerateClassSpecializations[2]; - ++mGenerateClassStep; - showClassQuestionDialog(); -} - -void CharacterCreation::showClassQuestionDialog() -{ - if (mGenerateClassStep == 10) + void CharacterCreation::onClassQuestionChosen(int _index) { - static boost::array classes = { { - {"Acrobat", {6, 2, 2}}, - {"Agent", {6, 1, 3}}, - {"Archer", {3, 5, 2}}, - {"Archer", {5, 5, 0}}, - {"Assassin", {6, 3, 1}}, - {"Barbarian", {3, 6, 1}}, - {"Bard", {3, 3, 3}}, - {"Battlemage", {1, 3, 6}}, - {"Crusader", {1, 6, 3}}, - {"Healer", {3, 1, 6}}, - {"Knight", {2, 6, 2}}, - {"Monk", {5, 3, 2}}, - {"Nightblade", {4, 2, 4}}, - {"Pilgrim", {5, 2, 3}}, - {"Rogue", {3, 4, 3}}, - {"Rogue", {4, 4, 2}}, - {"Rogue", {5, 4, 1}}, - {"Scout", {2, 5, 3}}, - {"Sorcerer", {2, 2, 6}}, - {"Spellsword", {2, 4, 4}}, - {"Spellsword", {5, 1, 4}}, - {"Witchhunter", {2, 3, 5}}, - {"Witchhunter", {5, 0, 5}} - } }; + MWBase::Environment::get().getSoundManager()->stopSay(); - int match = -1; - for (unsigned i = 0; i < classes.size(); ++i) + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassQuestionDialog); + mGenerateClassQuestionDialog = 0; + + if (_index < 0 || _index >= 3) { - if (mGenerateClassSpecializations[0] == classes[i].points[0] && - mGenerateClassSpecializations[1] == classes[i].points[1] && - mGenerateClassSpecializations[2] == classes[i].points[2]) - { - match = i; - mGenerateClass = classes[i].id; - break; - } + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + return; } - if (match == -1) + ESM::Class::Specialization specialization = mSpecializations[_index]; + if (specialization == ESM::Class::Stealth) + ++mGenerateClassSpecializations[0]; + else if (specialization == ESM::Class::Combat) + ++mGenerateClassSpecializations[1]; + else if (specialization == ESM::Class::Magic) + ++mGenerateClassSpecializations[2]; + ++mGenerateClassStep; + showClassQuestionDialog(); + } + + void CharacterCreation::showClassQuestionDialog() + { + if (mGenerateClassStep == 10) { - if (mGenerateClassSpecializations[0] >= 7) - mGenerateClass = "Thief"; - else if (mGenerateClassSpecializations[1] >= 7) - mGenerateClass = "Warrior"; - else if (mGenerateClassSpecializations[2] >= 7) - mGenerateClass = "Mage"; - else + static boost::array classes = { { + {"Acrobat", {6, 2, 2}}, + {"Agent", {6, 1, 3}}, + {"Archer", {3, 5, 2}}, + {"Archer", {5, 5, 0}}, + {"Assassin", {6, 3, 1}}, + {"Barbarian", {3, 6, 1}}, + {"Bard", {3, 3, 3}}, + {"Battlemage", {1, 3, 6}}, + {"Crusader", {1, 6, 3}}, + {"Healer", {3, 1, 6}}, + {"Knight", {2, 6, 2}}, + {"Monk", {5, 3, 2}}, + {"Nightblade", {4, 2, 4}}, + {"Pilgrim", {5, 2, 3}}, + {"Rogue", {3, 4, 3}}, + {"Rogue", {4, 4, 2}}, + {"Rogue", {5, 4, 1}}, + {"Scout", {2, 5, 3}}, + {"Sorcerer", {2, 2, 6}}, + {"Spellsword", {2, 4, 4}}, + {"Spellsword", {5, 1, 4}}, + {"Witchhunter", {2, 3, 5}}, + {"Witchhunter", {5, 0, 5}} + } }; + + int match = -1; + for (unsigned i = 0; i < classes.size(); ++i) { - std::cerr << "Failed to deduce class from chosen answers in generate class dialog" << std::endl; - mGenerateClass = "Thief"; + if (mGenerateClassSpecializations[0] == classes[i].points[0] && + mGenerateClassSpecializations[1] == classes[i].points[1] && + mGenerateClassSpecializations[2] == classes[i].points[2]) + { + match = i; + mGenerateClass = classes[i].id; + break; + } } + + if (match == -1) + { + if (mGenerateClassSpecializations[0] >= 7) + mGenerateClass = "Thief"; + else if (mGenerateClassSpecializations[1] >= 7) + mGenerateClass = "Warrior"; + else if (mGenerateClassSpecializations[2] >= 7) + mGenerateClass = "Mage"; + else + { + std::cerr << "Failed to deduce class from chosen answers in generate class dialog" << std::endl; + mGenerateClass = "Thief"; + } + } + + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = 0; + + mGenerateClassResultDialog = new GenerateClassResultDialog(); + mGenerateClassResultDialog->setClassId(mGenerateClass); + mGenerateClassResultDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack); + mGenerateClassResultDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassDone); + mGenerateClassResultDialog->setVisible(true); + return; } + if (mGenerateClassStep > 10) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); + return; + } + + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassQuestionDialog); + mGenerateClassQuestionDialog = 0; + + mGenerateClassQuestionDialog = new InfoBoxDialog(); + + InfoBoxDialog::ButtonList buttons; + mGenerateClassQuestionDialog->setText(sGenerateClassSteps(mGenerateClassStep).mText); + buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[0]); + buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[1]); + buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[2]); + mGenerateClassQuestionDialog->setButtons(buttons); + mGenerateClassQuestionDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen); + mGenerateClassQuestionDialog->setVisible(true); + + MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps(mGenerateClassStep).mSound); + } + + void CharacterCreation::onGenerateClassBack() + { MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); mGenerateClassResultDialog = 0; - mGenerateClassResultDialog = new GenerateClassResultDialog(); - mGenerateClassResultDialog->setClassId(mGenerateClass); - mGenerateClassResultDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack); - mGenerateClassResultDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassDone); - mGenerateClassResultDialog->setVisible(true); - return; - } + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); - if (mGenerateClassStep > 10) - { MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); - return; } - MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassQuestionDialog); - mGenerateClassQuestionDialog = 0; - - mGenerateClassQuestionDialog = new InfoBoxDialog(); - - InfoBoxDialog::ButtonList buttons; - mGenerateClassQuestionDialog->setText(sGenerateClassSteps(mGenerateClassStep).mText); - buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[0]); - buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[1]); - buttons.push_back(sGenerateClassSteps(mGenerateClassStep).mButtons[2]); - mGenerateClassQuestionDialog->setButtons(buttons); - mGenerateClassQuestionDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen); - mGenerateClassQuestionDialog->setVisible(true); - - MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps(mGenerateClassStep).mSound); -} - -void CharacterCreation::onGenerateClassBack() -{ - MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); - mGenerateClassResultDialog = 0; - - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); - - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); -} - -void CharacterCreation::onGenerateClassDone(WindowBase* parWindow) -{ - MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); - mGenerateClassResultDialog = 0; - - MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); - - const ESM::Class *klass = - MWBase::Environment::get().getWorld()->getStore().get().find(mGenerateClass); - - mPlayerClass = *klass; - MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); - - if (mCreationStage == CSE_ReviewNext) + void CharacterCreation::onGenerateClassDone(WindowBase* parWindow) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); - } - else if (mCreationStage >= CSE_ClassChosen) - { - MWBase::Environment::get().getWindowManager()->popGuiMode(); - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); - } - else - { - mCreationStage = CSE_ClassChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); - } -} + MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = 0; + + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); + + const ESM::Class *klass = + MWBase::Environment::get().getWorld()->getStore().get().find(mGenerateClass); + + mPlayerClass = *klass; + MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); + + if (mCreationStage == CSE_ReviewNext) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); + } + else if (mCreationStage >= CSE_ClassChosen) + { + MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); + } + else + { + mCreationStage = CSE_ClassChosen; + MWBase::Environment::get().getWindowManager()->popGuiMode(); + } + } + + CharacterCreation::~CharacterCreation() + { + delete mNameDialog; + delete mRaceDialog; + delete mClassChoiceDialog; + delete mGenerateClassQuestionDialog; + delete mGenerateClassResultDialog; + delete mPickClassDialog; + delete mCreateClassDialog; + delete mBirthSignDialog; + delete mReviewDialog; + } -CharacterCreation::~CharacterCreation() -{ - delete mNameDialog; - delete mRaceDialog; - delete mClassChoiceDialog; - delete mGenerateClassQuestionDialog; - delete mGenerateClassResultDialog; - delete mPickClassDialog; - delete mCreateClassDialog; - delete mBirthSignDialog; - delete mReviewDialog; } diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index e4d32c933..ab8103868 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -11,870 +11,873 @@ #undef min #undef max -using namespace MWGui; - -/* GenerateClassResultDialog */ - -GenerateClassResultDialog::GenerateClassResultDialog() - : WindowModal("openmw_chargen_generate_class_result.layout") +namespace MWGui { - // Centre dialog - center(); - setText("ReflectT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sMessageQuestionAnswer1", "")); + /* GenerateClassResultDialog */ - getWidget(mClassImage, "ClassImage"); - getWidget(mClassName, "ClassName"); - - MyGUI::Button* backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked); - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked); -} - -std::string GenerateClassResultDialog::getClassId() const -{ - return mClassName->getCaption(); -} - -void GenerateClassResultDialog::setClassId(const std::string &classId) -{ - mCurrentClassId = classId; - mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); - mClassName->setCaption(MWBase::Environment::get().getWorld()->getStore().get().find(mCurrentClassId)->mName); -} - -// widget controls - -void GenerateClassResultDialog::onOkClicked(MyGUI::Widget* _sender) -{ - eventDone(this); -} - -void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} - -/* PickClassDialog */ - -PickClassDialog::PickClassDialog() - : WindowModal("openmw_chargen_class.layout") -{ - // Centre dialog - center(); - - getWidget(mSpecializationName, "SpecializationName"); - - getWidget(mFavoriteAttribute[0], "FavoriteAttribute0"); - getWidget(mFavoriteAttribute[1], "FavoriteAttribute1"); - - for(int i = 0; i < 5; i++) + GenerateClassResultDialog::GenerateClassResultDialog() + : WindowModal("openmw_chargen_generate_class_result.layout") { - char theIndex = '0'+i; - getWidget(mMajorSkill[i], std::string("MajorSkill").append(1, theIndex)); - getWidget(mMinorSkill[i], std::string("MinorSkill").append(1, theIndex)); - } + // Centre dialog + center(); - getWidget(mClassList, "ClassList"); - mClassList->setScrollVisible(true); - mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); - mClassList->eventListMouseItemActivate += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); - mClassList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + setText("ReflectT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sMessageQuestionAnswer1", "")); - getWidget(mClassImage, "ClassImage"); + getWidget(mClassImage, "ClassImage"); + getWidget(mClassName, "ClassName"); - MyGUI::Button* backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onBackClicked); + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked); - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onOkClicked); - - updateClasses(); - updateStats(); -} - -void PickClassDialog::setNextButtonShow(bool shown) -{ - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - - if (shown) - okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); - else + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); -} + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked); + } -void PickClassDialog::open() -{ - WindowModal::open (); - updateClasses(); - updateStats(); -} - - -void PickClassDialog::setClassId(const std::string &classId) -{ - mCurrentClassId = classId; - mClassList->setIndexSelected(MyGUI::ITEM_NONE); - size_t count = mClassList->getItemCount(); - for (size_t i = 0; i < count; ++i) + std::string GenerateClassResultDialog::getClassId() const { - if (boost::iequals(*mClassList->getItemDataAt(i), classId)) + return mClassName->getCaption(); + } + + void GenerateClassResultDialog::setClassId(const std::string &classId) + { + mCurrentClassId = classId; + mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); + mClassName->setCaption(MWBase::Environment::get().getWorld()->getStore().get().find(mCurrentClassId)->mName); + } + + // widget controls + + void GenerateClassResultDialog::onOkClicked(MyGUI::Widget* _sender) + { + eventDone(this); + } + + void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } + + /* PickClassDialog */ + + PickClassDialog::PickClassDialog() + : WindowModal("openmw_chargen_class.layout") + { + // Centre dialog + center(); + + getWidget(mSpecializationName, "SpecializationName"); + + getWidget(mFavoriteAttribute[0], "FavoriteAttribute0"); + getWidget(mFavoriteAttribute[1], "FavoriteAttribute1"); + + for(int i = 0; i < 5; i++) { - mClassList->setIndexSelected(i); - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - break; + char theIndex = '0'+i; + getWidget(mMajorSkill[i], std::string("MajorSkill").append(1, theIndex)); + getWidget(mMinorSkill[i], std::string("MinorSkill").append(1, theIndex)); } + + getWidget(mClassList, "ClassList"); + mClassList->setScrollVisible(true); + mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + mClassList->eventListMouseItemActivate += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + mClassList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + + getWidget(mClassImage, "ClassImage"); + + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onBackClicked); + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onOkClicked); + + updateClasses(); + updateStats(); } - updateStats(); -} - -// widget controls - -void PickClassDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE) - return; - eventDone(this); -} - -void PickClassDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} - -void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index) -{ - if (_index == MyGUI::ITEM_NONE) - return; - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - - const std::string *classId = mClassList->getItemDataAt(_index); - if (boost::iequals(mCurrentClassId, *classId)) - return; - - mCurrentClassId = *classId; - updateStats(); -} - -// update widget content - -void PickClassDialog::updateClasses() -{ - mClassList->removeAllItems(); - - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - - int index = 0; - MWWorld::Store::iterator it = store.get().begin(); - for (; it != store.get().end(); ++it) + void PickClassDialog::setNextButtonShow(bool shown) { - bool playable = (it->mData.mIsPlayable != 0); - if (!playable) // Only display playable classes - continue; + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); - const std::string &id = it->mId; - mClassList->addItem(it->mName, id); - if (mCurrentClassId.empty()) + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } + + void PickClassDialog::open() + { + WindowModal::open (); + updateClasses(); + updateStats(); + } + + + void PickClassDialog::setClassId(const std::string &classId) + { + mCurrentClassId = classId; + mClassList->setIndexSelected(MyGUI::ITEM_NONE); + size_t count = mClassList->getItemCount(); + for (size_t i = 0; i < count; ++i) { - mCurrentClassId = id; - mClassList->setIndexSelected(index); + if (boost::iequals(*mClassList->getItemDataAt(i), classId)) + { + mClassList->setIndexSelected(i); + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + break; + } } - else if (boost::iequals(id, mCurrentClassId)) - { - mClassList->setIndexSelected(index); - } - ++index; - } -} -void PickClassDialog::updateStats() -{ - if (mCurrentClassId.empty()) - return; - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const ESM::Class *klass = store.get().search(mCurrentClassId); - if (!klass) - return; - - ESM::Class::Specialization specialization = static_cast(klass->mData.mSpecialization); - - static const char *specIds[3] = { - "sSpecializationCombat", - "sSpecializationMagic", - "sSpecializationStealth" - }; - std::string specName = MWBase::Environment::get().getWindowManager()->getGameSettingString(specIds[specialization], specIds[specialization]); - mSpecializationName->setCaption(specName); - ToolTips::createSpecializationToolTip(mSpecializationName, specName, specialization); - - mFavoriteAttribute[0]->setAttributeId(klass->mData.mAttribute[0]); - mFavoriteAttribute[1]->setAttributeId(klass->mData.mAttribute[1]); - ToolTips::createAttributeToolTip(mFavoriteAttribute[0], mFavoriteAttribute[0]->getAttributeId()); - ToolTips::createAttributeToolTip(mFavoriteAttribute[1], mFavoriteAttribute[1]->getAttributeId()); - - for (int i = 0; i < 5; ++i) - { - mMinorSkill[i]->setSkillNumber(klass->mData.mSkills[i][0]); - mMajorSkill[i]->setSkillNumber(klass->mData.mSkills[i][1]); - ToolTips::createSkillToolTip(mMinorSkill[i], klass->mData.mSkills[i][0]); - ToolTips::createSkillToolTip(mMajorSkill[i], klass->mData.mSkills[i][1]); + updateStats(); } - mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); -} + // widget controls -/* InfoBoxDialog */ - -void InfoBoxDialog::fitToText(MyGUI::TextBox* widget) -{ - MyGUI::IntCoord inner = widget->getTextRegion(); - MyGUI::IntCoord outer = widget->getCoord(); - MyGUI::IntSize size = widget->getTextSize(); - size.width += outer.width - inner.width; - size.height += outer.height - inner.height; - widget->setSize(size); -} - -void InfoBoxDialog::layoutVertically(MyGUI::Widget* widget, int margin) -{ - size_t count = widget->getChildCount(); - int pos = 0; - pos += margin; - int width = 0; - for (unsigned i = 0; i < count; ++i) + void PickClassDialog::onOkClicked(MyGUI::Widget* _sender) { - MyGUI::Widget* child = widget->getChildAt(i); - if (!child->getVisible()) - continue; - - child->setPosition(child->getLeft(), pos); - width = std::max(width, child->getWidth()); - pos += child->getHeight() + margin; - } - width += margin*2; - widget->setSize(width, pos); -} - -InfoBoxDialog::InfoBoxDialog() - : WindowModal("openmw_infobox.layout") - , mCurrentButton(-1) -{ - getWidget(mTextBox, "TextBox"); - getWidget(mText, "Text"); - mText->getSubWidgetText()->setWordWrap(true); - getWidget(mButtonBar, "ButtonBar"); - - center(); -} - -void InfoBoxDialog::setText(const std::string &str) -{ - mText->setCaption(str); - mTextBox->setVisible(!str.empty()); - fitToText(mText); -} - -std::string InfoBoxDialog::getText() const -{ - return mText->getCaption(); -} - -void InfoBoxDialog::setButtons(ButtonList &buttons) -{ - for (std::vector::iterator it = this->mButtons.begin(); it != this->mButtons.end(); ++it) - { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - this->mButtons.clear(); - mCurrentButton = -1; - - // TODO: The buttons should be generated from a template in the layout file, ie. cloning an existing widget - MyGUI::Button* button; - MyGUI::IntCoord coord = MyGUI::IntCoord(0, 0, mButtonBar->getWidth(), 10); - ButtonList::const_iterator end = buttons.end(); - for (ButtonList::const_iterator it = buttons.begin(); it != end; ++it) - { - const std::string &text = *it; - button = mButtonBar->createWidget("MW_Button", coord, MyGUI::Align::Top | MyGUI::Align::HCenter, ""); - button->getSubWidgetText()->setWordWrap(true); - button->setCaption(text); - fitToText(button); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &InfoBoxDialog::onButtonClicked); - coord.top += button->getHeight(); - this->mButtons.push_back(button); - } -} - -void InfoBoxDialog::open() -{ - WindowModal::open(); - // Fix layout - layoutVertically(mTextBox, 4); - layoutVertically(mButtonBar, 6); - layoutVertically(mMainWidget, 4 + 6); - - center(); -} - -int InfoBoxDialog::getChosenButton() const -{ - return mCurrentButton; -} - -void InfoBoxDialog::onButtonClicked(MyGUI::Widget* _sender) -{ - std::vector::const_iterator end = mButtons.end(); - int i = 0; - for (std::vector::const_iterator it = mButtons.begin(); it != end; ++it) - { - if (*it == _sender) - { - mCurrentButton = i; - eventButtonSelected(i); + if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE) return; - } - ++i; - } -} - -/* ClassChoiceDialog */ - -ClassChoiceDialog::ClassChoiceDialog() - : InfoBoxDialog() -{ - setText(""); - ButtonList buttons; - buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sClassChoiceMenu1", "")); - buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sClassChoiceMenu2", "")); - buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sClassChoiceMenu3", "")); - buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBack", "")); - setButtons(buttons); -} - -/* CreateClassDialog */ - -CreateClassDialog::CreateClassDialog() - : WindowModal("openmw_chargen_create_class.layout") - , mSpecDialog(NULL) - , mAttribDialog(NULL) - , mSkillDialog(NULL) - , mDescDialog(NULL) -{ - // Centre dialog - center(); - - setText("SpecializationT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sChooseClassMenu1", "Specialization")); - getWidget(mSpecializationName, "SpecializationName"); - mSpecializationName->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationClicked); - - setText("FavoriteAttributesT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sChooseClassMenu2", "Favorite Attributes:")); - getWidget(mFavoriteAttribute0, "FavoriteAttribute0"); - getWidget(mFavoriteAttribute1, "FavoriteAttribute1"); - mFavoriteAttribute0->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); - mFavoriteAttribute1->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); - - setText("MajorSkillT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSkillClassMajor", "")); - setText("MinorSkillT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSkillClassMinor", "")); - for(int i = 0; i < 5; i++) - { - char theIndex = '0'+i; - getWidget(mMajorSkill[i], std::string("MajorSkill").append(1, theIndex)); - getWidget(mMinorSkill[i], std::string("MinorSkill").append(1, theIndex)); - mSkills.push_back(mMajorSkill[i]); - mSkills.push_back(mMinorSkill[i]); + eventDone(this); } - std::vector::const_iterator end = mSkills.end(); - for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) + void PickClassDialog::onBackClicked(MyGUI::Widget* _sender) { - (*it)->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onSkillClicked); + eventBack(); } - setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sName", "")); - getWidget(mEditName, "EditName"); - - // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(mEditName); - - MyGUI::Button* descriptionButton; - getWidget(descriptionButton, "DescriptionButton"); - descriptionButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionClicked); - - MyGUI::Button* backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onBackClicked); - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onOkClicked); - - // Set default skills, attributes - - mFavoriteAttribute0->setAttributeId(ESM::Attribute::Strength); - mFavoriteAttribute1->setAttributeId(ESM::Attribute::Agility); - - mMajorSkill[0]->setSkillId(ESM::Skill::Block); - mMajorSkill[1]->setSkillId(ESM::Skill::Armorer); - mMajorSkill[2]->setSkillId(ESM::Skill::MediumArmor); - mMajorSkill[3]->setSkillId(ESM::Skill::HeavyArmor); - mMajorSkill[4]->setSkillId(ESM::Skill::BluntWeapon); - - mMinorSkill[0]->setSkillId(ESM::Skill::LongBlade); - mMinorSkill[1]->setSkillId(ESM::Skill::Axe); - mMinorSkill[2]->setSkillId(ESM::Skill::Spear); - mMinorSkill[3]->setSkillId(ESM::Skill::Athletics); - mMinorSkill[4]->setSkillId(ESM::Skill::Enchant); - - setSpecialization(0); - update(); -} - -CreateClassDialog::~CreateClassDialog() -{ - delete mSpecDialog; - delete mAttribDialog; - delete mSkillDialog; - delete mDescDialog; -} - -void CreateClassDialog::update() -{ - for (int i = 0; i < 5; ++i) + void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index) { - ToolTips::createSkillToolTip(mMajorSkill[i], mMajorSkill[i]->getSkillId()); - ToolTips::createSkillToolTip(mMinorSkill[i], mMinorSkill[i]->getSkillId()); + if (_index == MyGUI::ITEM_NONE) + return; + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + + const std::string *classId = mClassList->getItemDataAt(_index); + if (boost::iequals(mCurrentClassId, *classId)) + return; + + mCurrentClassId = *classId; + updateStats(); } - ToolTips::createAttributeToolTip(mFavoriteAttribute0, mFavoriteAttribute0->getAttributeId()); - ToolTips::createAttributeToolTip(mFavoriteAttribute1, mFavoriteAttribute1->getAttributeId()); -} + // update widget content -std::string CreateClassDialog::getName() const -{ - return mEditName->getOnlyText(); -} - -std::string CreateClassDialog::getDescription() const -{ - return mDescription; -} - -ESM::Class::Specialization CreateClassDialog::getSpecializationId() const -{ - return mSpecializationId; -} - -std::vector CreateClassDialog::getFavoriteAttributes() const -{ - std::vector v; - v.push_back(mFavoriteAttribute0->getAttributeId()); - v.push_back(mFavoriteAttribute1->getAttributeId()); - return v; -} - -std::vector CreateClassDialog::getMajorSkills() const -{ - std::vector v; - for(int i = 0; i < 5; i++) + void PickClassDialog::updateClasses() { - v.push_back(mMajorSkill[i]->getSkillId()); - } - return v; -} + mClassList->removeAllItems(); -std::vector CreateClassDialog::getMinorSkills() const -{ - std::vector v; - for(int i=0; i < 5; i++) - { - v.push_back(mMinorSkill[i]->getSkillId()); - } - return v; -} + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); -void CreateClassDialog::setNextButtonShow(bool shown) -{ - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - - if (shown) - okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); - else - okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); -} - -// widget controls - -void CreateClassDialog::onDialogCancel() -{ - MWBase::Environment::get().getWindowManager()->removeDialog(mSpecDialog); - mSpecDialog = 0; - - MWBase::Environment::get().getWindowManager()->removeDialog(mAttribDialog); - mAttribDialog = 0; - - MWBase::Environment::get().getWindowManager()->removeDialog(mSkillDialog); - mSkillDialog = 0; - - MWBase::Environment::get().getWindowManager()->removeDialog(mDescDialog); - mDescDialog = 0; -} - -void CreateClassDialog::onSpecializationClicked(MyGUI::Widget* _sender) -{ - delete mSpecDialog; - mSpecDialog = new SelectSpecializationDialog(); - mSpecDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); - mSpecDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationSelected); - mSpecDialog->setVisible(true); -} - -void CreateClassDialog::onSpecializationSelected() -{ - mSpecializationId = mSpecDialog->getSpecializationId(); - setSpecialization(mSpecializationId); - - MWBase::Environment::get().getWindowManager()->removeDialog(mSpecDialog); - mSpecDialog = 0; -} - -void CreateClassDialog::setSpecialization(int id) -{ - mSpecializationId = (ESM::Class::Specialization) id; - static const char *specIds[3] = { - "sSpecializationCombat", - "sSpecializationMagic", - "sSpecializationStealth" - }; - std::string specName = MWBase::Environment::get().getWindowManager()->getGameSettingString(specIds[mSpecializationId], specIds[mSpecializationId]); - mSpecializationName->setCaption(specName); - ToolTips::createSpecializationToolTip(mSpecializationName, specName, mSpecializationId); -} - -void CreateClassDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) -{ - delete mAttribDialog; - mAttribDialog = new SelectAttributeDialog(); - mAffectedAttribute = _sender; - mAttribDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); - mAttribDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeSelected); - mAttribDialog->setVisible(true); -} - -void CreateClassDialog::onAttributeSelected() -{ - ESM::Attribute::AttributeID id = mAttribDialog->getAttributeId(); - if (mAffectedAttribute == mFavoriteAttribute0) - { - if (mFavoriteAttribute1->getAttributeId() == id) - mFavoriteAttribute1->setAttributeId(mFavoriteAttribute0->getAttributeId()); - } - else if (mAffectedAttribute == mFavoriteAttribute1) - { - if (mFavoriteAttribute0->getAttributeId() == id) - mFavoriteAttribute0->setAttributeId(mFavoriteAttribute1->getAttributeId()); - } - mAffectedAttribute->setAttributeId(id); - MWBase::Environment::get().getWindowManager()->removeDialog(mAttribDialog); - mAttribDialog = 0; - - update(); -} - -void CreateClassDialog::onSkillClicked(Widgets::MWSkillPtr _sender) -{ - delete mSkillDialog; - mSkillDialog = new SelectSkillDialog(); - mAffectedSkill = _sender; - mSkillDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); - mSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSkillSelected); - mSkillDialog->setVisible(true); -} - -void CreateClassDialog::onSkillSelected() -{ - ESM::Skill::SkillEnum id = mSkillDialog->getSkillId(); - - // Avoid duplicate skills by swapping any skill field that matches the selected one - std::vector::const_iterator end = mSkills.end(); - for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) - { - if (*it == mAffectedSkill) - continue; - if ((*it)->getSkillId() == id) + int index = 0; + MWWorld::Store::iterator it = store.get().begin(); + for (; it != store.get().end(); ++it) { - (*it)->setSkillId(mAffectedSkill->getSkillId()); - break; + bool playable = (it->mData.mIsPlayable != 0); + if (!playable) // Only display playable classes + continue; + + const std::string &id = it->mId; + mClassList->addItem(it->mName, id); + if (mCurrentClassId.empty()) + { + mCurrentClassId = id; + mClassList->setIndexSelected(index); + } + else if (boost::iequals(id, mCurrentClassId)) + { + mClassList->setIndexSelected(index); + } + ++index; } } - mAffectedSkill->setSkillId(mSkillDialog->getSkillId()); - MWBase::Environment::get().getWindowManager()->removeDialog(mSkillDialog); - mSkillDialog = 0; - update(); -} + void PickClassDialog::updateStats() + { + if (mCurrentClassId.empty()) + return; + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Class *klass = store.get().search(mCurrentClassId); + if (!klass) + return; -void CreateClassDialog::onDescriptionClicked(MyGUI::Widget* _sender) -{ - mDescDialog = new DescriptionDialog(); - mDescDialog->setTextInput(mDescription); - mDescDialog->eventDone += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionEntered); - mDescDialog->setVisible(true); -} + ESM::Class::Specialization specialization = static_cast(klass->mData.mSpecialization); -void CreateClassDialog::onDescriptionEntered(WindowBase* parWindow) -{ - mDescription = mDescDialog->getTextInput(); - MWBase::Environment::get().getWindowManager()->removeDialog(mDescDialog); - mDescDialog = 0; -} + static const char *specIds[3] = { + "sSpecializationCombat", + "sSpecializationMagic", + "sSpecializationStealth" + }; + std::string specName = MWBase::Environment::get().getWindowManager()->getGameSettingString(specIds[specialization], specIds[specialization]); + mSpecializationName->setCaption(specName); + ToolTips::createSpecializationToolTip(mSpecializationName, specName, specialization); -void CreateClassDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if(getName().size() <= 0) - return; - eventDone(this); -} + mFavoriteAttribute[0]->setAttributeId(klass->mData.mAttribute[0]); + mFavoriteAttribute[1]->setAttributeId(klass->mData.mAttribute[1]); + ToolTips::createAttributeToolTip(mFavoriteAttribute[0], mFavoriteAttribute[0]->getAttributeId()); + ToolTips::createAttributeToolTip(mFavoriteAttribute[1], mFavoriteAttribute[1]->getAttributeId()); -void CreateClassDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} + for (int i = 0; i < 5; ++i) + { + mMinorSkill[i]->setSkillNumber(klass->mData.mSkills[i][0]); + mMajorSkill[i]->setSkillNumber(klass->mData.mSkills[i][1]); + ToolTips::createSkillToolTip(mMinorSkill[i], klass->mData.mSkills[i][0]); + ToolTips::createSkillToolTip(mMajorSkill[i], klass->mData.mSkills[i][1]); + } -/* SelectSpecializationDialog */ + mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); + } -SelectSpecializationDialog::SelectSpecializationDialog() - : WindowModal("openmw_chargen_select_specialization.layout") -{ - // Centre dialog - center(); + /* InfoBoxDialog */ - setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationMenu1", "")); + void InfoBoxDialog::fitToText(MyGUI::TextBox* widget) + { + MyGUI::IntCoord inner = widget->getTextRegion(); + MyGUI::IntCoord outer = widget->getCoord(); + MyGUI::IntSize size = widget->getTextSize(); + size.width += outer.width - inner.width; + size.height += outer.height - inner.height; + widget->setSize(size); + } - getWidget(mSpecialization0, "Specialization0"); - getWidget(mSpecialization1, "Specialization1"); - getWidget(mSpecialization2, "Specialization2"); - std::string combat = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Combat], ""); - std::string magic = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Magic], ""); - std::string stealth = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Stealth], ""); + void InfoBoxDialog::layoutVertically(MyGUI::Widget* widget, int margin) + { + size_t count = widget->getChildCount(); + int pos = 0; + pos += margin; + int width = 0; + for (unsigned i = 0; i < count; ++i) + { + MyGUI::Widget* child = widget->getChildAt(i); + if (!child->getVisible()) + continue; - mSpecialization0->setCaption(combat); - mSpecialization0->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); - mSpecialization1->setCaption(magic); - mSpecialization1->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); - mSpecialization2->setCaption(stealth); - mSpecialization2->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); - mSpecializationId = ESM::Class::Combat; + child->setPosition(child->getLeft(), pos); + width = std::max(width, child->getWidth()); + pos += child->getHeight() + margin; + } + width += margin*2; + widget->setSize(width, pos); + } - ToolTips::createSpecializationToolTip(mSpecialization0, combat, ESM::Class::Combat); - ToolTips::createSpecializationToolTip(mSpecialization1, magic, ESM::Class::Magic); - ToolTips::createSpecializationToolTip(mSpecialization2, stealth, ESM::Class::Stealth); + InfoBoxDialog::InfoBoxDialog() + : WindowModal("openmw_infobox.layout") + , mCurrentButton(-1) + { + getWidget(mTextBox, "TextBox"); + getWidget(mText, "Text"); + mText->getSubWidgetText()->setWordWrap(true); + getWidget(mButtonBar, "ButtonBar"); - MyGUI::Button* cancelButton; - getWidget(cancelButton, "CancelButton"); - cancelButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sCancel", "")); - cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked); -} + center(); + } -SelectSpecializationDialog::~SelectSpecializationDialog() -{ -} + void InfoBoxDialog::setText(const std::string &str) + { + mText->setCaption(str); + mTextBox->setVisible(!str.empty()); + fitToText(mText); + } -// widget controls + std::string InfoBoxDialog::getText() const + { + return mText->getCaption(); + } -void SelectSpecializationDialog::onSpecializationClicked(MyGUI::Widget* _sender) -{ - if (_sender == mSpecialization0) + void InfoBoxDialog::setButtons(ButtonList &buttons) + { + for (std::vector::iterator it = this->mButtons.begin(); it != this->mButtons.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + this->mButtons.clear(); + mCurrentButton = -1; + + // TODO: The buttons should be generated from a template in the layout file, ie. cloning an existing widget + MyGUI::Button* button; + MyGUI::IntCoord coord = MyGUI::IntCoord(0, 0, mButtonBar->getWidth(), 10); + ButtonList::const_iterator end = buttons.end(); + for (ButtonList::const_iterator it = buttons.begin(); it != end; ++it) + { + const std::string &text = *it; + button = mButtonBar->createWidget("MW_Button", coord, MyGUI::Align::Top | MyGUI::Align::HCenter, ""); + button->getSubWidgetText()->setWordWrap(true); + button->setCaption(text); + fitToText(button); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &InfoBoxDialog::onButtonClicked); + coord.top += button->getHeight(); + this->mButtons.push_back(button); + } + } + + void InfoBoxDialog::open() + { + WindowModal::open(); + // Fix layout + layoutVertically(mTextBox, 4); + layoutVertically(mButtonBar, 6); + layoutVertically(mMainWidget, 4 + 6); + + center(); + } + + int InfoBoxDialog::getChosenButton() const + { + return mCurrentButton; + } + + void InfoBoxDialog::onButtonClicked(MyGUI::Widget* _sender) + { + std::vector::const_iterator end = mButtons.end(); + int i = 0; + for (std::vector::const_iterator it = mButtons.begin(); it != end; ++it) + { + if (*it == _sender) + { + mCurrentButton = i; + eventButtonSelected(i); + return; + } + ++i; + } + } + + /* ClassChoiceDialog */ + + ClassChoiceDialog::ClassChoiceDialog() + : InfoBoxDialog() + { + setText(""); + ButtonList buttons; + buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sClassChoiceMenu1", "")); + buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sClassChoiceMenu2", "")); + buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sClassChoiceMenu3", "")); + buttons.push_back(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBack", "")); + setButtons(buttons); + } + + /* CreateClassDialog */ + + CreateClassDialog::CreateClassDialog() + : WindowModal("openmw_chargen_create_class.layout") + , mSpecDialog(NULL) + , mAttribDialog(NULL) + , mSkillDialog(NULL) + , mDescDialog(NULL) + { + // Centre dialog + center(); + + setText("SpecializationT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sChooseClassMenu1", "Specialization")); + getWidget(mSpecializationName, "SpecializationName"); + mSpecializationName->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationClicked); + + setText("FavoriteAttributesT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sChooseClassMenu2", "Favorite Attributes:")); + getWidget(mFavoriteAttribute0, "FavoriteAttribute0"); + getWidget(mFavoriteAttribute1, "FavoriteAttribute1"); + mFavoriteAttribute0->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); + mFavoriteAttribute1->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); + + setText("MajorSkillT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSkillClassMajor", "")); + setText("MinorSkillT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSkillClassMinor", "")); + for(int i = 0; i < 5; i++) + { + char theIndex = '0'+i; + getWidget(mMajorSkill[i], std::string("MajorSkill").append(1, theIndex)); + getWidget(mMinorSkill[i], std::string("MinorSkill").append(1, theIndex)); + mSkills.push_back(mMajorSkill[i]); + mSkills.push_back(mMinorSkill[i]); + } + + std::vector::const_iterator end = mSkills.end(); + for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) + { + (*it)->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onSkillClicked); + } + + setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sName", "")); + getWidget(mEditName, "EditName"); + + // Make sure the edit box has focus + MyGUI::InputManager::getInstance().setKeyFocusWidget(mEditName); + + MyGUI::Button* descriptionButton; + getWidget(descriptionButton, "DescriptionButton"); + descriptionButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionClicked); + + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onBackClicked); + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onOkClicked); + + // Set default skills, attributes + + mFavoriteAttribute0->setAttributeId(ESM::Attribute::Strength); + mFavoriteAttribute1->setAttributeId(ESM::Attribute::Agility); + + mMajorSkill[0]->setSkillId(ESM::Skill::Block); + mMajorSkill[1]->setSkillId(ESM::Skill::Armorer); + mMajorSkill[2]->setSkillId(ESM::Skill::MediumArmor); + mMajorSkill[3]->setSkillId(ESM::Skill::HeavyArmor); + mMajorSkill[4]->setSkillId(ESM::Skill::BluntWeapon); + + mMinorSkill[0]->setSkillId(ESM::Skill::LongBlade); + mMinorSkill[1]->setSkillId(ESM::Skill::Axe); + mMinorSkill[2]->setSkillId(ESM::Skill::Spear); + mMinorSkill[3]->setSkillId(ESM::Skill::Athletics); + mMinorSkill[4]->setSkillId(ESM::Skill::Enchant); + + setSpecialization(0); + update(); + } + + CreateClassDialog::~CreateClassDialog() + { + delete mSpecDialog; + delete mAttribDialog; + delete mSkillDialog; + delete mDescDialog; + } + + void CreateClassDialog::update() + { + for (int i = 0; i < 5; ++i) + { + ToolTips::createSkillToolTip(mMajorSkill[i], mMajorSkill[i]->getSkillId()); + ToolTips::createSkillToolTip(mMinorSkill[i], mMinorSkill[i]->getSkillId()); + } + + ToolTips::createAttributeToolTip(mFavoriteAttribute0, mFavoriteAttribute0->getAttributeId()); + ToolTips::createAttributeToolTip(mFavoriteAttribute1, mFavoriteAttribute1->getAttributeId()); + } + + std::string CreateClassDialog::getName() const + { + return mEditName->getOnlyText(); + } + + std::string CreateClassDialog::getDescription() const + { + return mDescription; + } + + ESM::Class::Specialization CreateClassDialog::getSpecializationId() const + { + return mSpecializationId; + } + + std::vector CreateClassDialog::getFavoriteAttributes() const + { + std::vector v; + v.push_back(mFavoriteAttribute0->getAttributeId()); + v.push_back(mFavoriteAttribute1->getAttributeId()); + return v; + } + + std::vector CreateClassDialog::getMajorSkills() const + { + std::vector v; + for(int i = 0; i < 5; i++) + { + v.push_back(mMajorSkill[i]->getSkillId()); + } + return v; + } + + std::vector CreateClassDialog::getMinorSkills() const + { + std::vector v; + for(int i=0; i < 5; i++) + { + v.push_back(mMinorSkill[i]->getSkillId()); + } + return v; + } + + void CreateClassDialog::setNextButtonShow(bool shown) + { + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } + + // widget controls + + void CreateClassDialog::onDialogCancel() + { + MWBase::Environment::get().getWindowManager()->removeDialog(mSpecDialog); + mSpecDialog = 0; + + MWBase::Environment::get().getWindowManager()->removeDialog(mAttribDialog); + mAttribDialog = 0; + + MWBase::Environment::get().getWindowManager()->removeDialog(mSkillDialog); + mSkillDialog = 0; + + MWBase::Environment::get().getWindowManager()->removeDialog(mDescDialog); + mDescDialog = 0; + } + + void CreateClassDialog::onSpecializationClicked(MyGUI::Widget* _sender) + { + delete mSpecDialog; + mSpecDialog = new SelectSpecializationDialog(); + mSpecDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); + mSpecDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationSelected); + mSpecDialog->setVisible(true); + } + + void CreateClassDialog::onSpecializationSelected() + { + mSpecializationId = mSpecDialog->getSpecializationId(); + setSpecialization(mSpecializationId); + + MWBase::Environment::get().getWindowManager()->removeDialog(mSpecDialog); + mSpecDialog = 0; + } + + void CreateClassDialog::setSpecialization(int id) + { + mSpecializationId = (ESM::Class::Specialization) id; + static const char *specIds[3] = { + "sSpecializationCombat", + "sSpecializationMagic", + "sSpecializationStealth" + }; + std::string specName = MWBase::Environment::get().getWindowManager()->getGameSettingString(specIds[mSpecializationId], specIds[mSpecializationId]); + mSpecializationName->setCaption(specName); + ToolTips::createSpecializationToolTip(mSpecializationName, specName, mSpecializationId); + } + + void CreateClassDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) + { + delete mAttribDialog; + mAttribDialog = new SelectAttributeDialog(); + mAffectedAttribute = _sender; + mAttribDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); + mAttribDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeSelected); + mAttribDialog->setVisible(true); + } + + void CreateClassDialog::onAttributeSelected() + { + ESM::Attribute::AttributeID id = mAttribDialog->getAttributeId(); + if (mAffectedAttribute == mFavoriteAttribute0) + { + if (mFavoriteAttribute1->getAttributeId() == id) + mFavoriteAttribute1->setAttributeId(mFavoriteAttribute0->getAttributeId()); + } + else if (mAffectedAttribute == mFavoriteAttribute1) + { + if (mFavoriteAttribute0->getAttributeId() == id) + mFavoriteAttribute0->setAttributeId(mFavoriteAttribute1->getAttributeId()); + } + mAffectedAttribute->setAttributeId(id); + MWBase::Environment::get().getWindowManager()->removeDialog(mAttribDialog); + mAttribDialog = 0; + + update(); + } + + void CreateClassDialog::onSkillClicked(Widgets::MWSkillPtr _sender) + { + delete mSkillDialog; + mSkillDialog = new SelectSkillDialog(); + mAffectedSkill = _sender; + mSkillDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); + mSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSkillSelected); + mSkillDialog->setVisible(true); + } + + void CreateClassDialog::onSkillSelected() + { + ESM::Skill::SkillEnum id = mSkillDialog->getSkillId(); + + // Avoid duplicate skills by swapping any skill field that matches the selected one + std::vector::const_iterator end = mSkills.end(); + for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) + { + if (*it == mAffectedSkill) + continue; + if ((*it)->getSkillId() == id) + { + (*it)->setSkillId(mAffectedSkill->getSkillId()); + break; + } + } + + mAffectedSkill->setSkillId(mSkillDialog->getSkillId()); + MWBase::Environment::get().getWindowManager()->removeDialog(mSkillDialog); + mSkillDialog = 0; + update(); + } + + void CreateClassDialog::onDescriptionClicked(MyGUI::Widget* _sender) + { + mDescDialog = new DescriptionDialog(); + mDescDialog->setTextInput(mDescription); + mDescDialog->eventDone += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionEntered); + mDescDialog->setVisible(true); + } + + void CreateClassDialog::onDescriptionEntered(WindowBase* parWindow) + { + mDescription = mDescDialog->getTextInput(); + MWBase::Environment::get().getWindowManager()->removeDialog(mDescDialog); + mDescDialog = 0; + } + + void CreateClassDialog::onOkClicked(MyGUI::Widget* _sender) + { + if(getName().size() <= 0) + return; + eventDone(this); + } + + void CreateClassDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } + + /* SelectSpecializationDialog */ + + SelectSpecializationDialog::SelectSpecializationDialog() + : WindowModal("openmw_chargen_select_specialization.layout") + { + // Centre dialog + center(); + + setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationMenu1", "")); + + getWidget(mSpecialization0, "Specialization0"); + getWidget(mSpecialization1, "Specialization1"); + getWidget(mSpecialization2, "Specialization2"); + std::string combat = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Combat], ""); + std::string magic = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Magic], ""); + std::string stealth = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Class::sGmstSpecializationIds[ESM::Class::Stealth], ""); + + mSpecialization0->setCaption(combat); + mSpecialization0->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); + mSpecialization1->setCaption(magic); + mSpecialization1->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); + mSpecialization2->setCaption(stealth); + mSpecialization2->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); mSpecializationId = ESM::Class::Combat; - else if (_sender == mSpecialization1) - mSpecializationId = ESM::Class::Magic; - else if (_sender == mSpecialization2) - mSpecializationId = ESM::Class::Stealth; - else - return; - eventItemSelected(); -} + ToolTips::createSpecializationToolTip(mSpecialization0, combat, ESM::Class::Combat); + ToolTips::createSpecializationToolTip(mSpecialization1, magic, ESM::Class::Magic); + ToolTips::createSpecializationToolTip(mSpecialization2, stealth, ESM::Class::Stealth); -void SelectSpecializationDialog::onCancelClicked(MyGUI::Widget* _sender) -{ - eventCancel(); -} - -/* SelectAttributeDialog */ - -SelectAttributeDialog::SelectAttributeDialog() - : WindowModal("openmw_chargen_select_attribute.layout") -{ - // Centre dialog - center(); - - setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sAttributesMenu1", "")); - - for (int i = 0; i < 8; ++i) - { - Widgets::MWAttributePtr attribute; - char theIndex = '0'+i; - - getWidget(attribute, std::string("Attribute").append(1, theIndex)); - attribute->setAttributeId(ESM::Attribute::sAttributeIds[i]); - attribute->eventClicked += MyGUI::newDelegate(this, &SelectAttributeDialog::onAttributeClicked); - ToolTips::createAttributeToolTip(attribute, attribute->getAttributeId()); + MyGUI::Button* cancelButton; + getWidget(cancelButton, "CancelButton"); + cancelButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sCancel", "")); + cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked); } - MyGUI::Button* cancelButton; - getWidget(cancelButton, "CancelButton"); - cancelButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sCancel", "")); - cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked); -} - -SelectAttributeDialog::~SelectAttributeDialog() -{ -} - -// widget controls - -void SelectAttributeDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) -{ - // TODO: Change MWAttribute to set and get AttributeID enum instead of int - mAttributeId = static_cast(_sender->getAttributeId()); - eventItemSelected(); -} - -void SelectAttributeDialog::onCancelClicked(MyGUI::Widget* _sender) -{ - eventCancel(); -} - - -/* SelectSkillDialog */ - -SelectSkillDialog::SelectSkillDialog() - : WindowModal("openmw_chargen_select_skill.layout") -{ - // Centre dialog - center(); - - setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSkillsMenu1", "")); - setText("CombatLabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationCombat", "")); - setText("MagicLabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationMagic", "")); - setText("StealthLabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationStealth", "")); - - for(int i = 0; i < 9; i++) + SelectSpecializationDialog::~SelectSpecializationDialog() { - char theIndex = '0'+i; - getWidget(mCombatSkill[i], std::string("CombatSkill").append(1, theIndex)); - getWidget(mMagicSkill[i], std::string("MagicSkill").append(1, theIndex)); - getWidget(mStealthSkill[i], std::string("StealthSkill").append(1, theIndex)); } - struct {Widgets::MWSkillPtr widget; ESM::Skill::SkillEnum skillId;} mSkills[3][9] = { + // widget controls + + void SelectSpecializationDialog::onSpecializationClicked(MyGUI::Widget* _sender) + { + if (_sender == mSpecialization0) + mSpecializationId = ESM::Class::Combat; + else if (_sender == mSpecialization1) + mSpecializationId = ESM::Class::Magic; + else if (_sender == mSpecialization2) + mSpecializationId = ESM::Class::Stealth; + else + return; + + eventItemSelected(); + } + + void SelectSpecializationDialog::onCancelClicked(MyGUI::Widget* _sender) + { + eventCancel(); + } + + /* SelectAttributeDialog */ + + SelectAttributeDialog::SelectAttributeDialog() + : WindowModal("openmw_chargen_select_attribute.layout") + { + // Centre dialog + center(); + + setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sAttributesMenu1", "")); + + for (int i = 0; i < 8; ++i) { - {mCombatSkill[0], ESM::Skill::Block}, - {mCombatSkill[1], ESM::Skill::Armorer}, - {mCombatSkill[2], ESM::Skill::MediumArmor}, - {mCombatSkill[3], ESM::Skill::HeavyArmor}, - {mCombatSkill[4], ESM::Skill::BluntWeapon}, - {mCombatSkill[5], ESM::Skill::LongBlade}, - {mCombatSkill[6], ESM::Skill::Axe}, - {mCombatSkill[7], ESM::Skill::Spear}, - {mCombatSkill[8], ESM::Skill::Athletics} - }, - { - {mMagicSkill[0], ESM::Skill::Enchant}, - {mMagicSkill[1], ESM::Skill::Destruction}, - {mMagicSkill[2], ESM::Skill::Alteration}, - {mMagicSkill[3], ESM::Skill::Illusion}, - {mMagicSkill[4], ESM::Skill::Conjuration}, - {mMagicSkill[5], ESM::Skill::Mysticism}, - {mMagicSkill[6], ESM::Skill::Restoration}, - {mMagicSkill[7], ESM::Skill::Alchemy}, - {mMagicSkill[8], ESM::Skill::Unarmored} - }, - { - {mStealthSkill[0], ESM::Skill::Security}, - {mStealthSkill[1], ESM::Skill::Sneak}, - {mStealthSkill[2], ESM::Skill::Acrobatics}, - {mStealthSkill[3], ESM::Skill::LightArmor}, - {mStealthSkill[4], ESM::Skill::ShortBlade}, - {mStealthSkill[5] ,ESM::Skill::Marksman}, - {mStealthSkill[6] ,ESM::Skill::Mercantile}, - {mStealthSkill[7] ,ESM::Skill::Speechcraft}, - {mStealthSkill[8] ,ESM::Skill::HandToHand} + Widgets::MWAttributePtr attribute; + char theIndex = '0'+i; + + getWidget(attribute, std::string("Attribute").append(1, theIndex)); + attribute->setAttributeId(ESM::Attribute::sAttributeIds[i]); + attribute->eventClicked += MyGUI::newDelegate(this, &SelectAttributeDialog::onAttributeClicked); + ToolTips::createAttributeToolTip(attribute, attribute->getAttributeId()); } - }; - for (int spec = 0; spec < 3; ++spec) - { - for (int i = 0; i < 9; ++i) - { - mSkills[spec][i].widget->setSkillId(mSkills[spec][i].skillId); - mSkills[spec][i].widget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked); - ToolTips::createSkillToolTip(mSkills[spec][i].widget, mSkills[spec][i].widget->getSkillId()); - } + MyGUI::Button* cancelButton; + getWidget(cancelButton, "CancelButton"); + cancelButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sCancel", "")); + cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked); + } + + SelectAttributeDialog::~SelectAttributeDialog() + { + } + + // widget controls + + void SelectAttributeDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) + { + // TODO: Change MWAttribute to set and get AttributeID enum instead of int + mAttributeId = static_cast(_sender->getAttributeId()); + eventItemSelected(); + } + + void SelectAttributeDialog::onCancelClicked(MyGUI::Widget* _sender) + { + eventCancel(); + } + + + /* SelectSkillDialog */ + + SelectSkillDialog::SelectSkillDialog() + : WindowModal("openmw_chargen_select_skill.layout") + { + // Centre dialog + center(); + + setText("LabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSkillsMenu1", "")); + setText("CombatLabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationCombat", "")); + setText("MagicLabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationMagic", "")); + setText("StealthLabelT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sSpecializationStealth", "")); + + for(int i = 0; i < 9; i++) + { + char theIndex = '0'+i; + getWidget(mCombatSkill[i], std::string("CombatSkill").append(1, theIndex)); + getWidget(mMagicSkill[i], std::string("MagicSkill").append(1, theIndex)); + getWidget(mStealthSkill[i], std::string("StealthSkill").append(1, theIndex)); + } + + struct {Widgets::MWSkillPtr widget; ESM::Skill::SkillEnum skillId;} mSkills[3][9] = { + { + {mCombatSkill[0], ESM::Skill::Block}, + {mCombatSkill[1], ESM::Skill::Armorer}, + {mCombatSkill[2], ESM::Skill::MediumArmor}, + {mCombatSkill[3], ESM::Skill::HeavyArmor}, + {mCombatSkill[4], ESM::Skill::BluntWeapon}, + {mCombatSkill[5], ESM::Skill::LongBlade}, + {mCombatSkill[6], ESM::Skill::Axe}, + {mCombatSkill[7], ESM::Skill::Spear}, + {mCombatSkill[8], ESM::Skill::Athletics} + }, + { + {mMagicSkill[0], ESM::Skill::Enchant}, + {mMagicSkill[1], ESM::Skill::Destruction}, + {mMagicSkill[2], ESM::Skill::Alteration}, + {mMagicSkill[3], ESM::Skill::Illusion}, + {mMagicSkill[4], ESM::Skill::Conjuration}, + {mMagicSkill[5], ESM::Skill::Mysticism}, + {mMagicSkill[6], ESM::Skill::Restoration}, + {mMagicSkill[7], ESM::Skill::Alchemy}, + {mMagicSkill[8], ESM::Skill::Unarmored} + }, + { + {mStealthSkill[0], ESM::Skill::Security}, + {mStealthSkill[1], ESM::Skill::Sneak}, + {mStealthSkill[2], ESM::Skill::Acrobatics}, + {mStealthSkill[3], ESM::Skill::LightArmor}, + {mStealthSkill[4], ESM::Skill::ShortBlade}, + {mStealthSkill[5] ,ESM::Skill::Marksman}, + {mStealthSkill[6] ,ESM::Skill::Mercantile}, + {mStealthSkill[7] ,ESM::Skill::Speechcraft}, + {mStealthSkill[8] ,ESM::Skill::HandToHand} + } + }; + + for (int spec = 0; spec < 3; ++spec) + { + for (int i = 0; i < 9; ++i) + { + mSkills[spec][i].widget->setSkillId(mSkills[spec][i].skillId); + mSkills[spec][i].widget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked); + ToolTips::createSkillToolTip(mSkills[spec][i].widget, mSkills[spec][i].widget->getSkillId()); + } + } + + MyGUI::Button* cancelButton; + getWidget(cancelButton, "CancelButton"); + cancelButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sCancel", "")); + cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked); + } + + SelectSkillDialog::~SelectSkillDialog() + { + } + + // widget controls + + void SelectSkillDialog::onSkillClicked(Widgets::MWSkillPtr _sender) + { + mSkillId = _sender->getSkillId(); + eventItemSelected(); + } + + void SelectSkillDialog::onCancelClicked(MyGUI::Widget* _sender) + { + eventCancel(); + } + + /* DescriptionDialog */ + + DescriptionDialog::DescriptionDialog() + : WindowModal("openmw_chargen_class_description.layout") + { + // Centre dialog + center(); + + getWidget(mTextEdit, "TextEdit"); + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DescriptionDialog::onOkClicked); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sInputMenu1", "")); + + // Make sure the edit box has focus + MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); + } + + DescriptionDialog::~DescriptionDialog() + { + } + + // widget controls + + void DescriptionDialog::onOkClicked(MyGUI::Widget* _sender) + { + eventDone(this); } - MyGUI::Button* cancelButton; - getWidget(cancelButton, "CancelButton"); - cancelButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sCancel", "")); - cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked); -} - -SelectSkillDialog::~SelectSkillDialog() -{ -} - -// widget controls - -void SelectSkillDialog::onSkillClicked(Widgets::MWSkillPtr _sender) -{ - mSkillId = _sender->getSkillId(); - eventItemSelected(); -} - -void SelectSkillDialog::onCancelClicked(MyGUI::Widget* _sender) -{ - eventCancel(); -} - -/* DescriptionDialog */ - -DescriptionDialog::DescriptionDialog() - : WindowModal("openmw_chargen_class_description.layout") -{ - // Centre dialog - center(); - - getWidget(mTextEdit, "TextEdit"); - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DescriptionDialog::onOkClicked); - okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sInputMenu1", "")); - - // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); -} - -DescriptionDialog::~DescriptionDialog() -{ -} - -// widget controls - -void DescriptionDialog::onOkClicked(MyGUI::Widget* _sender) -{ - eventDone(this); } diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index bd1b0fe7a..463c645dc 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -278,16 +278,15 @@ namespace MWGui std::string Console::complete( std::string input, std::vector &matches ) { - using namespace std; - string output=input; - string tmp=input; + std::string output = input; + std::string tmp = input; bool has_front_quote = false; /* Does the input string contain things that don't have to be completed? If yes erase them. */ /* Are there quotation marks? */ - if( tmp.find('"') != string::npos ) { + if( tmp.find('"') != std::string::npos ) { int numquotes=0; - for(string::iterator it=tmp.begin(); it < tmp.end(); ++it) { + for(std::string::iterator it=tmp.begin(); it < tmp.end(); ++it) { if( *it == '"' ) numquotes++; } @@ -299,7 +298,7 @@ namespace MWGui } else { size_t pos; - if( ( ((pos = tmp.rfind(' ')) != string::npos ) ) && ( pos > tmp.rfind('"') ) ) { + if( ( ((pos = tmp.rfind(' ')) != std::string::npos ) ) && ( pos > tmp.rfind('"') ) ) { tmp.erase( 0, tmp.rfind(' ')+1); } else { @@ -311,7 +310,7 @@ namespace MWGui /* No quotation marks. Are there spaces?*/ else { size_t rpos; - if( (rpos=tmp.rfind(' ')) != string::npos ) { + if( (rpos=tmp.rfind(' ')) != std::string::npos ) { if( rpos == 0 ) { tmp.clear(); } @@ -330,7 +329,7 @@ namespace MWGui } /* Iterate through the vector. */ - for(vector::iterator it=mNames.begin(); it < mNames.end();++it) { + for(std::vector::iterator it=mNames.begin(); it < mNames.end();++it) { bool string_different=false; /* Is the string shorter than the input string? If yes skip it. */ @@ -338,7 +337,7 @@ namespace MWGui continue; /* Is the beginning of the string different from the input string? If yes skip it. */ - for( string::iterator iter=tmp.begin(), iter2=(*it).begin(); iter < tmp.end();iter++, iter2++) { + for( std::string::iterator iter=tmp.begin(), iter2=(*it).begin(); iter < tmp.end();iter++, iter2++) { if( tolower(*iter) != tolower(*iter2) ) { string_different=true; break; @@ -361,24 +360,24 @@ namespace MWGui /* Only one match. We're done. */ if( matches.size() == 1 ) { /* Adding quotation marks when the input string started with a quotation mark or has spaces in it*/ - if( ( matches.front().find(' ') != string::npos ) ) { + if( ( matches.front().find(' ') != std::string::npos ) ) { if( !has_front_quote ) - output.append(string("\"")); - return output.append(matches.front() + string("\" ")); + output.append(std::string("\"")); + return output.append(matches.front() + std::string("\" ")); } else if( has_front_quote ) { - return output.append(matches.front() + string("\" ")); + return output.append(matches.front() + std::string("\" ")); } else { - return output.append(matches.front() + string(" ")); + return output.append(matches.front() + std::string(" ")); } } /* Check if all matching strings match further than input. If yes complete to this match. */ int i = tmp.length(); - for(string::iterator iter=matches.front().begin()+tmp.length(); iter < matches.front().end(); iter++, i++) { - for(vector::iterator it=matches.begin(); it < matches.end();++it) { + for(std::string::iterator iter=matches.front().begin()+tmp.length(); iter < matches.front().end(); iter++, i++) { + for(std::vector::iterator it=matches.begin(); it < matches.end();++it) { if( tolower((*it)[i]) != tolower(*iter) ) { /* Append the longest match to the end of the output string*/ output.append(matches.front().substr( 0, i)); diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index d15a5acd3..16f253531 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -14,10 +14,6 @@ #include "tradewindow.hpp" #include "inventorywindow.hpp" -using namespace MWGui; -using namespace Widgets; - - namespace { bool compareType(std::string type1, std::string type2) @@ -68,686 +64,690 @@ namespace } } - -ContainerBase::ContainerBase(DragAndDrop* dragAndDrop) - : mDragAndDrop(dragAndDrop) - , mFilter(ContainerBase::Filter_All) - , mDisplayEquippedItems(true) - , mHighlightEquippedItems(true) +namespace MWGui { -} -void ContainerBase::setWidgets(MyGUI::Widget* containerWidget, MyGUI::ScrollView* itemView) -{ - mContainerWidget = containerWidget; - mItemView = itemView; - - mContainerWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerBase::onContainerClicked); - mContainerWidget->eventMouseWheel += MyGUI::newDelegate(this, &ContainerWindow::onMouseWheel); -} - -ContainerBase::~ContainerBase() -{ -} - -void ContainerBase::onSelectedItem(MyGUI::Widget* _sender) -{ - mSelectedItem = _sender; - - if (mDragAndDrop && !isTrading()) + ContainerBase::ContainerBase(DragAndDrop* dragAndDrop) + : mDragAndDrop(dragAndDrop) + , mFilter(ContainerBase::Filter_All) + , mDisplayEquippedItems(true) + , mHighlightEquippedItems(true) { - if(!mDragAndDrop->mIsOnDragAndDrop) + } + + void ContainerBase::setWidgets(MyGUI::Widget* containerWidget, MyGUI::ScrollView* itemView) + { + mContainerWidget = containerWidget; + mItemView = itemView; + + mContainerWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerBase::onContainerClicked); + mContainerWidget->eventMouseWheel += MyGUI::newDelegate(this, &ContainerWindow::onMouseWheel); + } + + ContainerBase::~ContainerBase() + { + } + + void ContainerBase::onSelectedItem(MyGUI::Widget* _sender) + { + mSelectedItem = _sender; + + if (mDragAndDrop && !isTrading()) + { + if(!mDragAndDrop->mIsOnDragAndDrop) + { + MWWorld::Ptr object = (*_sender->getUserData()); + int count = object.getRefData().getCount(); + + if (MyGUI::InputManager::getInstance().isShiftPressed() || count == 1) + { + startDragItem(_sender, count); + } + else if (MyGUI::InputManager::getInstance().isControlPressed()) + { + startDragItem(_sender, 1); + } + else + { + std::string message = "#{sTake}"; + CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); + dialog->open(MWWorld::Class::get(object).getName(object), message, count); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerBase::startDragItem); + } + } + else + onContainerClicked(mContainerWidget); + } + else if (isTrading()) { MWWorld::Ptr object = (*_sender->getUserData()); int count = object.getRefData().getCount(); - if (MyGUI::InputManager::getInstance().isShiftPressed() || count == 1) + if (isInventory()) { - startDragItem(_sender, count); - } - else if (MyGUI::InputManager::getInstance().isControlPressed()) - { - startDragItem(_sender, 1); - } - else - { - std::string message = "#{sTake}"; - CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); - dialog->open(MWWorld::Class::get(object).getName(object), message, count); - dialog->eventOkClicked.clear(); - dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerBase::startDragItem); - } - } - else - onContainerClicked(mContainerWidget); - } - else if (isTrading()) - { - MWWorld::Ptr object = (*_sender->getUserData()); - int count = object.getRefData().getCount(); - - if (isInventory()) - { - // the player is trying to sell an item, check if the merchant accepts it - if (!MWBase::Environment::get().getWindowManager()->getTradeWindow()->npcAcceptsItem(object)) - { - // user notification "i don't buy this item" - MWBase::Environment::get().getWindowManager()-> - messageBox("#{sBarterDialog4}"); - return; - } - } - - bool buying = isTradeWindow(); // buying or selling? - std::string message = buying ? "#{sQuanityMenuMessage02}" : "#{sQuanityMenuMessage01}"; - - if (std::find(mBoughtItems.begin(), mBoughtItems.end(), object) != mBoughtItems.end()) - { - if (MyGUI::InputManager::getInstance().isShiftPressed() || count == 1) - { - sellAlreadyBoughtItem(NULL, count); - } - else if (MyGUI::InputManager::getInstance().isControlPressed()) - { - sellAlreadyBoughtItem(NULL, 1); - } - else - { - CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); - dialog->open(MWWorld::Class::get(object).getName(object), message, count); - dialog->eventOkClicked.clear(); - dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerBase::sellAlreadyBoughtItem); - } - } - else - { - if (MyGUI::InputManager::getInstance().isShiftPressed() || count == 1) - { - sellItem(NULL, count); - } - else if (MyGUI::InputManager::getInstance().isControlPressed()) - { - sellItem(NULL, 1); - } - else - { - CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); - dialog->open(MWWorld::Class::get(object).getName(object), message, count); - dialog->eventOkClicked.clear(); - dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerBase::sellItem); - } - } - } - else - { - onSelectedItemImpl(*_sender->getUserData()); - } -} - -void ContainerBase::sellAlreadyBoughtItem(MyGUI::Widget* _sender, int count) -{ - MWWorld::Ptr object = *mSelectedItem->getUserData(); - - if (isInventory()) - { - MWBase::Environment::get().getWindowManager()->getTradeWindow()->addItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->sellToNpc(object, count, true); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->drawItems(); - } - else - { - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->addItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count, true); - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); - } - - std::string sound = MWWorld::Class::get(object).getUpSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - - drawItems(); -} - -void ContainerBase::sellItem(MyGUI::Widget* _sender, int count) -{ - MWWorld::Ptr object = *mSelectedItem->getUserData(); - - if (isInventory()) - { - MWBase::Environment::get().getWindowManager()->getTradeWindow()->addBarteredItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->sellToNpc(object, count, false); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->drawItems(); - } - else - { - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->addBarteredItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count, false); - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); - } - - std::string sound = MWWorld::Class::get(object).getUpSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - - drawItems(); -} - -void ContainerBase::startDragItem(MyGUI::Widget* _sender, int count) -{ - mDragAndDrop->mIsOnDragAndDrop = true; - mSelectedItem->detachFromWidget(); - mSelectedItem->attachToWidget(mDragAndDrop->mDragAndDropWidget); - - MWWorld::Ptr object = *mSelectedItem->getUserData(); - _unequipItem(object); - - mDragAndDrop->mDraggedCount = count; - - mDragAndDrop->mDraggedFrom = this; - - std::string sound = MWWorld::Class::get(object).getUpSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - - mDragAndDrop->mDraggedWidget = mSelectedItem; - static_cast(mSelectedItem)->setImageTexture(""); // remove the background texture (not visible during drag) - static_cast(mSelectedItem->getChildAt(0)->getChildAt(0))->setCaption( - getCountString(mDragAndDrop->mDraggedCount)); - - drawItems(); - - MWBase::Environment::get().getWindowManager()->setDragDrop(true); -} - -void ContainerBase::onContainerClicked(MyGUI::Widget* _sender) -{ - if (mDragAndDrop == NULL) return; - - if(mDragAndDrop->mIsOnDragAndDrop) //drop item here - { - MWWorld::Ptr object = *mDragAndDrop->mDraggedWidget->getUserData(); - MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - - if (mDragAndDrop->mDraggedFrom != this) - { - assert(object.getContainerStore() && "Item is not in a container!"); - - // check the container's Organic flag (if this is a container). container with Organic flag doesn't allow putting items inside - if (mPtr.getTypeName() == typeid(ESM::Container).name()) - { - MWWorld::LiveCellRef* ref = mPtr.get(); - if (ref->mBase->mFlags & ESM::Container::Organic) + // the player is trying to sell an item, check if the merchant accepts it + if (!MWBase::Environment::get().getWindowManager()->getTradeWindow()->npcAcceptsItem(object)) { - // user notification + // user notification "i don't buy this item" MWBase::Environment::get().getWindowManager()-> - messageBox("#{sContentsMessage2}"); - + messageBox("#{sBarterDialog4}"); return; } } - int origCount = object.getRefData().getCount(); + bool buying = isTradeWindow(); // buying or selling? + std::string message = buying ? "#{sQuanityMenuMessage02}" : "#{sQuanityMenuMessage01}"; - // check that we don't exceed the allowed weight (only for containers, not for inventory) - if (!isInventory()) + if (std::find(mBoughtItems.begin(), mBoughtItems.end(), object) != mBoughtItems.end()) { - float capacity = MWWorld::Class::get(mPtr).getCapacity(mPtr); - - // try adding the item, and if weight is exceeded, just remove it again. - object.getRefData().setCount(mDragAndDrop->mDraggedCount); - MWWorld::ContainerStoreIterator it = containerStore.add(object); - - float curWeight = MWWorld::Class::get(mPtr).getEncumbrance(mPtr); - if (curWeight > capacity) + if (MyGUI::InputManager::getInstance().isShiftPressed() || count == 1) { - it->getRefData().setCount(0); - object.getRefData().setCount(origCount); - // user notification - MWBase::Environment::get().getWindowManager()-> - messageBox("#{sContentsMessage3}"); - - return; + sellAlreadyBoughtItem(NULL, count); + } + else if (MyGUI::InputManager::getInstance().isControlPressed()) + { + sellAlreadyBoughtItem(NULL, 1); } else { - object.getRefData().setCount(origCount - mDragAndDrop->mDraggedCount); + CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); + dialog->open(MWWorld::Class::get(object).getName(object), message, count); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerBase::sellAlreadyBoughtItem); } } else { - object.getRefData().setCount (mDragAndDrop->mDraggedCount); - containerStore.add(object); - object.getRefData().setCount (origCount - mDragAndDrop->mDraggedCount); + if (MyGUI::InputManager::getInstance().isShiftPressed() || count == 1) + { + sellItem(NULL, count); + } + else if (MyGUI::InputManager::getInstance().isControlPressed()) + { + sellItem(NULL, 1); + } + else + { + CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); + dialog->open(MWWorld::Class::get(object).getName(object), message, count); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerBase::sellItem); + } } } + else + { + onSelectedItemImpl(*_sender->getUserData()); + } + } - mDragAndDrop->mIsOnDragAndDrop = false; - MyGUI::Gui::getInstance().destroyWidget(mDragAndDrop->mDraggedWidget); - drawItems(); - mDragAndDrop->mDraggedFrom->drawItems(); + void ContainerBase::sellAlreadyBoughtItem(MyGUI::Widget* _sender, int count) + { + MWWorld::Ptr object = *mSelectedItem->getUserData(); - mDragAndDrop->mDraggedFrom->notifyItemDragged(object, -mDragAndDrop->mDraggedCount); - notifyItemDragged(object, mDragAndDrop->mDraggedCount); + if (isInventory()) + { + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addItem(object, count); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->sellToNpc(object, count, true); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->drawItems(); + } + else + { + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->addItem(object, count); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count, true); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); + } - MWBase::Environment::get().getWindowManager()->setDragDrop(false); - - std::string sound = MWWorld::Class::get(object).getDownSoundId(object); + std::string sound = MWWorld::Class::get(object).getUpSoundId(object); MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - } -} -void ContainerBase::onMouseWheel(MyGUI::Widget* _sender, int _rel) -{ - if (mItemView->getViewOffset().left + _rel*0.3 > 0) - mItemView->setViewOffset(MyGUI::IntPoint(0, 0)); - else - mItemView->setViewOffset(MyGUI::IntPoint(mItemView->getViewOffset().left + _rel*0.3, 0)); -} - -void ContainerBase::setFilter(int filter) -{ - mFilter = filter; - drawItems(); -} - -void ContainerBase::openContainer(MWWorld::Ptr container) -{ - mPtr = container; -} - -void ContainerBase::drawItems() -{ - while (mContainerWidget->getChildCount()) - { - MyGUI::Gui::getInstance().destroyWidget(mContainerWidget->getChildAt(0)); - } - MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - - int x = 0; - int y = 0; - int maxHeight = mItemView->getSize().height - 58; - - bool onlyMagic = false; - bool noMagic = false; - int categories = 0; - if (mFilter & Filter_All) - categories |= MWWorld::ContainerStore::Type_All; - if (mFilter & Filter_Weapon) - categories |= MWWorld::ContainerStore::Type_Weapon; - if (mFilter & Filter_Apparel) - categories |= MWWorld::ContainerStore::Type_Clothing | MWWorld::ContainerStore::Type_Armor; - if (mFilter & Filter_Magic) - { - categories |= MWWorld::ContainerStore::Type_Clothing | MWWorld::ContainerStore::Type_Armor - | MWWorld::ContainerStore::Type_Weapon | MWWorld::ContainerStore::Type_Book - | MWWorld::ContainerStore::Type_Potion; - onlyMagic = true; - } - if (mFilter & Filter_Misc) - { - categories |= MWWorld::ContainerStore::Type_Miscellaneous | MWWorld::ContainerStore::Type_Book - | MWWorld::ContainerStore::Type_Ingredient | MWWorld::ContainerStore::Type_Repair - | MWWorld::ContainerStore::Type_Lockpick | MWWorld::ContainerStore::Type_Light - | MWWorld::ContainerStore::Type_Apparatus | MWWorld::ContainerStore::Type_Probe; - } - if (mFilter & Filter_Ingredients) - categories |= MWWorld::ContainerStore::Type_Ingredient; - if (mFilter & Filter_ChargedSoulstones) - categories |= MWWorld::ContainerStore::Type_Miscellaneous; - if (mFilter & Filter_NoMagic) - noMagic = true; - - /// \todo performance improvement: don't create/destroy all the widgets everytime the container window changes size, only reposition them - - std::vector< std::pair > items; - - std::vector equippedItems = getEquippedItems(); - - // add bought items (always at the beginning) - std::vector boughtItems; - for (MWWorld::ContainerStoreIterator it (mBoughtItems.begin()); it!=mBoughtItems.end(); ++it) - { - boughtItems.push_back(*it); - } - std::sort(boughtItems.begin(), boughtItems.end(), sortItems); - - for (std::vector::iterator it=boughtItems.begin(); - it != boughtItems.end(); ++it) - { - items.push_back( std::make_pair(*it, ItemState_Barter) ); + drawItems(); } - // filter out the equipped items of categories we don't want - std::vector unwantedItems = equippedItems; - for (MWWorld::ContainerStoreIterator iter (containerStore.begin(categories)); iter!=containerStore.end(); ++iter) + void ContainerBase::sellItem(MyGUI::Widget* _sender, int count) { - std::vector::iterator found = std::find(unwantedItems.begin(), unwantedItems.end(), *iter); - if (found != unwantedItems.end()) + MWWorld::Ptr object = *mSelectedItem->getUserData(); + + if (isInventory()) { - unwantedItems.erase(found); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addBarteredItem(object, count); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->sellToNpc(object, count, false); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->drawItems(); } - } - // now erase everything that's still in unwantedItems. - for (std::vector::iterator it=unwantedItems.begin(); - it != unwantedItems.end(); ++it) - { - std::vector::iterator found = std::find(equippedItems.begin(), equippedItems.end(), *it); - assert(found != equippedItems.end()); - equippedItems.erase(found); - } - // and add the items that are left (= have the correct category) - if (mDisplayEquippedItems && mHighlightEquippedItems) - { - for (std::vector::const_iterator it=equippedItems.begin(); - it != equippedItems.end(); ++it) + else { - items.push_back( std::make_pair(*it, ItemState_Equipped) ); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->addBarteredItem(object, count); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count, false); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); } + + std::string sound = MWWorld::Class::get(object).getUpSoundId(object); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + + drawItems(); } - std::vector ignoreItems = itemsToIgnore(); - - // now add the regular items - std::vector regularItems; - for (MWWorld::ContainerStoreIterator iter (containerStore.begin(categories)); iter!=containerStore.end(); ++iter) + void ContainerBase::startDragItem(MyGUI::Widget* _sender, int count) { - if ( (std::find(equippedItems.begin(), equippedItems.end(), *iter) == equippedItems.end() - || (!mHighlightEquippedItems && mDisplayEquippedItems)) - && std::find(ignoreItems.begin(), ignoreItems.end(), *iter) == ignoreItems.end() - && std::find(mBoughtItems.begin(), mBoughtItems.end(), *iter) == mBoughtItems.end()) - regularItems.push_back(*iter); + mDragAndDrop->mIsOnDragAndDrop = true; + mSelectedItem->detachFromWidget(); + mSelectedItem->attachToWidget(mDragAndDrop->mDragAndDropWidget); + + MWWorld::Ptr object = *mSelectedItem->getUserData(); + _unequipItem(object); + + mDragAndDrop->mDraggedCount = count; + + mDragAndDrop->mDraggedFrom = this; + + std::string sound = MWWorld::Class::get(object).getUpSoundId(object); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + + mDragAndDrop->mDraggedWidget = mSelectedItem; + static_cast(mSelectedItem)->setImageTexture(""); // remove the background texture (not visible during drag) + static_cast(mSelectedItem->getChildAt(0)->getChildAt(0))->setCaption( + getCountString(mDragAndDrop->mDraggedCount)); + + drawItems(); + + MWBase::Environment::get().getWindowManager()->setDragDrop(true); } - // sort them and add - std::sort(regularItems.begin(), regularItems.end(), sortItems); - for (std::vector::const_iterator it=regularItems.begin(); it!=regularItems.end(); ++it) + void ContainerBase::onContainerClicked(MyGUI::Widget* _sender) { - items.push_back( std::make_pair(*it, ItemState_Normal) ); - } + if (mDragAndDrop == NULL) return; - for (std::vector< std::pair >::const_iterator it=items.begin(); - it != items.end(); ++it) - { - const MWWorld::Ptr* iter = &((*it).first); - - - if (onlyMagic - && it->second != ItemState_Barter - && MWWorld::Class::get(*iter).getEnchantment(*iter) == "" - && iter->getTypeName() != typeid(ESM::Potion).name()) - continue; - - if (noMagic - && it->second != ItemState_Barter - && (MWWorld::Class::get(*iter).getEnchantment(*iter) != "" - || iter->getTypeName() == typeid(ESM::Potion).name())) - continue; - - if ( (mFilter & Filter_ChargedSoulstones) - && !isChargedSoulstone(*iter)) - continue; - - int displayCount = iter->getRefData().getCount(); - if (mDragAndDrop != NULL && mDragAndDrop->mIsOnDragAndDrop && *iter == *mDragAndDrop->mDraggedWidget->getUserData()) + if(mDragAndDrop->mIsOnDragAndDrop) //drop item here { - displayCount -= mDragAndDrop->mDraggedCount; - } - if(displayCount > 0) - { - std::string path = std::string("icons\\"); - path += MWWorld::Class::get(*iter).getInventoryIcon(*iter); + MWWorld::Ptr object = *mDragAndDrop->mDraggedWidget->getUserData(); + MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - // background widget (for the "equipped" frame and magic item background image) - bool isMagic = (MWWorld::Class::get(*iter).getEnchantment(*iter) != ""); - MyGUI::ImageBox* backgroundWidget = mContainerWidget->createWidget("ImageBox", MyGUI::IntCoord(x, y, 42, 42), MyGUI::Align::Default); - backgroundWidget->setUserString("ToolTipType", "ItemPtr"); - backgroundWidget->setUserData(*iter); - - std::string backgroundTex = "textures\\menu_icon"; - if (isMagic) - backgroundTex += "_magic"; - if (it->second == ItemState_Normal) + if (mDragAndDrop->mDraggedFrom != this) { - if (!isMagic) - backgroundTex = ""; - } - else if (it->second == ItemState_Equipped) - { - backgroundTex += "_equip"; - } - else if (it->second == ItemState_Barter) - { - backgroundTex += "_barter"; - } - if (backgroundTex != "") - backgroundTex += ".dds"; + assert(object.getContainerStore() && "Item is not in a container!"); - backgroundWidget->setImageTexture(backgroundTex); - if (it->second == ItemState_Barter && !isMagic) - backgroundWidget->setProperty("ImageCoord", "2 2 42 42"); - else - backgroundWidget->setProperty("ImageCoord", "0 0 42 42"); - backgroundWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerBase::onSelectedItem); - backgroundWidget->eventMouseWheel += MyGUI::newDelegate(this, &ContainerBase::onMouseWheel); + // check the container's Organic flag (if this is a container). container with Organic flag doesn't allow putting items inside + if (mPtr.getTypeName() == typeid(ESM::Container).name()) + { + MWWorld::LiveCellRef* ref = mPtr.get(); + if (ref->mBase->mFlags & ESM::Container::Organic) + { + // user notification + MWBase::Environment::get().getWindowManager()-> + messageBox("#{sContentsMessage2}"); - // image - MyGUI::ImageBox* image = backgroundWidget->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); - int pos = path.rfind("."); - path.erase(pos); - path.append(".dds"); - image->setImageTexture(path); - image->setNeedMouseFocus(false); + return; + } + } - // text widget that shows item count - MyGUI::TextBox* text = image->createWidget("SandBrightText", MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label")); - text->setTextAlign(MyGUI::Align::Right); - text->setNeedMouseFocus(false); - text->setTextShadow(true); - text->setTextShadowColour(MyGUI::Colour(0,0,0)); - text->setCaption(getCountString(displayCount)); + int origCount = object.getRefData().getCount(); - y += 42; - if (y > maxHeight) - { - x += 42; - y = 0; + // check that we don't exceed the allowed weight (only for containers, not for inventory) + if (!isInventory()) + { + float capacity = MWWorld::Class::get(mPtr).getCapacity(mPtr); + + // try adding the item, and if weight is exceeded, just remove it again. + object.getRefData().setCount(mDragAndDrop->mDraggedCount); + MWWorld::ContainerStoreIterator it = containerStore.add(object); + + float curWeight = MWWorld::Class::get(mPtr).getEncumbrance(mPtr); + if (curWeight > capacity) + { + it->getRefData().setCount(0); + object.getRefData().setCount(origCount); + // user notification + MWBase::Environment::get().getWindowManager()-> + messageBox("#{sContentsMessage3}"); + + return; + } + else + { + object.getRefData().setCount(origCount - mDragAndDrop->mDraggedCount); + } + } + else + { + object.getRefData().setCount (mDragAndDrop->mDraggedCount); + containerStore.add(object); + object.getRefData().setCount (origCount - mDragAndDrop->mDraggedCount); + } } + mDragAndDrop->mIsOnDragAndDrop = false; + MyGUI::Gui::getInstance().destroyWidget(mDragAndDrop->mDraggedWidget); + drawItems(); + mDragAndDrop->mDraggedFrom->drawItems(); + + mDragAndDrop->mDraggedFrom->notifyItemDragged(object, -mDragAndDrop->mDraggedCount); + notifyItemDragged(object, mDragAndDrop->mDraggedCount); + + MWBase::Environment::get().getWindowManager()->setDragDrop(false); + + std::string sound = MWWorld::Class::get(object).getDownSoundId(object); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); } } - MyGUI::IntSize size = MyGUI::IntSize(std::max(mItemView->getSize().width, x+42), mItemView->getSize().height); - mItemView->setCanvasSize(size); - mContainerWidget->setSize(size); - - notifyContentChanged(); -} - -std::string ContainerBase::getCountString(const int count) -{ - if (count == 1) - return ""; - if (count > 9999) - return boost::lexical_cast(int(count/1000.f)) + "k"; - else - return boost::lexical_cast(count); -} - -void ContainerBase::addBarteredItem(MWWorld::Ptr item, int count) -{ - int origCount = item.getRefData().getCount(); - item.getRefData().setCount(count); - MWWorld::ContainerStoreIterator it = mBoughtItems.add(item); - item.getRefData().setCount(origCount - count); -} - -void ContainerBase::addItem(MWWorld::Ptr item, int count) -{ - MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - - int origCount = item.getRefData().getCount(); - - item.getRefData().setCount(count); - MWWorld::ContainerStoreIterator it = containerStore.add(item); - - item.getRefData().setCount(origCount - count); -} - -void ContainerBase::transferBoughtItems() -{ - MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - - for (MWWorld::ContainerStoreIterator it(mBoughtItems.begin()); it != mBoughtItems.end(); ++it) + void ContainerBase::onMouseWheel(MyGUI::Widget* _sender, int _rel) { - containerStore.add(*it); + if (mItemView->getViewOffset().left + _rel*0.3 > 0) + mItemView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mItemView->setViewOffset(MyGUI::IntPoint(mItemView->getViewOffset().left + _rel*0.3, 0)); } -} -void ContainerBase::returnBoughtItems(MWWorld::ContainerStore& store) -{ - for (MWWorld::ContainerStoreIterator it(mBoughtItems.begin()); it != mBoughtItems.end(); ++it) + void ContainerBase::setFilter(int filter) { - store.add(*it); + mFilter = filter; + drawItems(); } -} -std::vector ContainerBase::getEquippedItems() -{ - if (mPtr.getTypeName() != typeid(ESM::NPC).name()) - return std::vector(); - - MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - - std::vector items; - - for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) + void ContainerBase::openContainer(MWWorld::Ptr container) { - MWWorld::ContainerStoreIterator it = invStore.getSlot(slot); - if (it != invStore.end()) + mPtr = container; + } + + void ContainerBase::drawItems() + { + while (mContainerWidget->getChildCount()) { - items.push_back(*it); + MyGUI::Gui::getInstance().destroyWidget(mContainerWidget->getChildAt(0)); } - } - - return items; -} - -MWWorld::ContainerStore& ContainerBase::getContainerStore() -{ - MWWorld::ContainerStore& store = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - return store; -} - -// ------------------------------------------------------------------------------------------------ - -ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop) - : ContainerBase(dragAndDrop) - , WindowBase("openmw_container_window.layout") -{ - getWidget(mDisposeCorpseButton, "DisposeCorpseButton"); - getWidget(mTakeButton, "TakeButton"); - getWidget(mCloseButton, "CloseButton"); - - MyGUI::ScrollView* itemView; - MyGUI::Widget* containerWidget; - getWidget(containerWidget, "Items"); - getWidget(itemView, "ItemView"); - setWidgets(containerWidget, itemView); - - mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked); - mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked); - mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked); - - static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &ContainerWindow::onWindowResize); - - setCoord(200,0,600,300); -} - -ContainerWindow::~ContainerWindow() -{ -} - -void ContainerWindow::onWindowResize(MyGUI::Window* window) -{ - drawItems(); -} - -void ContainerWindow::open(MWWorld::Ptr container, bool loot) -{ - mDisplayEquippedItems = true; - mHighlightEquippedItems = false; - if (container.getTypeName() == typeid(ESM::NPC).name() && !loot) - { - // we are stealing stuff - mDisplayEquippedItems = false; - } - - mDisposeCorpseButton->setVisible(loot); - - openContainer(container); - setTitle(MWWorld::Class::get(container).getName(container)); - drawItems(); -} - -void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender) -{ - if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) - { - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); - } -} - -void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender) -{ - if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) - { - // transfer everything into the player's inventory MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); + int x = 0; + int y = 0; + int maxHeight = mItemView->getSize().height - 58; + + bool onlyMagic = false; + bool noMagic = false; + int categories = 0; + if (mFilter & Filter_All) + categories |= MWWorld::ContainerStore::Type_All; + if (mFilter & Filter_Weapon) + categories |= MWWorld::ContainerStore::Type_Weapon; + if (mFilter & Filter_Apparel) + categories |= MWWorld::ContainerStore::Type_Clothing | MWWorld::ContainerStore::Type_Armor; + if (mFilter & Filter_Magic) + { + categories |= MWWorld::ContainerStore::Type_Clothing | MWWorld::ContainerStore::Type_Armor + | MWWorld::ContainerStore::Type_Weapon | MWWorld::ContainerStore::Type_Book + | MWWorld::ContainerStore::Type_Potion; + onlyMagic = true; + } + if (mFilter & Filter_Misc) + { + categories |= MWWorld::ContainerStore::Type_Miscellaneous | MWWorld::ContainerStore::Type_Book + | MWWorld::ContainerStore::Type_Ingredient | MWWorld::ContainerStore::Type_Repair + | MWWorld::ContainerStore::Type_Lockpick | MWWorld::ContainerStore::Type_Light + | MWWorld::ContainerStore::Type_Apparatus | MWWorld::ContainerStore::Type_Probe; + } + if (mFilter & Filter_Ingredients) + categories |= MWWorld::ContainerStore::Type_Ingredient; + if (mFilter & Filter_ChargedSoulstones) + categories |= MWWorld::ContainerStore::Type_Miscellaneous; + if (mFilter & Filter_NoMagic) + noMagic = true; + + /// \todo performance improvement: don't create/destroy all the widgets everytime the container window changes size, only reposition them + + std::vector< std::pair > items; + std::vector equippedItems = getEquippedItems(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWWorld::ContainerStore& playerStore = MWWorld::Class::get(player).getContainerStore(player); - - int i=0; - for (MWWorld::ContainerStoreIterator iter (containerStore.begin()); iter!=containerStore.end(); ++iter) + // add bought items (always at the beginning) + std::vector boughtItems; + for (MWWorld::ContainerStoreIterator it (mBoughtItems.begin()); it!=mBoughtItems.end(); ++it) { - if (std::find(equippedItems.begin(), equippedItems.end(), *iter) != equippedItems.end() - && !mDisplayEquippedItems) - continue; + boughtItems.push_back(*it); + } + std::sort(boughtItems.begin(), boughtItems.end(), sortItems); - playerStore.add(*iter); - - if (i==0) - { - // play the sound of the first object - std::string sound = MWWorld::Class::get(*iter).getUpSoundId(*iter); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - } - - iter->getRefData().setCount(0); - - ++i; + for (std::vector::iterator it=boughtItems.begin(); + it != boughtItems.end(); ++it) + { + items.push_back( std::make_pair(*it, ItemState_Barter) ); } + // filter out the equipped items of categories we don't want + std::vector unwantedItems = equippedItems; + for (MWWorld::ContainerStoreIterator iter (containerStore.begin(categories)); iter!=containerStore.end(); ++iter) + { + std::vector::iterator found = std::find(unwantedItems.begin(), unwantedItems.end(), *iter); + if (found != unwantedItems.end()) + { + unwantedItems.erase(found); + } + } + // now erase everything that's still in unwantedItems. + for (std::vector::iterator it=unwantedItems.begin(); + it != unwantedItems.end(); ++it) + { + std::vector::iterator found = std::find(equippedItems.begin(), equippedItems.end(), *it); + assert(found != equippedItems.end()); + equippedItems.erase(found); + } + // and add the items that are left (= have the correct category) + if (mDisplayEquippedItems && mHighlightEquippedItems) + { + for (std::vector::const_iterator it=equippedItems.begin(); + it != equippedItems.end(); ++it) + { + items.push_back( std::make_pair(*it, ItemState_Equipped) ); + } + } + + std::vector ignoreItems = itemsToIgnore(); + + // now add the regular items + std::vector regularItems; + for (MWWorld::ContainerStoreIterator iter (containerStore.begin(categories)); iter!=containerStore.end(); ++iter) + { + if ( (std::find(equippedItems.begin(), equippedItems.end(), *iter) == equippedItems.end() + || (!mHighlightEquippedItems && mDisplayEquippedItems)) + && std::find(ignoreItems.begin(), ignoreItems.end(), *iter) == ignoreItems.end() + && std::find(mBoughtItems.begin(), mBoughtItems.end(), *iter) == mBoughtItems.end()) + regularItems.push_back(*iter); + } + + // sort them and add + std::sort(regularItems.begin(), regularItems.end(), sortItems); + for (std::vector::const_iterator it=regularItems.begin(); it!=regularItems.end(); ++it) + { + items.push_back( std::make_pair(*it, ItemState_Normal) ); + } + + for (std::vector< std::pair >::const_iterator it=items.begin(); + it != items.end(); ++it) + { + const MWWorld::Ptr* iter = &((*it).first); + + + if (onlyMagic + && it->second != ItemState_Barter + && MWWorld::Class::get(*iter).getEnchantment(*iter) == "" + && iter->getTypeName() != typeid(ESM::Potion).name()) + continue; + + if (noMagic + && it->second != ItemState_Barter + && (MWWorld::Class::get(*iter).getEnchantment(*iter) != "" + || iter->getTypeName() == typeid(ESM::Potion).name())) + continue; + + if ( (mFilter & Filter_ChargedSoulstones) + && !isChargedSoulstone(*iter)) + continue; + + int displayCount = iter->getRefData().getCount(); + if (mDragAndDrop != NULL && mDragAndDrop->mIsOnDragAndDrop && *iter == *mDragAndDrop->mDraggedWidget->getUserData()) + { + displayCount -= mDragAndDrop->mDraggedCount; + } + if(displayCount > 0) + { + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(*iter).getInventoryIcon(*iter); + + // background widget (for the "equipped" frame and magic item background image) + bool isMagic = (MWWorld::Class::get(*iter).getEnchantment(*iter) != ""); + MyGUI::ImageBox* backgroundWidget = mContainerWidget->createWidget("ImageBox", MyGUI::IntCoord(x, y, 42, 42), MyGUI::Align::Default); + backgroundWidget->setUserString("ToolTipType", "ItemPtr"); + backgroundWidget->setUserData(*iter); + + std::string backgroundTex = "textures\\menu_icon"; + if (isMagic) + backgroundTex += "_magic"; + if (it->second == ItemState_Normal) + { + if (!isMagic) + backgroundTex = ""; + } + else if (it->second == ItemState_Equipped) + { + backgroundTex += "_equip"; + } + else if (it->second == ItemState_Barter) + { + backgroundTex += "_barter"; + } + if (backgroundTex != "") + backgroundTex += ".dds"; + + backgroundWidget->setImageTexture(backgroundTex); + if (it->second == ItemState_Barter && !isMagic) + backgroundWidget->setProperty("ImageCoord", "2 2 42 42"); + else + backgroundWidget->setProperty("ImageCoord", "0 0 42 42"); + backgroundWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerBase::onSelectedItem); + backgroundWidget->eventMouseWheel += MyGUI::newDelegate(this, &ContainerBase::onMouseWheel); + + // image + MyGUI::ImageBox* image = backgroundWidget->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + image->setImageTexture(path); + image->setNeedMouseFocus(false); + + // text widget that shows item count + MyGUI::TextBox* text = image->createWidget("SandBrightText", MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label")); + text->setTextAlign(MyGUI::Align::Right); + text->setNeedMouseFocus(false); + text->setTextShadow(true); + text->setTextShadowColour(MyGUI::Colour(0,0,0)); + text->setCaption(getCountString(displayCount)); + + y += 42; + if (y > maxHeight) + { + x += 42; + y = 0; + } + + } + } + + MyGUI::IntSize size = MyGUI::IntSize(std::max(mItemView->getSize().width, x+42), mItemView->getSize().height); + mItemView->setCanvasSize(size); + mContainerWidget->setSize(size); + + notifyContentChanged(); + } + + std::string ContainerBase::getCountString(const int count) + { + if (count == 1) + return ""; + if (count > 9999) + return boost::lexical_cast(int(count/1000.f)) + "k"; + else + return boost::lexical_cast(count); + } + + void ContainerBase::addBarteredItem(MWWorld::Ptr item, int count) + { + int origCount = item.getRefData().getCount(); + item.getRefData().setCount(count); + MWWorld::ContainerStoreIterator it = mBoughtItems.add(item); + item.getRefData().setCount(origCount - count); + } + + void ContainerBase::addItem(MWWorld::Ptr item, int count) + { + MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); + + int origCount = item.getRefData().getCount(); + + item.getRefData().setCount(count); + MWWorld::ContainerStoreIterator it = containerStore.add(item); + + item.getRefData().setCount(origCount - count); + } + + void ContainerBase::transferBoughtItems() + { + MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); + + for (MWWorld::ContainerStoreIterator it(mBoughtItems.begin()); it != mBoughtItems.end(); ++it) + { + containerStore.add(*it); + } + } + + void ContainerBase::returnBoughtItems(MWWorld::ContainerStore& store) + { + for (MWWorld::ContainerStoreIterator it(mBoughtItems.begin()); it != mBoughtItems.end(); ++it) + { + store.add(*it); + } + } + + std::vector ContainerBase::getEquippedItems() + { + if (mPtr.getTypeName() != typeid(ESM::NPC).name()) + return std::vector(); + + MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); + + std::vector items; + + for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) + { + MWWorld::ContainerStoreIterator it = invStore.getSlot(slot); + if (it != invStore.end()) + { + items.push_back(*it); + } + } + + return items; + } + + MWWorld::ContainerStore& ContainerBase::getContainerStore() + { + MWWorld::ContainerStore& store = MWWorld::Class::get(mPtr).getContainerStore(mPtr); + return store; + } + + // ------------------------------------------------------------------------------------------------ + + ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop) + : ContainerBase(dragAndDrop) + , WindowBase("openmw_container_window.layout") + { + getWidget(mDisposeCorpseButton, "DisposeCorpseButton"); + getWidget(mTakeButton, "TakeButton"); + getWidget(mCloseButton, "CloseButton"); + + MyGUI::ScrollView* itemView; + MyGUI::Widget* containerWidget; + getWidget(containerWidget, "Items"); + getWidget(itemView, "ItemView"); + setWidgets(containerWidget, itemView); + + mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked); + mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked); + mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked); + + static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &ContainerWindow::onWindowResize); + + setCoord(200,0,600,300); + } + + ContainerWindow::~ContainerWindow() + { + } + + void ContainerWindow::onWindowResize(MyGUI::Window* window) + { + drawItems(); + } + + void ContainerWindow::open(MWWorld::Ptr container, bool loot) + { + mDisplayEquippedItems = true; + mHighlightEquippedItems = false; + if (container.getTypeName() == typeid(ESM::NPC).name() && !loot) + { + // we are stealing stuff + mDisplayEquippedItems = false; + } + + mDisposeCorpseButton->setVisible(loot); + + openContainer(container); + setTitle(MWWorld::Class::get(container).getName(container)); + drawItems(); + } + + void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender) + { + if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) + { + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); + } + } + + void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender) + { + if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) + { + // transfer everything into the player's inventory + MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); + + std::vector equippedItems = getEquippedItems(); + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::ContainerStore& playerStore = MWWorld::Class::get(player).getContainerStore(player); + + int i=0; + for (MWWorld::ContainerStoreIterator iter (containerStore.begin()); iter!=containerStore.end(); ++iter) + { + if (std::find(equippedItems.begin(), equippedItems.end(), *iter) != equippedItems.end() + && !mDisplayEquippedItems) + continue; + + playerStore.add(*iter); + + if (i==0) + { + // play the sound of the first object + std::string sound = MWWorld::Class::get(*iter).getUpSoundId(*iter); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + } + + iter->getRefData().setCount(0); + + ++i; + } + + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); + } + } + + void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender) + { + if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) + { + onTakeAllButtonClicked(mTakeButton); + + /// \todo I don't think this is the correct flag to check + if (MWWorld::Class::get(mPtr).isEssential(mPtr)) + MWBase::Environment::get().getWindowManager()->messageBox("#{sDisposeCorpseFail}"); + else + MWBase::Environment::get().getWorld()->deleteObject(mPtr); + + mPtr = MWWorld::Ptr(); + } + } + + void ContainerWindow::onReferenceUnavailable() + { MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } -} - -void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender) -{ - if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) - { - onTakeAllButtonClicked(mTakeButton); - - /// \todo I don't think this is the correct flag to check - if (MWWorld::Class::get(mPtr).isEssential(mPtr)) - MWBase::Environment::get().getWindowManager()->messageBox("#{sDisposeCorpseFail}"); - else - MWBase::Environment::get().getWorld()->deleteObject(mPtr); - - mPtr = MWWorld::Ptr(); - } -} - -void ContainerWindow::onReferenceUnavailable() -{ - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); + } diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 392f64126..b596cef01 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -19,521 +19,521 @@ #include "inventorywindow.hpp" #include "travelwindow.hpp" -using namespace MWGui; -using namespace Widgets; - /** *Copied from the internet. */ -namespace { - -std::string lower_string(const std::string& str) +namespace { - std::string lowerCase = Misc::StringUtils::lowerCase (str); - return lowerCase; -} - -std::string::size_type find_str_ci(const std::string& str, const std::string& substr,size_t pos) -{ - return lower_string(str).find(lower_string(substr),pos); -} - -bool sortByLength (const std::string& left, const std::string& right) -{ - return left.size() > right.size(); -} -} - - - -PersuasionDialog::PersuasionDialog() - : WindowModal("openmw_persuasion_dialog.layout") -{ - getWidget(mCancelButton, "CancelButton"); - getWidget(mAdmireButton, "AdmireButton"); - getWidget(mIntimidateButton, "IntimidateButton"); - getWidget(mTauntButton, "TauntButton"); - getWidget(mBribe10Button, "Bribe10Button"); - getWidget(mBribe100Button, "Bribe100Button"); - getWidget(mBribe1000Button, "Bribe1000Button"); - getWidget(mGoldLabel, "GoldLabel"); - - mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onCancel); - mAdmireButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); - mIntimidateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); - mTauntButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); - mBribe10Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); - mBribe100Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); - mBribe1000Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); -} - -void PersuasionDialog::onCancel(MyGUI::Widget *sender) -{ - setVisible(false); -} - -void PersuasionDialog::onPersuade(MyGUI::Widget *sender) -{ - MWBase::MechanicsManager::PersuasionType type; - if (sender == mAdmireButton) type = MWBase::MechanicsManager::PT_Admire; - else if (sender == mIntimidateButton) type = MWBase::MechanicsManager::PT_Intimidate; - else if (sender == mTauntButton) type = MWBase::MechanicsManager::PT_Taunt; - else if (sender == mBribe10Button) + std::string lower_string(const std::string& str) { - MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-10); - type = MWBase::MechanicsManager::PT_Bribe10; - } - else if (sender == mBribe100Button) - { - MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-100); - type = MWBase::MechanicsManager::PT_Bribe100; - } - else /*if (sender == mBribe1000Button)*/ - { - MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-1000); - type = MWBase::MechanicsManager::PT_Bribe1000; + std::string lowerCase = Misc::StringUtils::lowerCase (str); + + return lowerCase; } - MWBase::Environment::get().getDialogueManager()->persuade(type); - - setVisible(false); -} - -void PersuasionDialog::open() -{ - WindowModal::open(); - center(); - - int playerGold = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold(); - - mBribe10Button->setEnabled (playerGold >= 10); - mBribe100Button->setEnabled (playerGold >= 100); - mBribe1000Button->setEnabled (playerGold >= 1000); - - mGoldLabel->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); -} - -// -------------------------------------------------------------------------------------------------- - -DialogueWindow::DialogueWindow() - : WindowBase("openmw_dialogue_window.layout") - , mPersuasionDialog() - , mEnabled(false) - , mServices(0) -{ - // Centre dialog - center(); - - mPersuasionDialog.setVisible(false); - - //History view - getWidget(mHistory, "History"); - mHistory->setOverflowToTheLeft(true); - mHistory->setMaxTextLength(1000000); - MyGUI::Widget* eventbox; - - //An EditBox cannot receive mouse click events, so we use an - //invisible widget on top of the editbox to receive them - getWidget(eventbox, "EventBox"); - eventbox->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onHistoryClicked); - eventbox->eventMouseWheel += MyGUI::newDelegate(this, &DialogueWindow::onMouseWheel); - - //Topics list - getWidget(mTopicsList, "TopicsList"); - mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); - - MyGUI::Button* byeButton; - getWidget(byeButton, "ByeButton"); - byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked); - - getWidget(mDispositionBar, "Disposition"); - getWidget(mDispositionText,"DispositionText"); - - static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); -} - -void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender) -{ - MyGUI::ISubWidgetText* t = mHistory->getClient()->getSubWidgetText(); - if(t == NULL) - return; - - const MyGUI::IntPoint& lastPressed = MyGUI::InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); - - size_t cursorPosition = t->getCursorPosition(lastPressed); - MyGUI::UString color = mHistory->getColorAtPos(cursorPosition); - - if (!mEnabled && color == "#572D21") - MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); - - if (!mEnabled) - return; - - if(color != "#B29154") + std::string::size_type find_str_ci(const std::string& str, const std::string& substr,size_t pos) { - MyGUI::UString key = mHistory->getColorTextAt(cursorPosition); + return lower_string(str).find(lower_string(substr),pos); + } - if(color == "#686EBA") + bool sortByLength (const std::string& left, const std::string& right) + { + return left.size() > right.size(); + } +} + +namespace MWGui +{ + + PersuasionDialog::PersuasionDialog() + : WindowModal("openmw_persuasion_dialog.layout") + { + getWidget(mCancelButton, "CancelButton"); + getWidget(mAdmireButton, "AdmireButton"); + getWidget(mIntimidateButton, "IntimidateButton"); + getWidget(mTauntButton, "TauntButton"); + getWidget(mBribe10Button, "Bribe10Button"); + getWidget(mBribe100Button, "Bribe100Button"); + getWidget(mBribe1000Button, "Bribe1000Button"); + getWidget(mGoldLabel, "GoldLabel"); + + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onCancel); + mAdmireButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + mIntimidateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + mTauntButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + mBribe10Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + mBribe100Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + mBribe1000Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade); + } + + void PersuasionDialog::onCancel(MyGUI::Widget *sender) + { + setVisible(false); + } + + void PersuasionDialog::onPersuade(MyGUI::Widget *sender) + { + MWBase::MechanicsManager::PersuasionType type; + if (sender == mAdmireButton) type = MWBase::MechanicsManager::PT_Admire; + else if (sender == mIntimidateButton) type = MWBase::MechanicsManager::PT_Intimidate; + else if (sender == mTauntButton) type = MWBase::MechanicsManager::PT_Taunt; + else if (sender == mBribe10Button) { - std::map::iterator i = mHyperLinks.upper_bound(cursorPosition); - if( !mHyperLinks.empty() ) - { - --i; - - if( i->first + i->second.mLength > cursorPosition) - { - MWBase::Environment::get().getDialogueManager()->keywordSelected(i->second.mTrueValue); - } - } - else - { - // the link was colored, but it is not in mHyperLinks. - // It means that those liunks are not marked with @# and found - // by topic name search - MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(key)); - } + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-10); + type = MWBase::MechanicsManager::PT_Bribe10; + } + else if (sender == mBribe100Button) + { + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-100); + type = MWBase::MechanicsManager::PT_Bribe100; + } + else /*if (sender == mBribe1000Button)*/ + { + MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-1000); + type = MWBase::MechanicsManager::PT_Bribe1000; } - if(color == "#572D21") - MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key)); - } -} + MWBase::Environment::get().getDialogueManager()->persuade(type); -void DialogueWindow::onWindowResize(MyGUI::Window* _sender) -{ - mTopicsList->adjustSize(); -} - -void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) -{ - if (mHistory->getVScrollPosition() - _rel*0.3 < 0) - mHistory->setVScrollPosition(0); - else - mHistory->setVScrollPosition(mHistory->getVScrollPosition() - _rel*0.3); -} - -void DialogueWindow::onByeClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); -} - -void DialogueWindow::onSelectTopic(const std::string& topic, int id) -{ - if (!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice()) - return; - - int separatorPos = 0; - for (unsigned int i=0; igetItemCount(); ++i) - { - if (mTopicsList->getItemNameAt(i) == "") - separatorPos = i; + setVisible(false); } - if (id >= separatorPos) - MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(topic)); - else + void PersuasionDialog::open() { + WindowModal::open(); + center(); + + int playerGold = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold(); + + mBribe10Button->setEnabled (playerGold >= 10); + mBribe100Button->setEnabled (playerGold >= 100); + mBribe1000Button->setEnabled (playerGold >= 1000); + + mGoldLabel->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); + } + + // -------------------------------------------------------------------------------------------------- + + DialogueWindow::DialogueWindow() + : WindowBase("openmw_dialogue_window.layout") + , mPersuasionDialog() + , mEnabled(false) + , mServices(0) + { + // Centre dialog + center(); + + mPersuasionDialog.setVisible(false); + + //History view + getWidget(mHistory, "History"); + mHistory->setOverflowToTheLeft(true); + mHistory->setMaxTextLength(1000000); + MyGUI::Widget* eventbox; + + //An EditBox cannot receive mouse click events, so we use an + //invisible widget on top of the editbox to receive them + getWidget(eventbox, "EventBox"); + eventbox->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onHistoryClicked); + eventbox->eventMouseWheel += MyGUI::newDelegate(this, &DialogueWindow::onMouseWheel); + + //Topics list + getWidget(mTopicsList, "TopicsList"); + mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); + + MyGUI::Button* byeButton; + getWidget(byeButton, "ByeButton"); + byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked); + + getWidget(mDispositionBar, "Disposition"); + getWidget(mDispositionText,"DispositionText"); + + static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); + } + + void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender) + { + MyGUI::ISubWidgetText* t = mHistory->getClient()->getSubWidgetText(); + if(t == NULL) + return; + + const MyGUI::IntPoint& lastPressed = MyGUI::InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); + + size_t cursorPosition = t->getCursorPosition(lastPressed); + MyGUI::UString color = mHistory->getColorAtPos(cursorPosition); + + if (!mEnabled && color == "#572D21") + MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + + if (!mEnabled) + return; + + if(color != "#B29154") + { + MyGUI::UString key = mHistory->getColorTextAt(cursorPosition); + + if(color == "#686EBA") + { + std::map::iterator i = mHyperLinks.upper_bound(cursorPosition); + if( !mHyperLinks.empty() ) + { + --i; + + if( i->first + i->second.mLength > cursorPosition) + { + MWBase::Environment::get().getDialogueManager()->keywordSelected(i->second.mTrueValue); + } + } + else + { + // the link was colored, but it is not in mHyperLinks. + // It means that those liunks are not marked with @# and found + // by topic name search + MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(key)); + } + } + + if(color == "#572D21") + MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key)); + } + } + + void DialogueWindow::onWindowResize(MyGUI::Window* _sender) + { + mTopicsList->adjustSize(); + } + + void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) + { + if (mHistory->getVScrollPosition() - _rel*0.3 < 0) + mHistory->setVScrollPosition(0); + else + mHistory->setVScrollPosition(mHistory->getVScrollPosition() - _rel*0.3); + } + + void DialogueWindow::onByeClicked(MyGUI::Widget* _sender) + { + MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + } + + void DialogueWindow::onSelectTopic(const std::string& topic, int id) + { + if (!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice()) + return; + + int separatorPos = 0; + for (unsigned int i=0; igetItemCount(); ++i) + { + if (mTopicsList->getItemNameAt(i) == "") + separatorPos = i; + } + + if (id >= separatorPos) + MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(topic)); + else + { + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + if (topic == gmst.find("sPersuasion")->getString()) + { + mPersuasionDialog.setVisible(true); + } + else if (topic == gmst.find("sCompanionShare")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion); + MWBase::Environment::get().getWindowManager()->showCompanionWindow(mPtr); + } + else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused()) + { + if (topic == gmst.find("sBarter")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->startTrade(mPtr); + } + else if (topic == gmst.find("sSpells")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying); + MWBase::Environment::get().getWindowManager()->getSpellBuyingWindow()->startSpellBuying(mPtr); + } + else if (topic == gmst.find("sTravel")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel); + MWBase::Environment::get().getWindowManager()->getTravelWindow()->startTravel(mPtr); + } + else if (topic == gmst.find("sSpellMakingMenuTitle")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation); + MWBase::Environment::get().getWindowManager()->startSpellMaking (mPtr); + } + else if (topic == gmst.find("sEnchanting")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting); + MWBase::Environment::get().getWindowManager()->startEnchanting (mPtr); + } + else if (topic == gmst.find("sServiceTrainingTitle")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training); + MWBase::Environment::get().getWindowManager()->startTraining (mPtr); + } + else if (topic == gmst.find("sRepair")->getString()) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair); + MWBase::Environment::get().getWindowManager()->startRepair (mPtr); + } + } + } + } + + void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName) + { + mEnabled = true; + mPtr = actor; + mTopicsList->setEnabled(true); + setTitle(npcName); + + mTopicsList->clear(); + mHyperLinks.clear(); + mHistory->setCaption(""); + updateOptions(); + } + + void DialogueWindow::setKeywords(std::list keyWords) + { + mTopicsList->clear(); + + bool isCompanion = !MWWorld::Class::get(mPtr).getScript(mPtr).empty() + && mPtr.getRefData().getLocals().getIntVar(MWWorld::Class::get(mPtr).getScript(mPtr), "companion"); + + bool anyService = mServices > 0 || isCompanion || mPtr.getTypeName() == typeid(ESM::NPC).name(); + const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - if (topic == gmst.find("sPersuasion")->getString()) + if (mPtr.getTypeName() == typeid(ESM::NPC).name()) + mTopicsList->addItem(gmst.find("sPersuasion")->getString()); + + if (mServices & Service_Trade) + mTopicsList->addItem(gmst.find("sBarter")->getString()); + + if (mServices & Service_BuySpells) + mTopicsList->addItem(gmst.find("sSpells")->getString()); + + if (mServices & Service_Travel) + mTopicsList->addItem(gmst.find("sTravel")->getString()); + + if (mServices & Service_CreateSpells) + mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString()); + + if (mServices & Service_Enchant) + mTopicsList->addItem(gmst.find("sEnchanting")->getString()); + + if (mServices & Service_Training) + mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString()); + + if (mServices & Service_Repair) + mTopicsList->addItem(gmst.find("sRepair")->getString()); + + if (isCompanion) + mTopicsList->addItem(gmst.find("sCompanionShare")->getString()); + + if (anyService) + mTopicsList->addSeparator(); + + + for(std::list::iterator it = keyWords.begin(); it != keyWords.end(); ++it) { - mPersuasionDialog.setVisible(true); + mTopicsList->addItem(*it); } - else if (topic == gmst.find("sCompanionShare")->getString()) + mTopicsList->adjustSize(); + } + + void DialogueWindow::removeKeyword(std::string keyWord) + { + if(mTopicsList->hasItem(keyWord)) { - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion); - MWBase::Environment::get().getWindowManager()->showCompanionWindow(mPtr); + mTopicsList->removeItem(keyWord); } - else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused()) + mTopicsList->adjustSize(); + } + + void addColorInString(std::string& str, const std::string& keyword,std::string color1, std::string color2) + { + size_t pos = 0; + while((pos = find_str_ci(str,keyword, pos)) != std::string::npos) { - if (topic == gmst.find("sBarter")->getString()) + // do not add color if this portion of text is already colored. { - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->startTrade(mPtr); - } - else if (topic == gmst.find("sSpells")->getString()) - { - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying); - MWBase::Environment::get().getWindowManager()->getSpellBuyingWindow()->startSpellBuying(mPtr); - } - else if (topic == gmst.find("sTravel")->getString()) - { - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel); - MWBase::Environment::get().getWindowManager()->getTravelWindow()->startTravel(mPtr); - } - else if (topic == gmst.find("sSpellMakingMenuTitle")->getString()) - { - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation); - MWBase::Environment::get().getWindowManager()->startSpellMaking (mPtr); - } - else if (topic == gmst.find("sEnchanting")->getString()) - { - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting); - MWBase::Environment::get().getWindowManager()->startEnchanting (mPtr); - } - else if (topic == gmst.find("sServiceTrainingTitle")->getString()) - { - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training); - MWBase::Environment::get().getWindowManager()->startTraining (mPtr); - } - else if (topic == gmst.find("sRepair")->getString()) - { - MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair); - MWBase::Environment::get().getWindowManager()->startRepair (mPtr); + MyGUI::TextIterator iterator (str); + MyGUI::UString colour; + while(iterator.moveNext()) + { + size_t iteratorPos = iterator.getPosition(); + iterator.getTagColour(colour); + if (iteratorPos == pos) + break; + } + + if (colour == color1) + return; } + + str.insert(pos,color1); + pos += color1.length(); + pos += keyword.length(); + str.insert(pos,color2); + pos+= color2.length(); } } -} -void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName) -{ - mEnabled = true; - mPtr = actor; - mTopicsList->setEnabled(true); - setTitle(npcName); - - mTopicsList->clear(); - mHyperLinks.clear(); - mHistory->setCaption(""); - updateOptions(); -} - -void DialogueWindow::setKeywords(std::list keyWords) -{ - mTopicsList->clear(); - - bool isCompanion = !MWWorld::Class::get(mPtr).getScript(mPtr).empty() - && mPtr.getRefData().getLocals().getIntVar(MWWorld::Class::get(mPtr).getScript(mPtr), "companion"); - - bool anyService = mServices > 0 || isCompanion || mPtr.getTypeName() == typeid(ESM::NPC).name(); - - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); - - if (mPtr.getTypeName() == typeid(ESM::NPC).name()) - mTopicsList->addItem(gmst.find("sPersuasion")->getString()); - - if (mServices & Service_Trade) - mTopicsList->addItem(gmst.find("sBarter")->getString()); - - if (mServices & Service_BuySpells) - mTopicsList->addItem(gmst.find("sSpells")->getString()); - - if (mServices & Service_Travel) - mTopicsList->addItem(gmst.find("sTravel")->getString()); - - if (mServices & Service_CreateSpells) - mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString()); - - if (mServices & Service_Enchant) - mTopicsList->addItem(gmst.find("sEnchanting")->getString()); - - if (mServices & Service_Training) - mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString()); - - if (mServices & Service_Repair) - mTopicsList->addItem(gmst.find("sRepair")->getString()); - - if (isCompanion) - mTopicsList->addItem(gmst.find("sCompanionShare")->getString()); - - if (anyService) - mTopicsList->addSeparator(); - - - for(std::list::iterator it = keyWords.begin(); it != keyWords.end(); ++it) + std::string DialogueWindow::parseText(const std::string& text) { - mTopicsList->addItem(*it); - } - mTopicsList->adjustSize(); -} + bool separatorReached = false; // only parse topics that are below the separator (this prevents actions like "Barter" that are not topics from getting blue-colored) -void DialogueWindow::removeKeyword(std::string keyWord) -{ - if(mTopicsList->hasItem(keyWord)) - { - mTopicsList->removeItem(keyWord); - } - mTopicsList->adjustSize(); -} + std::vector topics; -void addColorInString(std::string& str, const std::string& keyword,std::string color1, std::string color2) -{ - size_t pos = 0; - while((pos = find_str_ci(str,keyword, pos)) != std::string::npos) - { - // do not add color if this portion of text is already colored. + bool hasSeparator = false; + for (unsigned int i=0; igetItemCount(); ++i) { - MyGUI::TextIterator iterator (str); - MyGUI::UString colour; - while(iterator.moveNext()) - { - size_t iteratorPos = iterator.getPosition(); - iterator.getTagColour(colour); - if (iteratorPos == pos) - break; - } - - if (colour == color1) - return; + if (mTopicsList->getItemNameAt(i) == "") + hasSeparator = true; } - str.insert(pos,color1); - pos += color1.length(); - pos += keyword.length(); - str.insert(pos,color2); - pos+= color2.length(); - } -} - -std::string DialogueWindow::parseText(const std::string& text) -{ - bool separatorReached = false; // only parse topics that are below the separator (this prevents actions like "Barter" that are not topics from getting blue-colored) - - std::vector topics; - - bool hasSeparator = false; - for (unsigned int i=0; igetItemCount(); ++i) - { - if (mTopicsList->getItemNameAt(i) == "") - hasSeparator = true; - } - - for(unsigned int i = 0;igetItemCount();i++) - { - std::string keyWord = mTopicsList->getItemNameAt(i); - if (separatorReached || !hasSeparator) - topics.push_back(keyWord); - else if (keyWord == "") - separatorReached = true; - } - - // sort by length to make sure longer topics are replaced first - std::sort(topics.begin(), topics.end(), sortByLength); - - std::vector hypertext = MWDialogue::ParseHyperText(text); - - size_t historySize = 0; - if(mHistory->getClient()->getSubWidgetText() != NULL) - { - historySize = mHistory->getOnlyText().size(); - } - - std::string result; - size_t hypertextPos = 0; - for (size_t i = 0; i < hypertext.size(); ++i) - { - if (hypertext[i].mLink) + for(unsigned int i = 0;igetItemCount();i++) { - size_t asterisk_count = MWDialogue::RemovePseudoAsterisks(hypertext[i].mText); - std::string standardForm = hypertext[i].mText; - for(; asterisk_count > 0; --asterisk_count) - standardForm.append("*"); + std::string keyWord = mTopicsList->getItemNameAt(i); + if (separatorReached || !hasSeparator) + topics.push_back(keyWord); + else if (keyWord == "") + separatorReached = true; + } - standardForm = - MWBase::Environment::get().getWindowManager()-> - getTranslationDataStorage().topicStandardForm(standardForm); + // sort by length to make sure longer topics are replaced first + std::sort(topics.begin(), topics.end(), sortByLength); - if( std::find(topics.begin(), topics.end(), std::string(standardForm) ) != topics.end() ) + std::vector hypertext = MWDialogue::ParseHyperText(text); + + size_t historySize = 0; + if(mHistory->getClient()->getSubWidgetText() != NULL) + { + historySize = mHistory->getOnlyText().size(); + } + + std::string result; + size_t hypertextPos = 0; + for (size_t i = 0; i < hypertext.size(); ++i) + { + if (hypertext[i].mLink) { - result.append("#686EBA").append(hypertext[i].mText).append("#B29154"); + size_t asterisk_count = MWDialogue::RemovePseudoAsterisks(hypertext[i].mText); + std::string standardForm = hypertext[i].mText; + for(; asterisk_count > 0; --asterisk_count) + standardForm.append("*"); - mHyperLinks[historySize+hypertextPos].mLength = MyGUI::UString(hypertext[i].mText).length(); - mHyperLinks[historySize+hypertextPos].mTrueValue = lower_string(standardForm); + standardForm = + MWBase::Environment::get().getWindowManager()-> + getTranslationDataStorage().topicStandardForm(standardForm); + + if( std::find(topics.begin(), topics.end(), std::string(standardForm) ) != topics.end() ) + { + result.append("#686EBA").append(hypertext[i].mText).append("#B29154"); + + mHyperLinks[historySize+hypertextPos].mLength = MyGUI::UString(hypertext[i].mText).length(); + mHyperLinks[historySize+hypertextPos].mTrueValue = lower_string(standardForm); + } + else + result += hypertext[i].mText; } else - result += hypertext[i].mText; - } - else - { - if( !MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation() ) { - for(std::vector::const_iterator it = topics.begin(); it != topics.end(); ++it) + if( !MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation() ) { - addColorInString(hypertext[i].mText, *it, "#686EBA", "#B29154"); + for(std::vector::const_iterator it = topics.begin(); it != topics.end(); ++it) + { + addColorInString(hypertext[i].mText, *it, "#686EBA", "#B29154"); + } } + + result += hypertext[i].mText; } - result += hypertext[i].mText; + hypertextPos += MyGUI::UString(hypertext[i].mText).length(); } - hypertextPos += MyGUI::UString(hypertext[i].mText).length(); + return result; } - return result; -} - -void DialogueWindow::addText(std::string text) -{ - mHistory->addDialogText("#B29154"+parseText(text)+"#B29154"); -} - -void DialogueWindow::addMessageBox(const std::string& text) -{ - mHistory->addDialogText("\n#FFFFFF"+text+"#B29154"); -} - -void DialogueWindow::addTitle(std::string text) -{ - // This is called from the dialogue manager, so text is - // case-smashed - thus we have to retrieve the correct case - // of the text through the topic list. - for (size_t i=0; igetItemCount(); ++i) + void DialogueWindow::addText(std::string text) { - std::string item = mTopicsList->getItemNameAt(i); - if (lower_string(item) == text) - text = item; + mHistory->addDialogText("#B29154"+parseText(text)+"#B29154"); } - mHistory->addDialogHeading(text); -} - -void DialogueWindow::askQuestion(std::string question) -{ - mHistory->addDialogText("#572D21"+question+"#B29154"+" "); -} - -void DialogueWindow::updateOptions() -{ - //Clear the list of topics - mTopicsList->clear(); - mHyperLinks.clear(); - mHistory->eraseText(0, mHistory->getTextLength()); - - if (mPtr.getTypeName() == typeid(ESM::NPC).name()) + void DialogueWindow::addMessageBox(const std::string& text) { - mDispositionBar->setProgressRange(100); - mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr)); - mDispositionText->eraseText(0, mDispositionText->getTextLength()); - mDispositionText->addText("#B29154"+boost::lexical_cast(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")+"#B29154"); + mHistory->addDialogText("\n#FFFFFF"+text+"#B29154"); } -} -void DialogueWindow::goodbye() -{ - mHistory->addDialogText("\n#572D21" + MWBase::Environment::get().getWorld()->getStore().get().find("sGoodbye")->getString()); - mTopicsList->setEnabled(false); - mEnabled = false; -} - -void DialogueWindow::onReferenceUnavailable() -{ - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); -} - -void DialogueWindow::onFrame() -{ - if(mMainWidget->getVisible() && mEnabled && mPtr.getTypeName() == typeid(ESM::NPC).name()) + void DialogueWindow::addTitle(std::string text) { - int disp = std::max(0, std::min(100, - MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) - + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange())); - mDispositionBar->setProgressRange(100); - mDispositionBar->setProgressPosition(disp); - mDispositionText->eraseText(0, mDispositionText->getTextLength()); - mDispositionText->addText("#B29154"+boost::lexical_cast(disp)+std::string("/100")+"#B29154"); + // This is called from the dialogue manager, so text is + // case-smashed - thus we have to retrieve the correct case + // of the text through the topic list. + for (size_t i=0; igetItemCount(); ++i) + { + std::string item = mTopicsList->getItemNameAt(i); + if (lower_string(item) == text) + text = item; + } + + mHistory->addDialogHeading(text); + } + + void DialogueWindow::askQuestion(std::string question) + { + mHistory->addDialogText("#572D21"+question+"#B29154"+" "); + } + + void DialogueWindow::updateOptions() + { + //Clear the list of topics + mTopicsList->clear(); + mHyperLinks.clear(); + mHistory->eraseText(0, mHistory->getTextLength()); + + if (mPtr.getTypeName() == typeid(ESM::NPC).name()) + { + mDispositionBar->setProgressRange(100); + mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr)); + mDispositionText->eraseText(0, mDispositionText->getTextLength()); + mDispositionText->addText("#B29154"+boost::lexical_cast(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")+"#B29154"); + } + } + + void DialogueWindow::goodbye() + { + mHistory->addDialogText("\n#572D21" + MWBase::Environment::get().getWorld()->getStore().get().find("sGoodbye")->getString()); + mTopicsList->setEnabled(false); + mEnabled = false; + } + + void DialogueWindow::onReferenceUnavailable() + { + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); + } + + void DialogueWindow::onFrame() + { + if(mMainWidget->getVisible() && mEnabled && mPtr.getTypeName() == typeid(ESM::NPC).name()) + { + int disp = std::max(0, std::min(100, + MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) + + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange())); + mDispositionBar->setProgressRange(100); + mDispositionBar->setProgressPosition(disp); + mDispositionText->eraseText(0, mDispositionText->getTextLength()); + mDispositionText->addText("#B29154"+boost::lexical_cast(disp)+std::string("/100")+"#B29154"); + } } } diff --git a/apps/openmw/mwgui/dialoguehistory.cpp b/apps/openmw/mwgui/dialoguehistory.cpp index a122a7891..d99d55bc1 100644 --- a/apps/openmw/mwgui/dialoguehistory.cpp +++ b/apps/openmw/mwgui/dialoguehistory.cpp @@ -12,65 +12,67 @@ #include #include -using namespace MWGui; -using namespace Widgets; - -MyGUI::UString DialogueHistory::getColorAtPos(size_t _pos) +namespace MWGui { - MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour()); - MyGUI::TextIterator iterator(getCaption()); - while(iterator.moveNext()) - { - size_t pos = iterator.getPosition(); - iterator.getTagColour(colour); - if (pos < _pos) - continue; - else if (pos == _pos) - break; - } - return colour; -} -MyGUI::UString DialogueHistory::getColorTextAt(size_t _pos) -{ - bool breakOnNext = false; - MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour()); - MyGUI::UString colour2 = colour; - MyGUI::TextIterator iterator(getCaption()); - MyGUI::TextIterator col_start = iterator; - while(iterator.moveNext()) + MyGUI::UString DialogueHistory::getColorAtPos(size_t _pos) { - size_t pos = iterator.getPosition(); - iterator.getTagColour(colour); - if(colour != colour2) + MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour()); + MyGUI::TextIterator iterator(getCaption()); + while(iterator.moveNext()) { - if(breakOnNext) + size_t pos = iterator.getPosition(); + iterator.getTagColour(colour); + if (pos < _pos) + continue; + else if (pos == _pos) + break; + } + return colour; + } + + MyGUI::UString DialogueHistory::getColorTextAt(size_t _pos) + { + bool breakOnNext = false; + MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour()); + MyGUI::UString colour2 = colour; + MyGUI::TextIterator iterator(getCaption()); + MyGUI::TextIterator col_start = iterator; + while(iterator.moveNext()) + { + size_t pos = iterator.getPosition(); + iterator.getTagColour(colour); + if(colour != colour2) { - return getOnlyText().substr(col_start.getPosition(), iterator.getPosition()-col_start.getPosition()); + if(breakOnNext) + { + return getOnlyText().substr(col_start.getPosition(), iterator.getPosition()-col_start.getPosition()); + } + col_start = iterator; + colour2 = colour; + } + if (pos < _pos) + continue; + else if (pos == _pos) + { + breakOnNext = true; } - col_start = iterator; - colour2 = colour; - } - if (pos < _pos) - continue; - else if (pos == _pos) - { - breakOnNext = true; } + return ""; } - return ""; -} -void DialogueHistory::addDialogHeading(const MyGUI::UString& parText) -{ - MyGUI::UString head("\n#D8C09A"); - head.append(parText); - head.append("#B29154\n"); - addText(head); -} + void DialogueHistory::addDialogHeading(const MyGUI::UString& parText) + { + MyGUI::UString head("\n#D8C09A"); + head.append(parText); + head.append("#B29154\n"); + addText(head); + } + + void DialogueHistory::addDialogText(const MyGUI::UString& parText) + { + addText(parText); + addText("\n"); + } -void DialogueHistory::addDialogText(const MyGUI::UString& parText) -{ - addText(parText); - addText("\n"); } diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index b21b903bd..2d72f2174 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -9,8 +9,6 @@ #include #include -using namespace MWGui; - namespace { int convertFromHex(std::string hex) @@ -78,287 +76,292 @@ namespace } } -std::vector BookTextParser::split(std::string utf8Text, const int width, const int height) +namespace MWGui { - using Ogre::UTFString; - std::vector result; - MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor - utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext); - - boost::algorithm::replace_all(utf8Text, "\n", ""); - boost::algorithm::replace_all(utf8Text, "
", "\n"); - boost::algorithm::replace_all(utf8Text, "

", "\n\n"); - - UTFString text(utf8Text); - const int spacing = 48; - - const UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<'); - const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n'); - const UTFString::unicode_char SPACE = unicodeCharFromChar(' '); - - while (!text.empty()) + std::vector BookTextParser::split(std::string utf8Text, const int width, const int height) { - // read in characters until we have exceeded the size, or run out of text - int currentWidth = 0; - int currentHeight = 0; + using Ogre::UTFString; + std::vector result; - size_t currentWordStart = 0; - size_t index = 0; - while (currentHeight <= height - spacing && index < text.size()) + MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor + utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext); + + boost::algorithm::replace_all(utf8Text, "\n", ""); + boost::algorithm::replace_all(utf8Text, "
", "\n"); + boost::algorithm::replace_all(utf8Text, "

", "\n\n"); + + UTFString text(utf8Text); + const int spacing = 48; + + const UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<'); + const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n'); + const UTFString::unicode_char SPACE = unicodeCharFromChar(' '); + + while (!text.empty()) { - const UTFString::unicode_char ch = text.getChar(index); - if (ch == LEFT_ANGLE) - { - const size_t tagStart = index + 1; - const size_t tagEnd = text.find('>', tagStart); - if (tagEnd == UTFString::npos) - throw std::runtime_error("BookTextParser Error: Tag is not terminated"); - const std::string tag = text.substr(tagStart, tagEnd - tagStart).asUTF8(); + // read in characters until we have exceeded the size, or run out of text + int currentWidth = 0; + int currentHeight = 0; - if (boost::algorithm::starts_with(tag, "IMG")) + size_t currentWordStart = 0; + size_t index = 0; + while (currentHeight <= height - spacing && index < text.size()) + { + const UTFString::unicode_char ch = text.getChar(index); + if (ch == LEFT_ANGLE) { - const int h = mHeight; - parseImage(tag, false); - currentHeight += (mHeight - h); - currentWidth = 0; - } - else if (boost::algorithm::starts_with(tag, "FONT")) - { - parseFont(tag); - if (currentWidth != 0) { - currentHeight += currentFontHeight(); - currentWidth = 0; - } - currentWidth = 0; - } - else if (boost::algorithm::starts_with(tag, "DIV")) - { - parseDiv(tag); - if (currentWidth != 0) { - currentHeight += currentFontHeight(); - currentWidth = 0; - } - } - index = tagEnd; - } - else if (ch == NEWLINE) - { - currentHeight += currentFontHeight(); - currentWidth = 0; - currentWordStart = index; - } - else if (ch == SPACE) - { - currentWidth += 3; // keep this in sync with the font's SpaceWidth property - currentWordStart = index; - } - else - { - currentWidth += widthForCharGlyph(ch); - } - - if (currentWidth > width) - { - currentHeight += currentFontHeight(); - currentWidth = 0; - // add size of the current word - UTFString word = text.substr(currentWordStart, index - currentWordStart); - for (UTFString::const_iterator it = word.begin(), end = word.end(); it != end; ++it) - currentWidth += widthForCharGlyph(it.getCharacter()); - } - index += UTFString::_utf16_char_length(ch); - } - const size_t pageEnd = (currentHeight > height - spacing && currentWordStart != 0) - ? currentWordStart : index; - - result.push_back(text.substr(0, pageEnd).asUTF8()); - text.erase(0, pageEnd); - } - - return result; -} - -float BookTextParser::widthForCharGlyph(unsigned unicodeChar) const -{ - std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont); - return MyGUI::FontManager::getInstance().getByName(fontName) - ->getGlyphInfo(unicodeChar)->width; -} - -float BookTextParser::currentFontHeight() const -{ - std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont); - return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); -} - -MyGUI::IntSize BookTextParser::parse(std::string text, MyGUI::Widget* parent, const int width) -{ - MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor - text = Interpreter::fixDefinesBook(text, interpreterContext); - - mParent = parent; - mWidth = width; - mHeight = 0; - - assert(mParent); - while (mParent->getChildCount()) - { - MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); - } - - boost::algorithm::replace_all(text, "\n", ""); - boost::algorithm::replace_all(text, "
", "\n"); - boost::algorithm::replace_all(text, "

", "\n\n"); - - // remove leading newlines -// while (text[0] == '\n') -// text.erase(0); - - // remove trailing " - if (text[text.size()-1] == '\"') - text.erase(text.size()-1); - - parseSubText(text); - return MyGUI::IntSize(mWidth, mHeight); -} - -void BookTextParser::parseImage(std::string tag, bool createWidget) -{ - int src_start = tag.find("SRC=")+5; - std::string image = tag.substr(src_start, tag.find('"', src_start)-src_start); - - // fix texture extension to .dds - if (image.size() > 4) - { - image[image.size()-3] = 'd'; - image[image.size()-2] = 'd'; - image[image.size()-1] = 's'; - } - - int width_start = tag.find("WIDTH=")+7; - int width = boost::lexical_cast(tag.substr(width_start, tag.find('"', width_start)-width_start)); - - int height_start = tag.find("HEIGHT=")+8; - int height = boost::lexical_cast(tag.substr(height_start, tag.find('"', height_start)-height_start)); - - if (createWidget) - { - MyGUI::ImageBox* box = mParent->createWidget ("ImageBox", - MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top, - mParent->getName() + boost::lexical_cast(mParent->getChildCount())); - box->setImageTexture("bookart\\" + image); - box->setProperty("NeedMouse", "false"); - } - - mWidth = std::max(mWidth, width); - mHeight += height; -} - -void BookTextParser::parseDiv(std::string tag) -{ - if (tag.find("ALIGN=") == std::string::npos) - return; - - int align_start = tag.find("ALIGN=")+7; - std::string align = tag.substr(align_start, tag.find('"', align_start)-align_start); - if (align == "CENTER") - mTextStyle.mTextAlign = MyGUI::Align::HCenter; - else if (align == "LEFT") - mTextStyle.mTextAlign = MyGUI::Align::Left; -} - -void BookTextParser::parseFont(std::string tag) -{ - if (tag.find("COLOR=") != std::string::npos) - { - int color_start = tag.find("COLOR=")+7; - std::string color = tag.substr(color_start, tag.find('"', color_start)-color_start); - - mTextStyle.mColour = MyGUI::Colour( - convertFromHex(color.substr(0, 2))/255.0, - convertFromHex(color.substr(2, 2))/255.0, - convertFromHex(color.substr(4, 2))/255.0); - } - if (tag.find("FACE=") != std::string::npos) - { - int face_start = tag.find("FACE=")+6; - std::string face = tag.substr(face_start, tag.find('"', face_start)-face_start); - - if (face != "Magic Cards") - mTextStyle.mFont = face; - } - if (tag.find("SIZE=") != std::string::npos) - { - /// \todo - } -} - -void BookTextParser::parseSubText(std::string text) -{ - if (text[0] == '<') - { - const size_t tagStart = 1; - const size_t tagEnd = text.find('>', tagStart); - if (tagEnd == std::string::npos) - throw std::runtime_error("BookTextParser Error: Tag is not terminated"); - const std::string tag = text.substr(tagStart, tagEnd - tagStart); - - if (boost::algorithm::starts_with(tag, "IMG")) - parseImage(tag); - if (boost::algorithm::starts_with(tag, "FONT")) - parseFont(tag); - if (boost::algorithm::starts_with(tag, "DOV")) - parseDiv(tag); - - text.erase(0, tagEnd + 1); - } - - size_t tagStart = std::string::npos; - std::string realText; // real text, without tags - for (size_t i = 0; i= text.size()) + const size_t tagStart = index + 1; + const size_t tagEnd = text.find('>', tagStart); + if (tagEnd == UTFString::npos) throw std::runtime_error("BookTextParser Error: Tag is not terminated"); - ++i; - c = text[i]; + const std::string tag = text.substr(tagStart, tagEnd - tagStart).asUTF8(); + + if (boost::algorithm::starts_with(tag, "IMG")) + { + const int h = mHeight; + parseImage(tag, false); + currentHeight += (mHeight - h); + currentWidth = 0; + } + else if (boost::algorithm::starts_with(tag, "FONT")) + { + parseFont(tag); + if (currentWidth != 0) { + currentHeight += currentFontHeight(); + currentWidth = 0; + } + currentWidth = 0; + } + else if (boost::algorithm::starts_with(tag, "DIV")) + { + parseDiv(tag); + if (currentWidth != 0) { + currentHeight += currentFontHeight(); + currentWidth = 0; + } + } + index = tagEnd; + } + else if (ch == NEWLINE) + { + currentHeight += currentFontHeight(); + currentWidth = 0; + currentWordStart = index; + } + else if (ch == SPACE) + { + currentWidth += 3; // keep this in sync with the font's SpaceWidth property + currentWordStart = index; + } + else + { + currentWidth += widthForCharGlyph(ch); + } + + if (currentWidth > width) + { + currentHeight += currentFontHeight(); + currentWidth = 0; + // add size of the current word + UTFString word = text.substr(currentWordStart, index - currentWordStart); + for (UTFString::const_iterator it = word.begin(), end = word.end(); it != end; ++it) + currentWidth += widthForCharGlyph(it.getCharacter()); + } + index += UTFString::_utf16_char_length(ch); + } + const size_t pageEnd = (currentHeight > height - spacing && currentWordStart != 0) + ? currentWordStart : index; + + result.push_back(text.substr(0, pageEnd).asUTF8()); + text.erase(0, pageEnd); + } + + return result; + } + + float BookTextParser::widthForCharGlyph(unsigned unicodeChar) const + { + std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont); + return MyGUI::FontManager::getInstance().getByName(fontName) + ->getGlyphInfo(unicodeChar)->width; + } + + float BookTextParser::currentFontHeight() const + { + std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont); + return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight(); + } + + MyGUI::IntSize BookTextParser::parse(std::string text, MyGUI::Widget* parent, const int width) + { + MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor + text = Interpreter::fixDefinesBook(text, interpreterContext); + + mParent = parent; + mWidth = width; + mHeight = 0; + + assert(mParent); + while (mParent->getChildCount()) + { + MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0)); + } + + boost::algorithm::replace_all(text, "\n", ""); + boost::algorithm::replace_all(text, "
", "\n"); + boost::algorithm::replace_all(text, "

", "\n\n"); + + // remove leading newlines + // while (text[0] == '\n') + // text.erase(0); + + // remove trailing " + if (text[text.size()-1] == '\"') + text.erase(text.size()-1); + + parseSubText(text); + return MyGUI::IntSize(mWidth, mHeight); + } + + void BookTextParser::parseImage(std::string tag, bool createWidget) + { + int src_start = tag.find("SRC=")+5; + std::string image = tag.substr(src_start, tag.find('"', src_start)-src_start); + + // fix texture extension to .dds + if (image.size() > 4) + { + image[image.size()-3] = 'd'; + image[image.size()-2] = 'd'; + image[image.size()-1] = 's'; + } + + int width_start = tag.find("WIDTH=")+7; + int width = boost::lexical_cast(tag.substr(width_start, tag.find('"', width_start)-width_start)); + + int height_start = tag.find("HEIGHT=")+8; + int height = boost::lexical_cast(tag.substr(height_start, tag.find('"', height_start)-height_start)); + + if (createWidget) + { + MyGUI::ImageBox* box = mParent->createWidget ("ImageBox", + MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top, + mParent->getName() + boost::lexical_cast(mParent->getChildCount())); + box->setImageTexture("bookart\\" + image); + box->setProperty("NeedMouse", "false"); + } + + mWidth = std::max(mWidth, width); + mHeight += height; + } + + void BookTextParser::parseDiv(std::string tag) + { + if (tag.find("ALIGN=") == std::string::npos) + return; + + int align_start = tag.find("ALIGN=")+7; + std::string align = tag.substr(align_start, tag.find('"', align_start)-align_start); + if (align == "CENTER") + mTextStyle.mTextAlign = MyGUI::Align::HCenter; + else if (align == "LEFT") + mTextStyle.mTextAlign = MyGUI::Align::Left; + } + + void BookTextParser::parseFont(std::string tag) + { + if (tag.find("COLOR=") != std::string::npos) + { + int color_start = tag.find("COLOR=")+7; + std::string color = tag.substr(color_start, tag.find('"', color_start)-color_start); + + mTextStyle.mColour = MyGUI::Colour( + convertFromHex(color.substr(0, 2))/255.0, + convertFromHex(color.substr(2, 2))/255.0, + convertFromHex(color.substr(4, 2))/255.0); + } + if (tag.find("FACE=") != std::string::npos) + { + int face_start = tag.find("FACE=")+6; + std::string face = tag.substr(face_start, tag.find('"', face_start)-face_start); + + if (face != "Magic Cards") + mTextStyle.mFont = face; + } + if (tag.find("SIZE=") != std::string::npos) + { + /// \todo + } + } + + void BookTextParser::parseSubText(std::string text) + { + if (text[0] == '<') + { + const size_t tagStart = 1; + const size_t tagEnd = text.find('>', tagStart); + if (tagEnd == std::string::npos) + throw std::runtime_error("BookTextParser Error: Tag is not terminated"); + const std::string tag = text.substr(tagStart, tagEnd - tagStart); + + if (boost::algorithm::starts_with(tag, "IMG")) + parseImage(tag); + if (boost::algorithm::starts_with(tag, "FONT")) + parseFont(tag); + if (boost::algorithm::starts_with(tag, "DOV")) + parseDiv(tag); + + text.erase(0, tagEnd + 1); + } + + size_t tagStart = std::string::npos; + std::string realText; // real text, without tags + for (size_t i = 0; i= text.size()) + throw std::runtime_error("BookTextParser Error: Tag is not terminated"); + ++i; + c = text[i]; + } + continue; + } + else + { + tagStart = i; + break; } - continue; } else - { - tagStart = i; - break; - } + realText += c; + } + + MyGUI::EditBox* box = mParent->createWidget("NormalText", + MyGUI::IntCoord(0, mHeight, mWidth, 24), MyGUI::Align::Left | MyGUI::Align::Top, + mParent->getName() + boost::lexical_cast(mParent->getChildCount())); + box->setProperty("Static", "true"); + box->setProperty("MultiLine", "true"); + box->setProperty("WordWrap", "true"); + box->setProperty("NeedMouse", "false"); + box->setMaxTextLength(realText.size()); + box->setTextAlign(mTextStyle.mTextAlign); + box->setTextColour(mTextStyle.mColour); + box->setFontName(mTextStyle.mFont); + box->setCaption(realText); + box->setSize(box->getSize().width, box->getTextSize().height); + mHeight += box->getTextSize().height; + + if (tagStart != std::string::npos) + { + parseSubText(text.substr(tagStart, text.size())); } - else - realText += c; } - MyGUI::EditBox* box = mParent->createWidget("NormalText", - MyGUI::IntCoord(0, mHeight, mWidth, 24), MyGUI::Align::Left | MyGUI::Align::Top, - mParent->getName() + boost::lexical_cast(mParent->getChildCount())); - box->setProperty("Static", "true"); - box->setProperty("MultiLine", "true"); - box->setProperty("WordWrap", "true"); - box->setProperty("NeedMouse", "false"); - box->setMaxTextLength(realText.size()); - box->setTextAlign(mTextStyle.mTextAlign); - box->setTextColour(mTextStyle.mColour); - box->setFontName(mTextStyle.mFont); - box->setCaption(realText); - box->setSize(box->getSize().width, box->getTextSize().height); - mHeight += box->getTextSize().height; - - if (tagStart != std::string::npos) - { - parseSubText(text.substr(tagStart, text.size())); - } } diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 988bcfc24..59e00bf69 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -12,528 +12,531 @@ #include "console.hpp" #include "spellicons.hpp" -using namespace MWGui; - -HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) - : Layout("openmw_hud.layout") - , mHealth(NULL) - , mMagicka(NULL) - , mStamina(NULL) - , mWeapImage(NULL) - , mSpellImage(NULL) - , mWeapStatus(NULL) - , mSpellStatus(NULL) - , mEffectBox(NULL) - , mMinimap(NULL) - , mCompass(NULL) - , mCrosshair(NULL) - , mFpsBox(NULL) - , mFpsCounter(NULL) - , mTriangleCounter(NULL) - , mBatchCounter(NULL) - , mHealthManaStaminaBaseLeft(0) - , mWeapBoxBaseLeft(0) - , mSpellBoxBaseLeft(0) - , mEffectBoxBaseRight(0) - , mMinimapBoxBaseRight(0) - , mDragAndDrop(dragAndDrop) - , mCellNameTimer(0.0f) - , mCellNameBox(NULL) - , mMapVisible(true) - , mWeaponVisible(true) - , mSpellVisible(true) - , mWorldMouseOver(false) +namespace MWGui { - setCoord(0,0, width, height); - // Energy bars - getWidget(mHealthFrame, "HealthFrame"); - getWidget(mHealth, "Health"); - getWidget(mMagicka, "Magicka"); - getWidget(mStamina, "Stamina"); - mHealthManaStaminaBaseLeft = mHealthFrame->getLeft(); - - MyGUI::Widget *healthFrame, *magickaFrame, *fatigueFrame; - getWidget(healthFrame, "HealthFrame"); - getWidget(magickaFrame, "MagickaFrame"); - getWidget(fatigueFrame, "FatigueFrame"); - healthFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); - magickaFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); - fatigueFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); - - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - - // Item and spell images and status bars - getWidget(mWeapBox, "WeapBox"); - getWidget(mWeapImage, "WeapImage"); - getWidget(mWeapStatus, "WeapStatus"); - mWeapBoxBaseLeft = mWeapBox->getLeft(); - mWeapBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWeaponClicked); - - getWidget(mSpellBox, "SpellBox"); - getWidget(mSpellImage, "SpellImage"); - getWidget(mSpellStatus, "SpellStatus"); - mSpellBoxBaseLeft = mSpellBox->getLeft(); - mSpellBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMagicClicked); - - getWidget(mEffectBox, "EffectBox"); - mEffectBoxBaseRight = viewSize.width - mEffectBox->getRight(); - - getWidget(mMinimapBox, "MiniMapBox"); - mMinimapBoxBaseRight = viewSize.width - mMinimapBox->getRight(); - getWidget(mMinimap, "MiniMap"); - getWidget(mCompass, "Compass"); - getWidget(mMinimapButton, "MiniMapButton"); - mMinimapButton->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); - - getWidget(mCellNameBox, "CellName"); - getWidget(mWeaponSpellBox, "WeaponSpellName"); - - getWidget(mCrosshair, "Crosshair"); - - setFpsLevel(fpsLevel); - - getWidget(mTriangleCounter, "TriangleCounter"); - getWidget(mBatchCounter, "BatchCounter"); - - LocalMapBase::init(mMinimap, mCompass, this); - - mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked); - mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver); - mMainWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &HUD::onWorldMouseLostFocus); - - mSpellIcons = new SpellIcons(); -} - -HUD::~HUD() -{ - delete mSpellIcons; -} - -void HUD::setFpsLevel(int level) -{ - mFpsCounter = 0; - - MyGUI::Widget* fps; - getWidget(fps, "FPSBoxAdv"); - fps->setVisible(false); - getWidget(fps, "FPSBox"); - fps->setVisible(false); - - if (level == 2) + HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) + : Layout("openmw_hud.layout") + , mHealth(NULL) + , mMagicka(NULL) + , mStamina(NULL) + , mWeapImage(NULL) + , mSpellImage(NULL) + , mWeapStatus(NULL) + , mSpellStatus(NULL) + , mEffectBox(NULL) + , mMinimap(NULL) + , mCompass(NULL) + , mCrosshair(NULL) + , mFpsBox(NULL) + , mFpsCounter(NULL) + , mTriangleCounter(NULL) + , mBatchCounter(NULL) + , mHealthManaStaminaBaseLeft(0) + , mWeapBoxBaseLeft(0) + , mSpellBoxBaseLeft(0) + , mEffectBoxBaseRight(0) + , mMinimapBoxBaseRight(0) + , mDragAndDrop(dragAndDrop) + , mCellNameTimer(0.0f) + , mCellNameBox(NULL) + , mMapVisible(true) + , mWeaponVisible(true) + , mSpellVisible(true) + , mWorldMouseOver(false) { - getWidget(mFpsBox, "FPSBoxAdv"); - mFpsBox->setVisible(true); - getWidget(mFpsCounter, "FPSCounterAdv"); + setCoord(0,0, width, height); + + // Energy bars + getWidget(mHealthFrame, "HealthFrame"); + getWidget(mHealth, "Health"); + getWidget(mMagicka, "Magicka"); + getWidget(mStamina, "Stamina"); + mHealthManaStaminaBaseLeft = mHealthFrame->getLeft(); + + MyGUI::Widget *healthFrame, *magickaFrame, *fatigueFrame; + getWidget(healthFrame, "HealthFrame"); + getWidget(magickaFrame, "MagickaFrame"); + getWidget(fatigueFrame, "FatigueFrame"); + healthFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); + magickaFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); + fatigueFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); + + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + + // Item and spell images and status bars + getWidget(mWeapBox, "WeapBox"); + getWidget(mWeapImage, "WeapImage"); + getWidget(mWeapStatus, "WeapStatus"); + mWeapBoxBaseLeft = mWeapBox->getLeft(); + mWeapBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWeaponClicked); + + getWidget(mSpellBox, "SpellBox"); + getWidget(mSpellImage, "SpellImage"); + getWidget(mSpellStatus, "SpellStatus"); + mSpellBoxBaseLeft = mSpellBox->getLeft(); + mSpellBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMagicClicked); + + getWidget(mEffectBox, "EffectBox"); + mEffectBoxBaseRight = viewSize.width - mEffectBox->getRight(); + + getWidget(mMinimapBox, "MiniMapBox"); + mMinimapBoxBaseRight = viewSize.width - mMinimapBox->getRight(); + getWidget(mMinimap, "MiniMap"); + getWidget(mCompass, "Compass"); + getWidget(mMinimapButton, "MiniMapButton"); + mMinimapButton->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); + + getWidget(mCellNameBox, "CellName"); + getWidget(mWeaponSpellBox, "WeaponSpellName"); + + getWidget(mCrosshair, "Crosshair"); + + setFpsLevel(fpsLevel); + + getWidget(mTriangleCounter, "TriangleCounter"); + getWidget(mBatchCounter, "BatchCounter"); + + LocalMapBase::init(mMinimap, mCompass, this); + + mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked); + mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver); + mMainWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &HUD::onWorldMouseLostFocus); + + mSpellIcons = new SpellIcons(); } - else if (level == 1) + + HUD::~HUD() { - getWidget(mFpsBox, "FPSBox"); - mFpsBox->setVisible(true); - getWidget(mFpsCounter, "FPSCounter"); + delete mSpellIcons; } -} -void HUD::setFPS(float fps) -{ - if (mFpsCounter) - mFpsCounter->setCaption(boost::lexical_cast((int)fps)); -} - -void HUD::setTriangleCount(unsigned int count) -{ - mTriangleCounter->setCaption(boost::lexical_cast(count)); -} - -void HUD::setBatchCount(unsigned int count) -{ - mBatchCounter->setCaption(boost::lexical_cast(count)); -} - -void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& value) -{ - static const char *ids[] = + void HUD::setFpsLevel(int level) { - "HBar", "MBar", "FBar", 0 - }; + mFpsCounter = 0; - for (int i=0; ids[i]; ++i) - if (ids[i]==id) + MyGUI::Widget* fps; + getWidget(fps, "FPSBoxAdv"); + fps->setVisible(false); + getWidget(fps, "FPSBox"); + fps->setVisible(false); + + if (level == 2) { - MyGUI::Widget* w; - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - switch (i) - { - case 0: - mHealth->setProgressRange (value.getModified()); - mHealth->setProgressPosition (value.getCurrent()); - getWidget(w, "HealthFrame"); - w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); - break; - case 1: - mMagicka->setProgressRange (value.getModified()); - mMagicka->setProgressPosition (value.getCurrent()); - getWidget(w, "MagickaFrame"); - w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); - break; - case 2: - mStamina->setProgressRange (value.getModified()); - mStamina->setProgressPosition (value.getCurrent()); - getWidget(w, "FatigueFrame"); - w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); - break; - } + getWidget(mFpsBox, "FPSBoxAdv"); + mFpsBox->setVisible(true); + getWidget(mFpsCounter, "FPSCounterAdv"); + } + else if (level == 1) + { + getWidget(mFpsBox, "FPSBox"); + mFpsBox->setVisible(true); + getWidget(mFpsCounter, "FPSCounter"); } -} - -void HUD::onWorldClicked(MyGUI::Widget* _sender) -{ - if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) - return; - - if (mDragAndDrop->mIsOnDragAndDrop) - { - // drop item into the gameworld - MWWorld::Ptr object = *mDragAndDrop->mDraggedWidget->getUserData(); - - MWBase::World* world = MWBase::Environment::get().getWorld(); - - MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); - float mouseX = cursorPosition.left / float(viewSize.width); - float mouseY = cursorPosition.top / float(viewSize.height); - - int origCount = object.getRefData().getCount(); - object.getRefData().setCount(mDragAndDrop->mDraggedCount); - - if (world->canPlaceObject(mouseX, mouseY)) - world->placeObject(object, mouseX, mouseY); - else - world->dropObjectOnGround(world->getPlayer().getPlayer(), object); - - MWBase::Environment::get().getWindowManager()->changePointer("arrow"); - - std::string sound = MWWorld::Class::get(object).getDownSoundId(object); - MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - - // remove object from the container it was coming from - object.getRefData().setCount(origCount - mDragAndDrop->mDraggedCount); - - mDragAndDrop->mIsOnDragAndDrop = false; - MyGUI::Gui::getInstance().destroyWidget(mDragAndDrop->mDraggedWidget); - mDragAndDrop->mDraggedWidget = 0; - - MWBase::Environment::get().getWindowManager()->setDragDrop(false); - mDragAndDrop->mDraggedFrom->drawItems(); - mDragAndDrop->mDraggedFrom->notifyItemDragged(object, -mDragAndDrop->mDraggedCount); } - else - { - GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); - if ( (mode != GM_Console) && (mode != GM_Container) && (mode != GM_Inventory) ) + void HUD::setFPS(float fps) + { + if (mFpsCounter) + mFpsCounter->setCaption(boost::lexical_cast((int)fps)); + } + + void HUD::setTriangleCount(unsigned int count) + { + mTriangleCounter->setCaption(boost::lexical_cast(count)); + } + + void HUD::setBatchCount(unsigned int count) + { + mBatchCounter->setCaption(boost::lexical_cast(count)); + } + + void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& value) + { + static const char *ids[] = + { + "HBar", "MBar", "FBar", 0 + }; + + for (int i=0; ids[i]; ++i) + if (ids[i]==id) + { + MyGUI::Widget* w; + std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + switch (i) + { + case 0: + mHealth->setProgressRange (value.getModified()); + mHealth->setProgressPosition (value.getCurrent()); + getWidget(w, "HealthFrame"); + w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); + break; + case 1: + mMagicka->setProgressRange (value.getModified()); + mMagicka->setProgressPosition (value.getCurrent()); + getWidget(w, "MagickaFrame"); + w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); + break; + case 2: + mStamina->setProgressRange (value.getModified()); + mStamina->setProgressPosition (value.getCurrent()); + getWidget(w, "FatigueFrame"); + w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); + break; + } + } + } + + void HUD::onWorldClicked(MyGUI::Widget* _sender) + { + if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) return; - MWWorld::Ptr object = MWBase::Environment::get().getWorld()->getFacedObject(); - - if (mode == GM_Console) - MWBase::Environment::get().getWindowManager()->getConsole()->setSelectedObject(object); - else if ((mode == GM_Container) || (mode == GM_Inventory)) + if (mDragAndDrop->mIsOnDragAndDrop) { - // pick up object - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object); - } - } -} + // drop item into the gameworld + MWWorld::Ptr object = *mDragAndDrop->mDraggedWidget->getUserData(); -void HUD::onWorldMouseOver(MyGUI::Widget* _sender, int x, int y) -{ - if (mDragAndDrop->mIsOnDragAndDrop) - { - mWorldMouseOver = false; + MWBase::World* world = MWBase::Environment::get().getWorld(); - MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); - float mouseX = cursorPosition.left / float(viewSize.width); - float mouseY = cursorPosition.top / float(viewSize.height); + MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); + float mouseX = cursorPosition.left / float(viewSize.width); + float mouseY = cursorPosition.top / float(viewSize.height); - MWBase::World* world = MWBase::Environment::get().getWorld(); + int origCount = object.getRefData().getCount(); + object.getRefData().setCount(mDragAndDrop->mDraggedCount); - // if we can't drop the object at the wanted position, show the "drop on ground" cursor. - bool canDrop = world->canPlaceObject(mouseX, mouseY); + if (world->canPlaceObject(mouseX, mouseY)) + world->placeObject(object, mouseX, mouseY); + else + world->dropObjectOnGround(world->getPlayer().getPlayer(), object); - if (!canDrop) - MWBase::Environment::get().getWindowManager()->changePointer("drop_ground"); - else MWBase::Environment::get().getWindowManager()->changePointer("arrow"); + std::string sound = MWWorld::Class::get(object).getDownSoundId(object); + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); + + // remove object from the container it was coming from + object.getRefData().setCount(origCount - mDragAndDrop->mDraggedCount); + + mDragAndDrop->mIsOnDragAndDrop = false; + MyGUI::Gui::getInstance().destroyWidget(mDragAndDrop->mDraggedWidget); + mDragAndDrop->mDraggedWidget = 0; + + MWBase::Environment::get().getWindowManager()->setDragDrop(false); + mDragAndDrop->mDraggedFrom->drawItems(); + mDragAndDrop->mDraggedFrom->notifyItemDragged(object, -mDragAndDrop->mDraggedCount); + } + else + { + GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); + + if ( (mode != GM_Console) && (mode != GM_Container) && (mode != GM_Inventory) ) + return; + + MWWorld::Ptr object = MWBase::Environment::get().getWorld()->getFacedObject(); + + if (mode == GM_Console) + MWBase::Environment::get().getWindowManager()->getConsole()->setSelectedObject(object); + else if ((mode == GM_Container) || (mode == GM_Inventory)) + { + // pick up object + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object); + } + } } - else + + void HUD::onWorldMouseOver(MyGUI::Widget* _sender, int x, int y) + { + if (mDragAndDrop->mIsOnDragAndDrop) + { + mWorldMouseOver = false; + + MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); + float mouseX = cursorPosition.left / float(viewSize.width); + float mouseY = cursorPosition.top / float(viewSize.height); + + MWBase::World* world = MWBase::Environment::get().getWorld(); + + // if we can't drop the object at the wanted position, show the "drop on ground" cursor. + bool canDrop = world->canPlaceObject(mouseX, mouseY); + + if (!canDrop) + MWBase::Environment::get().getWindowManager()->changePointer("drop_ground"); + else + MWBase::Environment::get().getWindowManager()->changePointer("arrow"); + + } + else + { + MWBase::Environment::get().getWindowManager()->changePointer("arrow"); + mWorldMouseOver = true; + } + } + + void HUD::onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new) { MWBase::Environment::get().getWindowManager()->changePointer("arrow"); - mWorldMouseOver = true; - } -} - -void HUD::onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new) -{ - MWBase::Environment::get().getWindowManager()->changePointer("arrow"); - mWorldMouseOver = false; -} - -void HUD::onHMSClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats); -} - -void HUD::onMapClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map); -} - -void HUD::onWeaponClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory); -} - -void HUD::onMagicClicked(MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic); -} - -void HUD::setCellName(const std::string& cellName) -{ - if (mCellName != cellName) - { - mCellNameTimer = 5.0f; - mCellName = cellName; - - mCellNameBox->setCaptionWithReplacing("#{sCell=" + mCellName + "}"); - mCellNameBox->setVisible(mMapVisible); - } -} - -void HUD::onFrame(float dt) -{ - mCellNameTimer -= dt; - mWeaponSpellTimer -= dt; - if (mCellNameTimer < 0) - mCellNameBox->setVisible(false); - if (mWeaponSpellTimer < 0) - mWeaponSpellBox->setVisible(false); -} - -void HUD::onResChange(int width, int height) -{ - setCoord(0, 0, width, height); -} - -void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent) -{ - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(spellId); - - std::string spellName = spell->mName; - if (spellName != mSpellName && mSpellVisible) - { - mWeaponSpellTimer = 5.0f; - mSpellName = spellName; - mWeaponSpellBox->setCaption(mSpellName); - mWeaponSpellBox->setVisible(true); + mWorldMouseOver = false; } - mSpellStatus->setProgressRange(100); - mSpellStatus->setProgressPosition(successChancePercent); - - if (mSpellImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); - - mSpellBox->setUserString("ToolTipType", "Spell"); - mSpellBox->setUserString("Spell", spellId); - - // use the icon of the first effect - const ESM::MagicEffect* effect = - MWBase::Environment::get().getWorld()->getStore().get().find(spell->mEffects.mList.front().mEffectID); - - std::string icon = effect->mIcon; - int slashPos = icon.find("\\"); - icon.insert(slashPos+1, "b_"); - icon = std::string("icons\\") + icon; - Widgets::fixTexturePath(icon); - mSpellImage->setImageTexture(icon); -} - -void HUD::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) -{ - std::string itemName = MWWorld::Class::get(item).getName(item); - if (itemName != mSpellName && mSpellVisible) + void HUD::onHMSClicked(MyGUI::Widget* _sender) { - mWeaponSpellTimer = 5.0f; - mSpellName = itemName; - mWeaponSpellBox->setCaption(mSpellName); - mWeaponSpellBox->setVisible(true); + MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Stats); } - mSpellStatus->setProgressRange(100); - mSpellStatus->setProgressPosition(chargePercent); - - if (mSpellImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); - - mSpellBox->setUserString("ToolTipType", "ItemPtr"); - mSpellBox->setUserData(item); - - mSpellImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); - MyGUI::ImageBox* itemBox = mSpellImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) - , MyGUI::Align::Stretch); - - std::string path = std::string("icons\\"); - path+=MWWorld::Class::get(item).getInventoryIcon(item); - Widgets::fixTexturePath(path); - itemBox->setImageTexture(path); - itemBox->setNeedMouseFocus(false); -} - -void HUD::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) -{ - std::string itemName = MWWorld::Class::get(item).getName(item); - if (itemName != mWeaponName && mWeaponVisible) + void HUD::onMapClicked(MyGUI::Widget* _sender) { - mWeaponSpellTimer = 5.0f; - mWeaponName = itemName; - mWeaponSpellBox->setCaption(mWeaponName); - mWeaponSpellBox->setVisible(true); + MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Map); } - mWeapBox->setUserString("ToolTipType", "ItemPtr"); - mWeapBox->setUserData(item); - - mWeapStatus->setProgressRange(100); - mWeapStatus->setProgressPosition(durabilityPercent); - - if (mWeapImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); - - std::string path = std::string("icons\\"); - path+=MWWorld::Class::get(item).getInventoryIcon(item); - Widgets::fixTexturePath(path); - - if (MWWorld::Class::get(item).getEnchantment(item) != "") + void HUD::onWeaponClicked(MyGUI::Widget* _sender) { - mWeapImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); - MyGUI::ImageBox* itemBox = mWeapImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) + MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory); + } + + void HUD::onMagicClicked(MyGUI::Widget* _sender) + { + MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic); + } + + void HUD::setCellName(const std::string& cellName) + { + if (mCellName != cellName) + { + mCellNameTimer = 5.0f; + mCellName = cellName; + + mCellNameBox->setCaptionWithReplacing("#{sCell=" + mCellName + "}"); + mCellNameBox->setVisible(mMapVisible); + } + } + + void HUD::onFrame(float dt) + { + mCellNameTimer -= dt; + mWeaponSpellTimer -= dt; + if (mCellNameTimer < 0) + mCellNameBox->setVisible(false); + if (mWeaponSpellTimer < 0) + mWeaponSpellBox->setVisible(false); + } + + void HUD::onResChange(int width, int height) + { + setCoord(0, 0, width, height); + } + + void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent) + { + const ESM::Spell* spell = + MWBase::Environment::get().getWorld()->getStore().get().find(spellId); + + std::string spellName = spell->mName; + if (spellName != mSpellName && mSpellVisible) + { + mWeaponSpellTimer = 5.0f; + mSpellName = spellName; + mWeaponSpellBox->setCaption(mSpellName); + mWeaponSpellBox->setVisible(true); + } + + mSpellStatus->setProgressRange(100); + mSpellStatus->setProgressPosition(successChancePercent); + + if (mSpellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); + + mSpellBox->setUserString("ToolTipType", "Spell"); + mSpellBox->setUserString("Spell", spellId); + + // use the icon of the first effect + const ESM::MagicEffect* effect = + MWBase::Environment::get().getWorld()->getStore().get().find(spell->mEffects.mList.front().mEffectID); + + std::string icon = effect->mIcon; + int slashPos = icon.find("\\"); + icon.insert(slashPos+1, "b_"); + icon = std::string("icons\\") + icon; + Widgets::fixTexturePath(icon); + mSpellImage->setImageTexture(icon); + } + + void HUD::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) + { + std::string itemName = MWWorld::Class::get(item).getName(item); + if (itemName != mSpellName && mSpellVisible) + { + mWeaponSpellTimer = 5.0f; + mSpellName = itemName; + mWeaponSpellBox->setCaption(mSpellName); + mWeaponSpellBox->setVisible(true); + } + + mSpellStatus->setProgressRange(100); + mSpellStatus->setProgressPosition(chargePercent); + + if (mSpellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); + + mSpellBox->setUserString("ToolTipType", "ItemPtr"); + mSpellBox->setUserData(item); + + mSpellImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); + MyGUI::ImageBox* itemBox = mSpellImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) , MyGUI::Align::Stretch); + + std::string path = std::string("icons\\"); + path+=MWWorld::Class::get(item).getInventoryIcon(item); + Widgets::fixTexturePath(path); itemBox->setImageTexture(path); itemBox->setNeedMouseFocus(false); } - else - mWeapImage->setImageTexture(path); -} -void HUD::unsetSelectedSpell() -{ - std::string spellName = "#{sNone}"; - if (spellName != mSpellName && mSpellVisible) + void HUD::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) { - mWeaponSpellTimer = 5.0f; - mSpellName = spellName; - mWeaponSpellBox->setCaptionWithReplacing(mSpellName); - mWeaponSpellBox->setVisible(true); + std::string itemName = MWWorld::Class::get(item).getName(item); + if (itemName != mWeaponName && mWeaponVisible) + { + mWeaponSpellTimer = 5.0f; + mWeaponName = itemName; + mWeaponSpellBox->setCaption(mWeaponName); + mWeaponSpellBox->setVisible(true); + } + + mWeapBox->setUserString("ToolTipType", "ItemPtr"); + mWeapBox->setUserData(item); + + mWeapStatus->setProgressRange(100); + mWeapStatus->setProgressPosition(durabilityPercent); + + if (mWeapImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); + + std::string path = std::string("icons\\"); + path+=MWWorld::Class::get(item).getInventoryIcon(item); + Widgets::fixTexturePath(path); + + if (MWWorld::Class::get(item).getEnchantment(item) != "") + { + mWeapImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); + MyGUI::ImageBox* itemBox = mWeapImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) + , MyGUI::Align::Stretch); + itemBox->setImageTexture(path); + itemBox->setNeedMouseFocus(false); + } + else + mWeapImage->setImageTexture(path); } - if (mSpellImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); - mSpellStatus->setProgressRange(100); - mSpellStatus->setProgressPosition(0); - mSpellImage->setImageTexture(""); - mSpellBox->clearUserStrings(); -} - -void HUD::unsetSelectedWeapon() -{ - std::string itemName = "#{sSkillHandtohand}"; - if (itemName != mWeaponName && mWeaponVisible) + void HUD::unsetSelectedSpell() { - mWeaponSpellTimer = 5.0f; - mWeaponName = itemName; - mWeaponSpellBox->setCaptionWithReplacing(mWeaponName); - mWeaponSpellBox->setVisible(true); + std::string spellName = "#{sNone}"; + if (spellName != mSpellName && mSpellVisible) + { + mWeaponSpellTimer = 5.0f; + mSpellName = spellName; + mWeaponSpellBox->setCaptionWithReplacing(mSpellName); + mWeaponSpellBox->setVisible(true); + } + + if (mSpellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); + mSpellStatus->setProgressRange(100); + mSpellStatus->setProgressPosition(0); + mSpellImage->setImageTexture(""); + mSpellBox->clearUserStrings(); + } + + void HUD::unsetSelectedWeapon() + { + std::string itemName = "#{sSkillHandtohand}"; + if (itemName != mWeaponName && mWeaponVisible) + { + mWeaponSpellTimer = 5.0f; + mWeaponName = itemName; + mWeaponSpellBox->setCaptionWithReplacing(mWeaponName); + mWeaponSpellBox->setVisible(true); + } + + if (mWeapImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); + mWeapStatus->setProgressRange(100); + mWeapStatus->setProgressPosition(0); + mWeapImage->setImageTexture("icons\\k\\stealth_handtohand.dds"); + mWeapBox->clearUserStrings(); + } + + void HUD::setCrosshairVisible(bool visible) + { + mCrosshair->setVisible (visible); + } + + void HUD::setHmsVisible(bool visible) + { + mHealth->setVisible(visible); + mMagicka->setVisible(visible); + mStamina->setVisible(visible); + updatePositions(); + } + + void HUD::setWeapVisible(bool visible) + { + mWeapBox->setVisible(visible); + updatePositions(); + } + + void HUD::setSpellVisible(bool visible) + { + mSpellBox->setVisible(visible); + updatePositions(); + } + + void HUD::setEffectVisible(bool visible) + { + mEffectBox->setVisible (visible); + updatePositions(); + } + + void HUD::setMinimapVisible(bool visible) + { + mMinimapBox->setVisible (visible); + updatePositions(); + } + + void HUD::updatePositions() + { + int weapDx = 0, spellDx = 0; + if (!mHealth->getVisible()) + spellDx = weapDx = mWeapBoxBaseLeft - mHealthManaStaminaBaseLeft; + + if (!mWeapBox->getVisible()) + spellDx += mSpellBoxBaseLeft - mWeapBoxBaseLeft; + + mWeaponVisible = mWeapBox->getVisible(); + mSpellVisible = mSpellBox->getVisible(); + if (!mWeaponVisible && !mSpellVisible) + mWeaponSpellBox->setVisible(false); + + mWeapBox->setPosition(mWeapBoxBaseLeft - weapDx, mWeapBox->getTop()); + mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop()); + + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + + // effect box can have variable width -> variable left coordinate + int effectsDx = 0; + if (!mMinimapBox->getVisible ()) + effectsDx = (viewSize.width - mMinimapBoxBaseRight) - (viewSize.width - mEffectBoxBaseRight); + + mMapVisible = mMinimapBox->getVisible (); + mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); + } + + void HUD::update() + { + mSpellIcons->updateWidgets(mEffectBox, true); } - if (mWeapImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); - mWeapStatus->setProgressRange(100); - mWeapStatus->setProgressPosition(0); - mWeapImage->setImageTexture("icons\\k\\stealth_handtohand.dds"); - mWeapBox->clearUserStrings(); -} - -void HUD::setCrosshairVisible(bool visible) -{ - mCrosshair->setVisible (visible); -} - -void HUD::setHmsVisible(bool visible) -{ - mHealth->setVisible(visible); - mMagicka->setVisible(visible); - mStamina->setVisible(visible); - updatePositions(); -} - -void HUD::setWeapVisible(bool visible) -{ - mWeapBox->setVisible(visible); - updatePositions(); -} - -void HUD::setSpellVisible(bool visible) -{ - mSpellBox->setVisible(visible); - updatePositions(); -} - -void HUD::setEffectVisible(bool visible) -{ - mEffectBox->setVisible (visible); - updatePositions(); -} - -void HUD::setMinimapVisible(bool visible) -{ - mMinimapBox->setVisible (visible); - updatePositions(); -} - -void HUD::updatePositions() -{ - int weapDx = 0, spellDx = 0; - if (!mHealth->getVisible()) - spellDx = weapDx = mWeapBoxBaseLeft - mHealthManaStaminaBaseLeft; - - if (!mWeapBox->getVisible()) - spellDx += mSpellBoxBaseLeft - mWeapBoxBaseLeft; - - mWeaponVisible = mWeapBox->getVisible(); - mSpellVisible = mSpellBox->getVisible(); - if (!mWeaponVisible && !mSpellVisible) - mWeaponSpellBox->setVisible(false); - - mWeapBox->setPosition(mWeapBoxBaseLeft - weapDx, mWeapBox->getTop()); - mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop()); - - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - - // effect box can have variable width -> variable left coordinate - int effectsDx = 0; - if (!mMinimapBox->getVisible ()) - effectsDx = (viewSize.width - mMinimapBoxBaseRight) - (viewSize.width - mEffectBoxBaseRight); - - mMapVisible = mMinimapBox->getVisible (); - mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); -} - -void HUD::update() -{ - mSpellIcons->updateWidgets(mEffectBox, true); } diff --git a/apps/openmw/mwgui/list.cpp b/apps/openmw/mwgui/list.cpp index c7b720730..8dda041ca 100644 --- a/apps/openmw/mwgui/list.cpp +++ b/apps/openmw/mwgui/list.cpp @@ -5,159 +5,165 @@ #include #include -using namespace MWGui; -using namespace MWGui::Widgets; - -MWList::MWList() : - mClient(0) - , mScrollView(0) - , mItemHeight(0) +namespace MWGui { -} -void MWList::initialiseOverride() -{ - Base::initialiseOverride(); - - assignWidget(mClient, "Client"); - if (mClient == 0) - mClient = this; - - mScrollView = mClient->createWidgetReal( - "MW_ScrollView", MyGUI::FloatCoord(0.0, 0.0, 1.0, 1.0), - MyGUI::Align::Top | MyGUI::Align::Left | MyGUI::Align::Stretch, getName() + "_ScrollView"); -} - -void MWList::addItem(const std::string& name) -{ - mItems.push_back(name); -} - -void MWList::addSeparator() -{ - mItems.push_back(""); -} - -void MWList::adjustSize() -{ - redraw(); -} - -void MWList::redraw(bool scrollbarShown) -{ - const int _scrollBarWidth = 24; // fetch this from skin? - const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0; - const int spacing = 3; - size_t scrollbarPosition = mScrollView->getScrollPosition(); - - while (mScrollView->getChildCount()) + namespace Widgets { - MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0)); - } - mItemHeight = 0; - int i=0; - for (std::vector::const_iterator it=mItems.begin(); - it!=mItems.end(); ++it) - { - if (*it != "") + MWList::MWList() : + mClient(0) + , mScrollView(0) + , mItemHeight(0) { - MyGUI::Button* button = mScrollView->createWidget( - "MW_ListLine", MyGUI::IntCoord(0, mItemHeight, mScrollView->getSize().width - scrollBarWidth - 2, 24), - MyGUI::Align::Left | MyGUI::Align::Top, getName() + "_item_" + (*it)); - button->setCaption((*it)); - button->getSubWidgetText()->setWordWrap(true); - button->getSubWidgetText()->setTextAlign(MyGUI::Align::Left); - button->eventMouseWheel += MyGUI::newDelegate(this, &MWList::onMouseWheel); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWList::onItemSelected); - - int height = button->getTextSize().height; - button->setSize(MyGUI::IntSize(button->getSize().width, height)); - button->setUserData(i); - - mItemHeight += height + spacing; } - else + + void MWList::initialiseOverride() { - MyGUI::ImageBox* separator = mScrollView->createWidget("MW_HLine", - MyGUI::IntCoord(2, mItemHeight, mScrollView->getWidth()-4, 18), - MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); - separator->setNeedMouseFocus(false); + Base::initialiseOverride(); - mItemHeight += 18 + spacing; + assignWidget(mClient, "Client"); + if (mClient == 0) + mClient = this; + + mScrollView = mClient->createWidgetReal( + "MW_ScrollView", MyGUI::FloatCoord(0.0, 0.0, 1.0, 1.0), + MyGUI::Align::Top | MyGUI::Align::Left | MyGUI::Align::Stretch, getName() + "_ScrollView"); } - ++i; + + void MWList::addItem(const std::string& name) + { + mItems.push_back(name); + } + + void MWList::addSeparator() + { + mItems.push_back(""); + } + + void MWList::adjustSize() + { + redraw(); + } + + void MWList::redraw(bool scrollbarShown) + { + const int _scrollBarWidth = 24; // fetch this from skin? + const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0; + const int spacing = 3; + size_t scrollbarPosition = mScrollView->getScrollPosition(); + + while (mScrollView->getChildCount()) + { + MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0)); + } + + mItemHeight = 0; + int i=0; + for (std::vector::const_iterator it=mItems.begin(); + it!=mItems.end(); ++it) + { + if (*it != "") + { + MyGUI::Button* button = mScrollView->createWidget( + "MW_ListLine", MyGUI::IntCoord(0, mItemHeight, mScrollView->getSize().width - scrollBarWidth - 2, 24), + MyGUI::Align::Left | MyGUI::Align::Top, getName() + "_item_" + (*it)); + button->setCaption((*it)); + button->getSubWidgetText()->setWordWrap(true); + button->getSubWidgetText()->setTextAlign(MyGUI::Align::Left); + button->eventMouseWheel += MyGUI::newDelegate(this, &MWList::onMouseWheel); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWList::onItemSelected); + + int height = button->getTextSize().height; + button->setSize(MyGUI::IntSize(button->getSize().width, height)); + button->setUserData(i); + + mItemHeight += height + spacing; + } + else + { + MyGUI::ImageBox* separator = mScrollView->createWidget("MW_HLine", + MyGUI::IntCoord(2, mItemHeight, mScrollView->getWidth()-4, 18), + MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); + separator->setNeedMouseFocus(false); + + mItemHeight += 18 + spacing; + } + ++i; + } + mScrollView->setCanvasSize(mClient->getSize().width + (_scrollBarWidth-scrollBarWidth), std::max(mItemHeight, mClient->getSize().height)); + + if (!scrollbarShown && mItemHeight > mClient->getSize().height) + redraw(true); + + size_t scrollbarRange = mScrollView->getScrollRange(); + if(scrollbarPosition > scrollbarRange) + scrollbarPosition = scrollbarRange; + mScrollView->setScrollPosition(scrollbarPosition); + } + + bool MWList::hasItem(const std::string& name) + { + return (std::find(mItems.begin(), mItems.end(), name) != mItems.end()); + } + + unsigned int MWList::getItemCount() + { + return mItems.size(); + } + + std::string MWList::getItemNameAt(unsigned int at) + { + assert(at < mItems.size() && "List item out of bounds"); + return mItems[at]; + } + + void MWList::removeItem(const std::string& name) + { + assert( std::find(mItems.begin(), mItems.end(), name) != mItems.end() ); + mItems.erase( std::find(mItems.begin(), mItems.end(), name) ); + } + + void MWList::clear() + { + mItems.clear(); + } + + void MWList::onMouseWheel(MyGUI::Widget* _sender, int _rel) + { + //NB view offset is negative + if (mScrollView->getViewOffset().top + _rel*0.3 > 0) + mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mScrollView->setViewOffset(MyGUI::IntPoint(0, mScrollView->getViewOffset().top + _rel*0.3)); + } + + void MWList::onItemSelected(MyGUI::Widget* _sender) + { + std::string name = static_cast(_sender)->getCaption(); + int id = *_sender->getUserData(); + eventItemSelected(name, id); + eventWidgetSelected(_sender); + } + + MyGUI::Widget* MWList::getItemWidget(const std::string& name) + { + return mScrollView->findWidget (getName() + "_item_" + name); + } + + size_t MWScrollView::getScrollPosition() + { + return getVScroll()->getScrollPosition(); + } + + void MWScrollView::setScrollPosition(size_t position) + { + getVScroll()->setScrollPosition(position); + } + size_t MWScrollView::getScrollRange() + { + return getVScroll()->getScrollRange(); + } + } - mScrollView->setCanvasSize(mClient->getSize().width + (_scrollBarWidth-scrollBarWidth), std::max(mItemHeight, mClient->getSize().height)); - - if (!scrollbarShown && mItemHeight > mClient->getSize().height) - redraw(true); - - size_t scrollbarRange = mScrollView->getScrollRange(); - if(scrollbarPosition > scrollbarRange) - scrollbarPosition = scrollbarRange; - mScrollView->setScrollPosition(scrollbarPosition); -} - -bool MWList::hasItem(const std::string& name) -{ - return (std::find(mItems.begin(), mItems.end(), name) != mItems.end()); -} - -unsigned int MWList::getItemCount() -{ - return mItems.size(); -} - -std::string MWList::getItemNameAt(unsigned int at) -{ - assert(at < mItems.size() && "List item out of bounds"); - return mItems[at]; -} - -void MWList::removeItem(const std::string& name) -{ - assert( std::find(mItems.begin(), mItems.end(), name) != mItems.end() ); - mItems.erase( std::find(mItems.begin(), mItems.end(), name) ); -} - -void MWList::clear() -{ - mItems.clear(); -} - -void MWList::onMouseWheel(MyGUI::Widget* _sender, int _rel) -{ - //NB view offset is negative - if (mScrollView->getViewOffset().top + _rel*0.3 > 0) - mScrollView->setViewOffset(MyGUI::IntPoint(0, 0)); - else - mScrollView->setViewOffset(MyGUI::IntPoint(0, mScrollView->getViewOffset().top + _rel*0.3)); -} - -void MWList::onItemSelected(MyGUI::Widget* _sender) -{ - std::string name = static_cast(_sender)->getCaption(); - int id = *_sender->getUserData(); - eventItemSelected(name, id); - eventWidgetSelected(_sender); -} - -MyGUI::Widget* MWList::getItemWidget(const std::string& name) -{ - return mScrollView->findWidget (getName() + "_item_" + name); -} - -size_t MWScrollView::getScrollPosition() -{ - return getVScroll()->getScrollPosition(); -} - -void MWScrollView::setScrollPosition(size_t position) -{ - getVScroll()->setScrollPosition(position); -} -size_t MWScrollView::getScrollRange() -{ - return getVScroll()->getScrollRange(); } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 1ac1c2448..4a78238ce 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -13,430 +13,433 @@ #include "widgets.hpp" -using namespace MWGui; - -LocalMapBase::LocalMapBase() - : mCurX(0) - , mCurY(0) - , mInterior(false) - , mFogOfWar(true) - , mLocalMap(NULL) - , mMapDragAndDrop(false) - , mPrefix() - , mChanged(true) - , mLayout(NULL) - , mLastPositionX(0.0f) - , mLastPositionY(0.0f) - , mLastDirectionX(0.0f) - , mLastDirectionY(0.0f) - , mCompass(NULL) +namespace MWGui { -} -void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop) -{ - mLocalMap = widget; - mLayout = layout; - mMapDragAndDrop = mapDragAndDrop; - mCompass = compass; - - // create 3x3 map widgets, 512x512 each, holding a 1024x1024 texture each - const int widgetSize = 512; - for (int mx=0; mx<3; ++mx) + LocalMapBase::LocalMapBase() + : mCurX(0) + , mCurY(0) + , mInterior(false) + , mFogOfWar(true) + , mLocalMap(NULL) + , mMapDragAndDrop(false) + , mPrefix() + , mChanged(true) + , mLayout(NULL) + , mLastPositionX(0.0f) + , mLastPositionY(0.0f) + , mLastDirectionX(0.0f) + , mLastDirectionY(0.0f) + , mCompass(NULL) { - for (int my=0; my<3; ++my) + } + + void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop) + { + mLocalMap = widget; + mLayout = layout; + mMapDragAndDrop = mapDragAndDrop; + mCompass = compass; + + // create 3x3 map widgets, 512x512 each, holding a 1024x1024 texture each + const int widgetSize = 512; + for (int mx=0; mx<3; ++mx) { - MyGUI::ImageBox* map = mLocalMap->createWidget("ImageBox", - MyGUI::IntCoord(mx*widgetSize, my*widgetSize, widgetSize, widgetSize), - MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast(mx) + "_" + boost::lexical_cast(my)); - - MyGUI::ImageBox* fog = map->createWidget("ImageBox", - MyGUI::IntCoord(0, 0, widgetSize, widgetSize), - MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast(mx) + "_" + boost::lexical_cast(my) + "_fog"); - - if (!mMapDragAndDrop) + for (int my=0; my<3; ++my) { - map->setNeedMouseFocus(false); - fog->setNeedMouseFocus(false); - } + MyGUI::ImageBox* map = mLocalMap->createWidget("ImageBox", + MyGUI::IntCoord(mx*widgetSize, my*widgetSize, widgetSize, widgetSize), + MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast(mx) + "_" + boost::lexical_cast(my)); - mMapWidgets.push_back(map); - mFogWidgets.push_back(fog); - } - } -} + MyGUI::ImageBox* fog = map->createWidget("ImageBox", + MyGUI::IntCoord(0, 0, widgetSize, widgetSize), + MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast(mx) + "_" + boost::lexical_cast(my) + "_fog"); -void LocalMapBase::setCellPrefix(const std::string& prefix) -{ - mPrefix = prefix; - mChanged = true; -} - -void LocalMapBase::toggleFogOfWar() -{ - mFogOfWar = !mFogOfWar; - applyFogOfWar(); -} - -void LocalMapBase::applyFogOfWar() -{ - for (int mx=0; mx<3; ++mx) - { - for (int my=0; my<3; ++my) - { - std::string name = "Map_" + boost::lexical_cast(mx) + "_" - + boost::lexical_cast(my); - - std::string image = mPrefix+"_"+ boost::lexical_cast(mCurX + (mx-1)) + "_" - + boost::lexical_cast(mCurY + (-1*(my-1))); - MyGUI::ImageBox* fog = mFogWidgets[my + 3*mx]; - fog->setImageTexture(mFogOfWar ? - ((MyGUI::RenderManager::getInstance().getTexture(image+"_fog") != 0) ? image+"_fog" - : "black.png" ) - : ""); - } - } - notifyMapChanged (); -} - -void LocalMapBase::onMarkerFocused (MyGUI::Widget* w1, MyGUI::Widget* w2) -{ - applyFogOfWar (); -} - -void LocalMapBase::onMarkerUnfocused (MyGUI::Widget* w1, MyGUI::Widget* w2) -{ - applyFogOfWar (); -} - -void LocalMapBase::setActiveCell(const int x, const int y, bool interior) -{ - if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell - - // clear all previous markers - for (unsigned int i=0; i< mLocalMap->getChildCount(); ++i) - { - if (mLocalMap->getChildAt(i)->getName ().substr (0, 6) == "Marker") - { - MyGUI::Gui::getInstance ().destroyWidget (mLocalMap->getChildAt(i)); - } - } - - for (int mx=0; mx<3; ++mx) - { - for (int my=0; my<3; ++my) - { - // map - std::string image = mPrefix+"_"+ boost::lexical_cast(x + (mx-1)) + "_" - + boost::lexical_cast(y + (-1*(my-1))); - - std::string name = "Map_" + boost::lexical_cast(mx) + "_" - + boost::lexical_cast(my); - - MyGUI::ImageBox* box = mMapWidgets[my + 3*mx]; - - if (MyGUI::RenderManager::getInstance().getTexture(image) != 0) - box->setImageTexture(image); - else - box->setImageTexture("black.png"); - - - // door markers - - // interior map only consists of one cell, so handle the markers only once - if (interior && (mx != 2 || my != 2)) - continue; - - MWWorld::CellStore* cell; - if (interior) - cell = MWBase::Environment::get().getWorld ()->getInterior (mPrefix); - else - cell = MWBase::Environment::get().getWorld ()->getExterior (x+mx-1, y-(my-1)); - - std::vector doors = MWBase::Environment::get().getWorld ()->getDoorMarkers (cell); - - for (std::vector::iterator it = doors.begin(); it != doors.end(); ++it) - { - MWBase::World::DoorMarker marker = *it; - - // convert world coordinates to normalized cell coordinates - MyGUI::IntCoord widgetCoord; - float nX,nY; - int cellDx, cellDy; - if (!interior) + if (!mMapDragAndDrop) { - const int cellSize = 8192; - - nX = (marker.x - cellSize * (x+mx-1)) / cellSize; - nY = 1 - (marker.y - cellSize * (y-(my-1))) / cellSize; - - widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + mx * 512, nY * 512 - 4 + my * 512, 8, 8); + map->setNeedMouseFocus(false); + fog->setNeedMouseFocus(false); } + + mMapWidgets.push_back(map); + mFogWidgets.push_back(fog); + } + } + } + + void LocalMapBase::setCellPrefix(const std::string& prefix) + { + mPrefix = prefix; + mChanged = true; + } + + void LocalMapBase::toggleFogOfWar() + { + mFogOfWar = !mFogOfWar; + applyFogOfWar(); + } + + void LocalMapBase::applyFogOfWar() + { + for (int mx=0; mx<3; ++mx) + { + for (int my=0; my<3; ++my) + { + std::string name = "Map_" + boost::lexical_cast(mx) + "_" + + boost::lexical_cast(my); + + std::string image = mPrefix+"_"+ boost::lexical_cast(mCurX + (mx-1)) + "_" + + boost::lexical_cast(mCurY + (-1*(my-1))); + MyGUI::ImageBox* fog = mFogWidgets[my + 3*mx]; + fog->setImageTexture(mFogOfWar ? + ((MyGUI::RenderManager::getInstance().getTexture(image+"_fog") != 0) ? image+"_fog" + : "black.png" ) + : ""); + } + } + notifyMapChanged (); + } + + void LocalMapBase::onMarkerFocused (MyGUI::Widget* w1, MyGUI::Widget* w2) + { + applyFogOfWar (); + } + + void LocalMapBase::onMarkerUnfocused (MyGUI::Widget* w1, MyGUI::Widget* w2) + { + applyFogOfWar (); + } + + void LocalMapBase::setActiveCell(const int x, const int y, bool interior) + { + if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell + + // clear all previous markers + for (unsigned int i=0; i< mLocalMap->getChildCount(); ++i) + { + if (mLocalMap->getChildAt(i)->getName ().substr (0, 6) == "Marker") + { + MyGUI::Gui::getInstance ().destroyWidget (mLocalMap->getChildAt(i)); + } + } + + for (int mx=0; mx<3; ++mx) + { + for (int my=0; my<3; ++my) + { + // map + std::string image = mPrefix+"_"+ boost::lexical_cast(x + (mx-1)) + "_" + + boost::lexical_cast(y + (-1*(my-1))); + + std::string name = "Map_" + boost::lexical_cast(mx) + "_" + + boost::lexical_cast(my); + + MyGUI::ImageBox* box = mMapWidgets[my + 3*mx]; + + if (MyGUI::RenderManager::getInstance().getTexture(image) != 0) + box->setImageTexture(image); else - { - Ogre::Vector2 position (marker.x, marker.y); - MWBase::Environment::get().getWorld ()->getInteriorMapPosition (position, nX, nY, cellDx, cellDy); + box->setImageTexture("black.png"); - widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + (1+cellDx-x) * 512, nY * 512 - 4 + (1+cellDy-y) * 512, 8, 8); + + // door markers + + // interior map only consists of one cell, so handle the markers only once + if (interior && (mx != 2 || my != 2)) + continue; + + MWWorld::CellStore* cell; + if (interior) + cell = MWBase::Environment::get().getWorld ()->getInterior (mPrefix); + else + cell = MWBase::Environment::get().getWorld ()->getExterior (x+mx-1, y-(my-1)); + + std::vector doors = MWBase::Environment::get().getWorld ()->getDoorMarkers (cell); + + for (std::vector::iterator it = doors.begin(); it != doors.end(); ++it) + { + MWBase::World::DoorMarker marker = *it; + + // convert world coordinates to normalized cell coordinates + MyGUI::IntCoord widgetCoord; + float nX,nY; + int cellDx, cellDy; + if (!interior) + { + const int cellSize = 8192; + + nX = (marker.x - cellSize * (x+mx-1)) / cellSize; + nY = 1 - (marker.y - cellSize * (y-(my-1))) / cellSize; + + widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + mx * 512, nY * 512 - 4 + my * 512, 8, 8); + } + else + { + Ogre::Vector2 position (marker.x, marker.y); + MWBase::Environment::get().getWorld ()->getInteriorMapPosition (position, nX, nY, cellDx, cellDy); + + widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + (1+cellDx-x) * 512, nY * 512 - 4 + (1+cellDy-y) * 512, 8, 8); + } + + static int counter = 0; + ++counter; + MyGUI::Button* markerWidget = mLocalMap->createWidget("ButtonImage", + widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(counter)); + markerWidget->setImageResource("DoorMarker"); + markerWidget->setUserString("ToolTipType", "Layout"); + markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); + markerWidget->setUserString("Caption_TextOneLine", marker.name); + markerWidget->setUserString("IsMarker", "true"); + markerWidget->eventMouseSetFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerFocused); + markerWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerUnfocused); + + MarkerPosition markerPos; + markerPos.interior = interior; + markerPos.cellX = interior ? cellDx : x + mx - 1; + markerPos.cellY = interior ? cellDy : y + ((my - 1)*-1); + markerPos.nX = nX; + markerPos.nY = nY; + + markerWidget->setUserData(markerPos); } - static int counter = 0; - ++counter; - MyGUI::Button* markerWidget = mLocalMap->createWidget("ButtonImage", - widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(counter)); - markerWidget->setImageResource("DoorMarker"); - markerWidget->setUserString("ToolTipType", "Layout"); - markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); - markerWidget->setUserString("Caption_TextOneLine", marker.name); - markerWidget->setUserString("IsMarker", "true"); - markerWidget->eventMouseSetFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerFocused); - markerWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerUnfocused); - MarkerPosition markerPos; - markerPos.interior = interior; - markerPos.cellX = interior ? cellDx : x + mx - 1; - markerPos.cellY = interior ? cellDy : y + ((my - 1)*-1); - markerPos.nX = nX; - markerPos.nY = nY; - - markerWidget->setUserData(markerPos); } - - } - } - mInterior = interior; - mCurX = x; - mCurY = y; - mChanged = false; + mInterior = interior; + mCurX = x; + mCurY = y; + mChanged = false; - // fog of war - applyFogOfWar(); + // fog of war + applyFogOfWar(); - // set the compass texture again, because MyGUI determines sorting of ImageBox widgets - // based on the last setImageTexture call - std::string tex = "textures\\compass.dds"; - mCompass->setImageTexture(""); - mCompass->setImageTexture(tex); -} - - -void LocalMapBase::setPlayerPos(const float x, const float y) -{ - if (x == mLastPositionX && y == mLastPositionY) - return; - - notifyPlayerUpdate (); - - MyGUI::IntSize size = mLocalMap->getCanvasSize(); - MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height); - MyGUI::IntCoord viewsize = mLocalMap->getCoord(); - MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top); - mLocalMap->setViewOffset(pos); - - mCompass->setPosition(MyGUI::IntPoint(512+x*512-16, 512+y*512-16)); - mLastPositionX = x; - mLastPositionY = y; -} - -void LocalMapBase::setPlayerDir(const float x, const float y) -{ - if (x == mLastDirectionX && y == mLastDirectionY) - return; - - notifyPlayerUpdate (); - - MyGUI::ISubWidget* main = mCompass->getSubWidgetMain(); - MyGUI::RotatingSkin* rotatingSubskin = main->castType(); - rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); - float angle = std::atan2(x,y); - rotatingSubskin->setAngle(angle); - - mLastDirectionX = x; - mLastDirectionY = y; -} - -// ------------------------------------------------------------------------------------------ - -MapWindow::MapWindow(const std::string& cacheDir) - : MWGui::WindowPinnableBase("openmw_map_window.layout") - , mGlobal(false) -{ - setCoord(500,0,320,300); - - mGlobalMapRender = new MWRender::GlobalMap(cacheDir); - mGlobalMapRender->render(); - - getWidget(mLocalMap, "LocalMap"); - getWidget(mGlobalMap, "GlobalMap"); - getWidget(mGlobalMapImage, "GlobalMapImage"); - getWidget(mGlobalMapOverlay, "GlobalMapOverlay"); - getWidget(mPlayerArrowLocal, "CompassLocal"); - getWidget(mPlayerArrowGlobal, "CompassGlobal"); - - mGlobalMapImage->setImageTexture("GlobalMap.png"); - mGlobalMapOverlay->setImageTexture("GlobalMapOverlay"); - - mGlobalMap->setVisible (false); - - getWidget(mButton, "WorldButton"); - mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); - mButton->setCaptionWithReplacing("#{sWorld}"); - - getWidget(mEventBoxGlobal, "EventBoxGlobal"); - mEventBoxGlobal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); - mEventBoxGlobal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); - getWidget(mEventBoxLocal, "EventBoxLocal"); - mEventBoxLocal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); - mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); - - LocalMapBase::init(mLocalMap, mPlayerArrowLocal, this); -} - -MapWindow::~MapWindow() -{ - delete mGlobalMapRender; -} - -void MapWindow::setCellName(const std::string& cellName) -{ - setTitle("#{sCell=" + cellName + "}"); -} - -void MapWindow::addVisitedLocation(const std::string& name, int x, int y) -{ - float worldX, worldY; - mGlobalMapRender->cellTopLeftCornerToImageSpace (x, y, worldX, worldY); - - MyGUI::IntCoord widgetCoord( - worldX * mGlobalMapRender->getWidth()+6, - worldY * mGlobalMapRender->getHeight()+6, - 12, 12); - - - static int _counter=0; - MyGUI::Button* markerWidget = mGlobalMapImage->createWidget("ButtonImage", - widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(_counter)); - markerWidget->setImageResource("DoorMarker"); - markerWidget->setUserString("ToolTipType", "Layout"); - markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); - markerWidget->setUserString("Caption_TextOneLine", name); - ++_counter; - - markerWidget = mEventBoxGlobal->createWidget("", - widgetCoord, MyGUI::Align::Default); - markerWidget->setNeedMouseFocus (true); - markerWidget->setUserString("ToolTipType", "Layout"); - markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); - markerWidget->setUserString("Caption_TextOneLine", name); -} - -void MapWindow::cellExplored(int x, int y) -{ - mGlobalMapRender->exploreCell(x,y); -} - -void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) -{ - if (_id!=MyGUI::MouseButton::Left) return; - mLastDragPos = MyGUI::IntPoint(_left, _top); -} - -void MapWindow::onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) -{ - if (_id!=MyGUI::MouseButton::Left) return; - - MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos; - - if (!mGlobal) - mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff ); - else - mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff ); - - - mLastDragPos = MyGUI::IntPoint(_left, _top); -} - -void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender) -{ - mGlobal = !mGlobal; - mGlobalMap->setVisible(mGlobal); - mLocalMap->setVisible(!mGlobal); - - mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : - "#{sWorld}"); - - if (mGlobal) - globalMapUpdatePlayer (); -} - -void MapWindow::onPinToggled() -{ - MWBase::Environment::get().getWindowManager()->setMinimapVisibility(!mPinned); -} - -void MapWindow::open() -{ - mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); - mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); - - for (unsigned int i=0; igetChildCount (); ++i) - { - if (mGlobalMapImage->getChildAt (i)->getName().substr(0,6) == "Marker") - mGlobalMapImage->getChildAt (i)->castType()->setImageResource("DoorMarker"); + // set the compass texture again, because MyGUI determines sorting of ImageBox widgets + // based on the last setImageTexture call + std::string tex = "textures\\compass.dds"; + mCompass->setImageTexture(""); + mCompass->setImageTexture(tex); } - globalMapUpdatePlayer(); - mPlayerArrowGlobal->setImageTexture ("textures\\compass.dds"); -} - -void MapWindow::globalMapUpdatePlayer () -{ - Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedPosition (); - Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedOrientation (); - Ogre::Vector2 dir (orient.yAxis ().x, orient.yAxis().y); - - float worldX, worldY; - mGlobalMapRender->worldPosToImageSpace (pos.x, pos.y, worldX, worldY); - worldX *= mGlobalMapRender->getWidth(); - worldY *= mGlobalMapRender->getHeight(); - - - // for interiors, we have no choice other than using the last position & direction. - /// \todo save this last position in the savegame? - if (MWBase::Environment::get().getWorld ()->isCellExterior ()) + void LocalMapBase::setPlayerPos(const float x, const float y) { - mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(worldX - 16, worldY - 16)); + if (x == mLastPositionX && y == mLastPositionY) + return; - MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain(); + notifyPlayerUpdate (); + + MyGUI::IntSize size = mLocalMap->getCanvasSize(); + MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height); + MyGUI::IntCoord viewsize = mLocalMap->getCoord(); + MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top); + mLocalMap->setViewOffset(pos); + + mCompass->setPosition(MyGUI::IntPoint(512+x*512-16, 512+y*512-16)); + mLastPositionX = x; + mLastPositionY = y; + } + + void LocalMapBase::setPlayerDir(const float x, const float y) + { + if (x == mLastDirectionX && y == mLastDirectionY) + return; + + notifyPlayerUpdate (); + + MyGUI::ISubWidget* main = mCompass->getSubWidgetMain(); MyGUI::RotatingSkin* rotatingSubskin = main->castType(); rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); - float angle = std::atan2(dir.x, dir.y); + float angle = std::atan2(x,y); rotatingSubskin->setAngle(angle); - // set the view offset so that player is in the center - MyGUI::IntSize viewsize = mGlobalMap->getSize(); - MyGUI::IntPoint viewoffs(0.5*viewsize.width - worldX, 0.5*viewsize.height - worldY); - mGlobalMap->setViewOffset(viewoffs); + mLastDirectionX = x; + mLastDirectionY = y; } -} -void MapWindow::notifyPlayerUpdate () -{ - globalMapUpdatePlayer (); -} + // ------------------------------------------------------------------------------------------ -void MapWindow::notifyMapChanged () -{ - // workaround to prevent the map from drawing on top of the button - MyGUI::IntCoord oldCoord = mButton->getCoord (); - MyGUI::Gui::getInstance().destroyWidget (mButton); - mButton = mMainWidget->createWidget("MW_Button", - oldCoord, MyGUI::Align::Bottom | MyGUI::Align::Right); - mButton->setProperty ("ExpandDirection", "Left"); + MapWindow::MapWindow(const std::string& cacheDir) + : MWGui::WindowPinnableBase("openmw_map_window.layout") + , mGlobal(false) + { + setCoord(500,0,320,300); + + mGlobalMapRender = new MWRender::GlobalMap(cacheDir); + mGlobalMapRender->render(); + + getWidget(mLocalMap, "LocalMap"); + getWidget(mGlobalMap, "GlobalMap"); + getWidget(mGlobalMapImage, "GlobalMapImage"); + getWidget(mGlobalMapOverlay, "GlobalMapOverlay"); + getWidget(mPlayerArrowLocal, "CompassLocal"); + getWidget(mPlayerArrowGlobal, "CompassGlobal"); + + mGlobalMapImage->setImageTexture("GlobalMap.png"); + mGlobalMapOverlay->setImageTexture("GlobalMapOverlay"); + + mGlobalMap->setVisible (false); + + getWidget(mButton, "WorldButton"); + mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); + mButton->setCaptionWithReplacing("#{sWorld}"); + + getWidget(mEventBoxGlobal, "EventBoxGlobal"); + mEventBoxGlobal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); + mEventBoxGlobal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); + getWidget(mEventBoxLocal, "EventBoxLocal"); + mEventBoxLocal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); + mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); + + LocalMapBase::init(mLocalMap, mPlayerArrowLocal, this); + } + + MapWindow::~MapWindow() + { + delete mGlobalMapRender; + } + + void MapWindow::setCellName(const std::string& cellName) + { + setTitle("#{sCell=" + cellName + "}"); + } + + void MapWindow::addVisitedLocation(const std::string& name, int x, int y) + { + float worldX, worldY; + mGlobalMapRender->cellTopLeftCornerToImageSpace (x, y, worldX, worldY); + + MyGUI::IntCoord widgetCoord( + worldX * mGlobalMapRender->getWidth()+6, + worldY * mGlobalMapRender->getHeight()+6, + 12, 12); + + + static int _counter=0; + MyGUI::Button* markerWidget = mGlobalMapImage->createWidget("ButtonImage", + widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(_counter)); + markerWidget->setImageResource("DoorMarker"); + markerWidget->setUserString("ToolTipType", "Layout"); + markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); + markerWidget->setUserString("Caption_TextOneLine", name); + ++_counter; + + markerWidget = mEventBoxGlobal->createWidget("", + widgetCoord, MyGUI::Align::Default); + markerWidget->setNeedMouseFocus (true); + markerWidget->setUserString("ToolTipType", "Layout"); + markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); + markerWidget->setUserString("Caption_TextOneLine", name); + } + + void MapWindow::cellExplored(int x, int y) + { + mGlobalMapRender->exploreCell(x,y); + } + + void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) + { + if (_id!=MyGUI::MouseButton::Left) return; + mLastDragPos = MyGUI::IntPoint(_left, _top); + } + + void MapWindow::onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) + { + if (_id!=MyGUI::MouseButton::Left) return; + + MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos; + + if (!mGlobal) + mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff ); + else + mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff ); + + + mLastDragPos = MyGUI::IntPoint(_left, _top); + } + + void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender) + { + mGlobal = !mGlobal; + mGlobalMap->setVisible(mGlobal); + mLocalMap->setVisible(!mGlobal); + + mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : + "#{sWorld}"); + + if (mGlobal) + globalMapUpdatePlayer (); + } + + void MapWindow::onPinToggled() + { + MWBase::Environment::get().getWindowManager()->setMinimapVisibility(!mPinned); + } + + void MapWindow::open() + { + mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); + mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); + + for (unsigned int i=0; igetChildCount (); ++i) + { + if (mGlobalMapImage->getChildAt (i)->getName().substr(0,6) == "Marker") + mGlobalMapImage->getChildAt (i)->castType()->setImageResource("DoorMarker"); + } + + globalMapUpdatePlayer(); + + mPlayerArrowGlobal->setImageTexture ("textures\\compass.dds"); + } + + void MapWindow::globalMapUpdatePlayer () + { + Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedPosition (); + Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedOrientation (); + Ogre::Vector2 dir (orient.yAxis ().x, orient.yAxis().y); + + float worldX, worldY; + mGlobalMapRender->worldPosToImageSpace (pos.x, pos.y, worldX, worldY); + worldX *= mGlobalMapRender->getWidth(); + worldY *= mGlobalMapRender->getHeight(); + + + // for interiors, we have no choice other than using the last position & direction. + /// \todo save this last position in the savegame? + if (MWBase::Environment::get().getWorld ()->isCellExterior ()) + { + mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(worldX - 16, worldY - 16)); + + MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain(); + MyGUI::RotatingSkin* rotatingSubskin = main->castType(); + rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); + float angle = std::atan2(dir.x, dir.y); + rotatingSubskin->setAngle(angle); + + // set the view offset so that player is in the center + MyGUI::IntSize viewsize = mGlobalMap->getSize(); + MyGUI::IntPoint viewoffs(0.5*viewsize.width - worldX, 0.5*viewsize.height - worldY); + mGlobalMap->setViewOffset(viewoffs); + } + } + + void MapWindow::notifyPlayerUpdate () + { + globalMapUpdatePlayer (); + } + + void MapWindow::notifyMapChanged () + { + // workaround to prevent the map from drawing on top of the button + MyGUI::IntCoord oldCoord = mButton->getCoord (); + MyGUI::Gui::getInstance().destroyWidget (mButton); + mButton = mMainWidget->createWidget("MW_Button", + oldCoord, MyGUI::Align::Bottom | MyGUI::Align::Right); + mButton->setProperty ("ExpandDirection", "Left"); + + mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); + mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : + "#{sWorld}"); + } - mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); - mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : - "#{sWorld}"); } diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 8e53380bd..876fb3522 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -5,411 +5,414 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/inputmanager.hpp" -using namespace MWGui; - -MessageBoxManager::MessageBoxManager () +namespace MWGui { - // defines - mMessageBoxSpeed = 0.1; - mInterMessageBoxe = NULL; -} -void MessageBoxManager::onFrame (float frameDuration) -{ - std::vector::iterator it; - for(it = mTimers.begin(); it != mTimers.end();) + MessageBoxManager::MessageBoxManager () { - // if this messagebox is already deleted, remove the timer and move on - if (std::find(mMessageBoxes.begin(), mMessageBoxes.end(), it->messageBox) == mMessageBoxes.end()) - { - it = mTimers.erase(it); - continue; - } + // defines + mMessageBoxSpeed = 0.1; + mInterMessageBoxe = NULL; + } - it->current += frameDuration; - if(it->current >= it->max) + void MessageBoxManager::onFrame (float frameDuration) + { + std::vector::iterator it; + for(it = mTimers.begin(); it != mTimers.end();) { - it->messageBox->mMarkedToDelete = true; - - if(*mMessageBoxes.begin() == it->messageBox) // if this box is the last one + // if this messagebox is already deleted, remove the timer and move on + if (std::find(mMessageBoxes.begin(), mMessageBoxes.end(), it->messageBox) == mMessageBoxes.end()) { - // collect all with mMarkedToDelete and delete them. - // and place the other messageboxes on the right position - int height = 0; - std::vector::iterator it2 = mMessageBoxes.begin(); - while(it2 != mMessageBoxes.end()) + it = mTimers.erase(it); + continue; + } + + it->current += frameDuration; + if(it->current >= it->max) + { + it->messageBox->mMarkedToDelete = true; + + if(*mMessageBoxes.begin() == it->messageBox) // if this box is the last one { - if((*it2)->mMarkedToDelete) + // collect all with mMarkedToDelete and delete them. + // and place the other messageboxes on the right position + int height = 0; + std::vector::iterator it2 = mMessageBoxes.begin(); + while(it2 != mMessageBoxes.end()) { - delete (*it2); - it2 = mMessageBoxes.erase(it2); - } - else { - (*it2)->update(height); - height += (*it2)->getHeight(); - it2++; + if((*it2)->mMarkedToDelete) + { + delete (*it2); + it2 = mMessageBoxes.erase(it2); + } + else { + (*it2)->update(height); + height += (*it2)->getHeight(); + it2++; + } } } + it = mTimers.erase(it); + } + else + { + it++; + } + } + + if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) { + delete mInterMessageBoxe; + mInterMessageBoxe = NULL; + MWBase::Environment::get().getInputManager()->changeInputMode( + MWBase::Environment::get().getWindowManager()->isGuiMode()); + } + } + + void MessageBoxManager::createMessageBox (const std::string& message) + { + MessageBox *box = new MessageBox(*this, message); + + removeMessageBox(message.length()*mMessageBoxSpeed, box); + + mMessageBoxes.push_back(box); + std::vector::iterator it; + + if(mMessageBoxes.size() > 3) { + delete *mMessageBoxes.begin(); + mMessageBoxes.erase(mMessageBoxes.begin()); + } + + int height = 0; + for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it) + { + (*it)->update(height); + height += (*it)->getHeight(); + } + } + + bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector& buttons) + { + if(mInterMessageBoxe != NULL) { + throw std::runtime_error("There is a message box already"); + } + + mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons); + + return true; + } + + bool MessageBoxManager::isInteractiveMessageBox () + { + return mInterMessageBoxe != NULL; + } + + void MessageBoxManager::removeMessageBox (float time, MessageBox *msgbox) + { + MessageBoxManagerTimer timer; + timer.current = 0; + timer.max = time; + timer.messageBox = msgbox; + + mTimers.insert(mTimers.end(), timer); + } + + bool MessageBoxManager::removeMessageBox (MessageBox *msgbox) + { + std::vector::iterator it; + for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it) + { + if((*it) == msgbox) + { + delete (*it); + mMessageBoxes.erase(it); + return true; + } + } + return false; + } + + void MessageBoxManager::setMessageBoxSpeed (int speed) + { + mMessageBoxSpeed = speed; + } + + void MessageBoxManager::enterPressed () + { + if(mInterMessageBoxe != NULL) + mInterMessageBoxe->enterPressed(); + } + + int MessageBoxManager::readPressedButton () + { + if(mInterMessageBoxe != NULL) + { + return mInterMessageBoxe->readPressedButton(); + } + return -1; + } + + + + + MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message) + : Layout("openmw_messagebox.layout") + , mMessageBoxManager(parMessageBoxManager) + , mMessage(message) + { + // defines + mFixedWidth = 300; + mBottomPadding = 20; + mNextBoxPadding = 20; + mMarkedToDelete = false; + + getWidget(mMessageWidget, "message"); + + mMessageWidget->setOverflowToTheLeft(true); + mMessageWidget->setCaptionWithReplacing(mMessage); + + MyGUI::IntSize size; + size.width = mFixedWidth; + size.height = 100; // dummy + + MyGUI::IntCoord coord; + coord.left = 10; // dummy + coord.top = 10; // dummy + + mMessageWidget->setSize(size); + + MyGUI::IntSize textSize = mMessageWidget->getTextSize(); + + size.height = mHeight = textSize.height + 20; // this is the padding between the text and the box + + mMainWidget->setSize(size); + size.width -= 15; // this is to center the text (see messagebox.layout, Widget type="Edit" position="-2 -3 0 0") + mMessageWidget->setSize(size); + } + + void MessageBox::update (int height) + { + MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize(); + MyGUI::IntCoord coord; + coord.left = (gameWindowSize.width - mFixedWidth)/2; + coord.top = (gameWindowSize.height - mHeight - height - mBottomPadding); + + MyGUI::IntSize size; + size.width = mFixedWidth; + size.height = mHeight; + + mMainWidget->setCoord(coord); + mMainWidget->setSize(size); + mMainWidget->setVisible(true); + } + + int MessageBox::getHeight () + { + return mHeight+mNextBoxPadding; // 20 is the padding between this and the next MessageBox + } + + + + InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons) + : WindowModal("openmw_interactive_messagebox.layout") + , mMessageBoxManager(parMessageBoxManager) + , mButtonPressed(-1) + { + WindowModal::open(); + + int fixedWidth = 500; + int textPadding = 10; // padding between text-widget and main-widget + int textButtonPadding = 20; // padding between the text-widget und the button-widget + int buttonLeftPadding = 10; // padding between the buttons if horizontal + int buttonTopPadding = 5; // ^-- if vertical + int buttonPadding = 5; // padding between button label and button itself + int buttonMainPadding = 10; // padding between buttons and bottom of the main widget + + mMarkedToDelete = false; + + + getWidget(mMessageWidget, "message"); + getWidget(mButtonsWidget, "buttons"); + + mMessageWidget->setOverflowToTheLeft(true); + mMessageWidget->setCaptionWithReplacing(message); + + MyGUI::IntSize textSize = mMessageWidget->getTextSize(); + + MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize(); + + int biggestButtonWidth = 0; + int buttonWidth = 0; + int buttonsWidth = 0; + int buttonHeight = 0; + MyGUI::IntCoord dummyCoord(0, 0, 0, 0); + + std::vector::const_iterator it; + for(it = buttons.begin(); it != buttons.end(); ++it) + { + MyGUI::Button* button = mButtonsWidget->createWidget( + MyGUI::WidgetStyle::Child, + std::string("MW_Button"), + dummyCoord, + MyGUI::Align::Default); + button->setCaptionWithReplacing(*it); + + button->eventMouseButtonClick += MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed); + + mButtons.push_back(button); + + buttonWidth = button->getTextSize().width + 2*buttonPadding + buttonLeftPadding; + buttonsWidth += buttonWidth; + buttonHeight = button->getTextSize().height + 2*buttonPadding + buttonTopPadding; + + if(buttonWidth > biggestButtonWidth) + { + biggestButtonWidth = buttonWidth; + } + } + buttonsWidth += buttonLeftPadding; + + MyGUI::IntSize mainWidgetSize; + if(buttonsWidth < fixedWidth) + { + // on one line + if(textSize.width + 2*textPadding < buttonsWidth) + { + mainWidgetSize.width = buttonsWidth; + } + else + { + mainWidgetSize.width = textSize.width + 3*textPadding; + } + mainWidgetSize.height = textSize.height + textButtonPadding + buttonHeight + buttonMainPadding; + + MyGUI::IntCoord absCoord; + absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; + absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; + + mMainWidget->setCoord(absCoord); + mMainWidget->setSize(mainWidgetSize); + + MyGUI::IntCoord messageWidgetCoord; + messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; + messageWidgetCoord.top = textPadding; + mMessageWidget->setCoord(messageWidgetCoord); + + mMessageWidget->setSize(textSize); + + MyGUI::IntCoord buttonCord; + MyGUI::IntSize buttonSize(0, buttonHeight); + int left = (mainWidgetSize.width - buttonsWidth)/2 + buttonPadding; + + std::vector::const_iterator button; + for(button = mButtons.begin(); button != mButtons.end(); ++button) + { + buttonCord.left = left; + buttonCord.top = textSize.height + textButtonPadding; + + buttonSize.width = (*button)->getTextSize().width + 2*buttonPadding; + buttonSize.height = (*button)->getTextSize().height + 2*buttonPadding; + + (*button)->setCoord(buttonCord); + (*button)->setSize(buttonSize); + + left += buttonSize.width + buttonLeftPadding; } - it = mTimers.erase(it); } else { - it++; + // among each other + if(biggestButtonWidth > textSize.width) { + mainWidgetSize.width = biggestButtonWidth + buttonTopPadding; + } + else { + mainWidgetSize.width = textSize.width + 3*textPadding; + } + mainWidgetSize.height = textSize.height + 2*textPadding + textButtonPadding + buttonHeight * buttons.size() + buttonMainPadding; + + mMainWidget->setSize(mainWidgetSize); + + MyGUI::IntCoord absCoord; + absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; + absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; + + mMainWidget->setCoord(absCoord); + mMainWidget->setSize(mainWidgetSize); + + + MyGUI::IntCoord messageWidgetCoord; + messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; + messageWidgetCoord.top = textPadding; + mMessageWidget->setCoord(messageWidgetCoord); + + mMessageWidget->setSize(textSize); + + MyGUI::IntCoord buttonCord; + MyGUI::IntSize buttonSize(0, buttonHeight); + + int top = textButtonPadding + buttonTopPadding + textSize.height; + + std::vector::const_iterator button; + for(button = mButtons.begin(); button != mButtons.end(); ++button) + { + buttonSize.width = (*button)->getTextSize().width + buttonPadding*2; + buttonSize.height = (*button)->getTextSize().height + buttonPadding*2; + + buttonCord.top = top; + buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2 - 5; // FIXME: -5 is not so nice :/ + + (*button)->setCoord(buttonCord); + (*button)->setSize(buttonSize); + + top += buttonSize.height + 2*buttonTopPadding; + } + } } - if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) { - delete mInterMessageBoxe; - mInterMessageBoxe = NULL; - MWBase::Environment::get().getInputManager()->changeInputMode( - MWBase::Environment::get().getWindowManager()->isGuiMode()); - } -} - -void MessageBoxManager::createMessageBox (const std::string& message) -{ - MessageBox *box = new MessageBox(*this, message); - - removeMessageBox(message.length()*mMessageBoxSpeed, box); - - mMessageBoxes.push_back(box); - std::vector::iterator it; - - if(mMessageBoxes.size() > 3) { - delete *mMessageBoxes.begin(); - mMessageBoxes.erase(mMessageBoxes.begin()); - } - - int height = 0; - for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it) + void InteractiveMessageBox::enterPressed() { - (*it)->update(height); - height += (*it)->getHeight(); - } -} - -bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector& buttons) -{ - if(mInterMessageBoxe != NULL) { - throw std::runtime_error("There is a message box already"); - } - - mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons); - - return true; -} - -bool MessageBoxManager::isInteractiveMessageBox () -{ - return mInterMessageBoxe != NULL; -} - -void MessageBoxManager::removeMessageBox (float time, MessageBox *msgbox) -{ - MessageBoxManagerTimer timer; - timer.current = 0; - timer.max = time; - timer.messageBox = msgbox; - - mTimers.insert(mTimers.end(), timer); -} - -bool MessageBoxManager::removeMessageBox (MessageBox *msgbox) -{ - std::vector::iterator it; - for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it) - { - if((*it) == msgbox) - { - delete (*it); - mMessageBoxes.erase(it); - return true; - } - } - return false; -} - -void MessageBoxManager::setMessageBoxSpeed (int speed) -{ - mMessageBoxSpeed = speed; -} - -void MessageBoxManager::enterPressed () -{ - if(mInterMessageBoxe != NULL) - mInterMessageBoxe->enterPressed(); -} - -int MessageBoxManager::readPressedButton () -{ - if(mInterMessageBoxe != NULL) - { - return mInterMessageBoxe->readPressedButton(); - } - return -1; -} - - - - -MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message) - : Layout("openmw_messagebox.layout") - , mMessageBoxManager(parMessageBoxManager) - , mMessage(message) -{ - // defines - mFixedWidth = 300; - mBottomPadding = 20; - mNextBoxPadding = 20; - mMarkedToDelete = false; - - getWidget(mMessageWidget, "message"); - - mMessageWidget->setOverflowToTheLeft(true); - mMessageWidget->setCaptionWithReplacing(mMessage); - - MyGUI::IntSize size; - size.width = mFixedWidth; - size.height = 100; // dummy - - MyGUI::IntCoord coord; - coord.left = 10; // dummy - coord.top = 10; // dummy - - mMessageWidget->setSize(size); - - MyGUI::IntSize textSize = mMessageWidget->getTextSize(); - - size.height = mHeight = textSize.height + 20; // this is the padding between the text and the box - - mMainWidget->setSize(size); - size.width -= 15; // this is to center the text (see messagebox.layout, Widget type="Edit" position="-2 -3 0 0") - mMessageWidget->setSize(size); -} - -void MessageBox::update (int height) -{ - MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize(); - MyGUI::IntCoord coord; - coord.left = (gameWindowSize.width - mFixedWidth)/2; - coord.top = (gameWindowSize.height - mHeight - height - mBottomPadding); - - MyGUI::IntSize size; - size.width = mFixedWidth; - size.height = mHeight; - - mMainWidget->setCoord(coord); - mMainWidget->setSize(size); - mMainWidget->setVisible(true); -} - -int MessageBox::getHeight () -{ - return mHeight+mNextBoxPadding; // 20 is the padding between this and the next MessageBox -} - - - -InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons) - : WindowModal("openmw_interactive_messagebox.layout") - , mMessageBoxManager(parMessageBoxManager) - , mButtonPressed(-1) -{ - WindowModal::open(); - - int fixedWidth = 500; - int textPadding = 10; // padding between text-widget and main-widget - int textButtonPadding = 20; // padding between the text-widget und the button-widget - int buttonLeftPadding = 10; // padding between the buttons if horizontal - int buttonTopPadding = 5; // ^-- if vertical - int buttonPadding = 5; // padding between button label and button itself - int buttonMainPadding = 10; // padding between buttons and bottom of the main widget - - mMarkedToDelete = false; - - - getWidget(mMessageWidget, "message"); - getWidget(mButtonsWidget, "buttons"); - - mMessageWidget->setOverflowToTheLeft(true); - mMessageWidget->setCaptionWithReplacing(message); - - MyGUI::IntSize textSize = mMessageWidget->getTextSize(); - - MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize(); - - int biggestButtonWidth = 0; - int buttonWidth = 0; - int buttonsWidth = 0; - int buttonHeight = 0; - MyGUI::IntCoord dummyCoord(0, 0, 0, 0); - - std::vector::const_iterator it; - for(it = buttons.begin(); it != buttons.end(); ++it) - { - MyGUI::Button* button = mButtonsWidget->createWidget( - MyGUI::WidgetStyle::Child, - std::string("MW_Button"), - dummyCoord, - MyGUI::Align::Default); - button->setCaptionWithReplacing(*it); - - button->eventMouseButtonClick += MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed); - - mButtons.push_back(button); - - buttonWidth = button->getTextSize().width + 2*buttonPadding + buttonLeftPadding; - buttonsWidth += buttonWidth; - buttonHeight = button->getTextSize().height + 2*buttonPadding + buttonTopPadding; - - if(buttonWidth > biggestButtonWidth) - { - biggestButtonWidth = buttonWidth; - } - } - buttonsWidth += buttonLeftPadding; - - MyGUI::IntSize mainWidgetSize; - if(buttonsWidth < fixedWidth) - { - // on one line - if(textSize.width + 2*textPadding < buttonsWidth) - { - mainWidgetSize.width = buttonsWidth; - } - else - { - mainWidgetSize.width = textSize.width + 3*textPadding; - } - mainWidgetSize.height = textSize.height + textButtonPadding + buttonHeight + buttonMainPadding; - - MyGUI::IntCoord absCoord; - absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; - absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; - - mMainWidget->setCoord(absCoord); - mMainWidget->setSize(mainWidgetSize); - - MyGUI::IntCoord messageWidgetCoord; - messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; - messageWidgetCoord.top = textPadding; - mMessageWidget->setCoord(messageWidgetCoord); - - mMessageWidget->setSize(textSize); - - MyGUI::IntCoord buttonCord; - MyGUI::IntSize buttonSize(0, buttonHeight); - int left = (mainWidgetSize.width - buttonsWidth)/2 + buttonPadding; + std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}")); std::vector::const_iterator button; for(button = mButtons.begin(); button != mButtons.end(); ++button) { - buttonCord.left = left; - buttonCord.top = textSize.height + textButtonPadding; - - buttonSize.width = (*button)->getTextSize().width + 2*buttonPadding; - buttonSize.height = (*button)->getTextSize().height + 2*buttonPadding; - - (*button)->setCoord(buttonCord); - (*button)->setSize(buttonSize); - - left += buttonSize.width + buttonLeftPadding; + if(Misc::StringUtils::lowerCase((*button)->getCaption()) == ok) + { + buttonActivated(*button); + MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); + break; + } } + } - else + + void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed) { - // among each other - if(biggestButtonWidth > textSize.width) { - mainWidgetSize.width = biggestButtonWidth + buttonTopPadding; - } - else { - mainWidgetSize.width = textSize.width + 3*textPadding; - } - mainWidgetSize.height = textSize.height + 2*textPadding + textButtonPadding + buttonHeight * buttons.size() + buttonMainPadding; - - mMainWidget->setSize(mainWidgetSize); - - MyGUI::IntCoord absCoord; - absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; - absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; - - mMainWidget->setCoord(absCoord); - mMainWidget->setSize(mainWidgetSize); - - - MyGUI::IntCoord messageWidgetCoord; - messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; - messageWidgetCoord.top = textPadding; - mMessageWidget->setCoord(messageWidgetCoord); - - mMessageWidget->setSize(textSize); - - MyGUI::IntCoord buttonCord; - MyGUI::IntSize buttonSize(0, buttonHeight); - - int top = textButtonPadding + buttonTopPadding + textSize.height; + buttonActivated (pressed); + } + void InteractiveMessageBox::buttonActivated (MyGUI::Widget* pressed) + { + mMarkedToDelete = true; + int index = 0; std::vector::const_iterator button; for(button = mButtons.begin(); button != mButtons.end(); ++button) { - buttonSize.width = (*button)->getTextSize().width + buttonPadding*2; - buttonSize.height = (*button)->getTextSize().height + buttonPadding*2; - - buttonCord.top = top; - buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2 - 5; // FIXME: -5 is not so nice :/ - - (*button)->setCoord(buttonCord); - (*button)->setSize(buttonSize); - - top += buttonSize.height + 2*buttonTopPadding; + if(*button == pressed) + { + mButtonPressed = index; + mMessageBoxManager.onButtonPressed(mButtonPressed); + return; + } + index++; } - } -} -void InteractiveMessageBox::enterPressed() -{ - - std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}")); - std::vector::const_iterator button; - for(button = mButtons.begin(); button != mButtons.end(); ++button) + int InteractiveMessageBox::readPressedButton () { - if(Misc::StringUtils::lowerCase((*button)->getCaption()) == ok) - { - buttonActivated(*button); - MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); - break; - } + int pressed = mButtonPressed; + mButtonPressed = -1; + return pressed; } } - -void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed) -{ - buttonActivated (pressed); -} - -void InteractiveMessageBox::buttonActivated (MyGUI::Widget* pressed) -{ - mMarkedToDelete = true; - int index = 0; - std::vector::const_iterator button; - for(button = mButtons.begin(); button != mButtons.end(); ++button) - { - if(*button == pressed) - { - mButtonPressed = index; - mMessageBoxManager.onButtonPressed(mButtonPressed); - return; - } - index++; - } -} - -int InteractiveMessageBox::readPressedButton () -{ - int pressed = mButtonPressed; - mButtonPressed = -1; - return pressed; -} diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index c031ad6b6..0a7374085 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -10,435 +10,437 @@ #include "tooltips.hpp" -using namespace MWGui; -using namespace Widgets; - namespace { -int wrap(int index, int max) -{ - if (index < 0) - return max - 1; - else if (index >= max) - return 0; - else - return index; -} - -int countParts(const std::string &part, const std::string &race, bool male) -{ - /// \todo loop through the whole store for appropriate bodyparts instead of looking for fixed IDs - const MWWorld::Store &store = - MWBase::Environment::get().getWorld()->getStore().get(); - - std::string prefix = - "b_n_" + race + ((male) ? "_m_" : "_f_") + part; - - std::string suffix; - suffix.reserve(prefix.size() + 3); - - int count = -1; - do { - ++count; - suffix = "_" + (boost::format("%02d") % (count + 1)).str(); + int wrap(int index, int max) + { + if (index < 0) + return max - 1; + else if (index >= max) + return 0; + else + return index; } - while (store.search(prefix + suffix) != 0); - if (count == 0 && part == "hair") { - count = -1; + int countParts(const std::string &part, const std::string &race, bool male) + { + /// \todo loop through the whole store for appropriate bodyparts instead of looking for fixed IDs + const MWWorld::Store &store = + MWBase::Environment::get().getWorld()->getStore().get(); + + std::string prefix = + "b_n_" + race + ((male) ? "_m_" : "_f_") + part; + + std::string suffix; + suffix.reserve(prefix.size() + 3); + + int count = -1; do { ++count; - suffix = (boost::format("%02d") % (count + 1)).str(); + suffix = "_" + (boost::format("%02d") % (count + 1)).str(); } while (store.search(prefix + suffix) != 0); + + if (count == 0 && part == "hair") { + count = -1; + do { + ++count; + suffix = (boost::format("%02d") % (count + 1)).str(); + } + while (store.search(prefix + suffix) != 0); + } + return count; } - return count; -} } -RaceDialog::RaceDialog() - : WindowModal("openmw_chargen_race.layout") - , mGenderIndex(0) - , mFaceIndex(0) - , mHairIndex(0) - , mFaceCount(10) - , mHairCount(14) - , mCurrentAngle(0) +namespace MWGui { - // Centre dialog - center(); - setText("AppearanceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu1", "Appearance")); - getWidget(mPreviewImage, "PreviewImage"); - - getWidget(mHeadRotate, "HeadRotate"); - mHeadRotate->setScrollRange(50); - mHeadRotate->setScrollPosition(25); - mHeadRotate->setScrollViewPage(10); - mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate); - - // Set up next/previous buttons - MyGUI::Button *prevButton, *nextButton; - - setText("GenderChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu2", "Change Sex")); - getWidget(prevButton, "PrevGenderButton"); - getWidget(nextButton, "NextGenderButton"); - prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousGender); - nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextGender); - - setText("FaceChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu3", "Change Face")); - getWidget(prevButton, "PrevFaceButton"); - getWidget(nextButton, "NextFaceButton"); - prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousFace); - nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextFace); - - setText("HairChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu4", "Change Hair")); - getWidget(prevButton, "PrevHairButton"); - getWidget(nextButton, "NextHairButton"); - prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousHair); - nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextHair); - - setText("RaceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu5", "Race")); - getWidget(mRaceList, "RaceList"); - mRaceList->setScrollVisible(true); - mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); - mRaceList->eventListMouseItemActivate += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); - mRaceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); - - setText("SkillsT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sBonusSkillTitle", "Skill Bonus")); - getWidget(mSkillList, "SkillList"); - setText("SpellPowerT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu7", "Specials")); - getWidget(mSpellPowerList, "SpellPowerList"); - - MyGUI::Button* backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onBackClicked); - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked); - - updateRaces(); - updateSkills(); - updateSpellPowers(); -} - -void RaceDialog::setNextButtonShow(bool shown) -{ - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - - if (shown) - okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); - else - okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); -} - -void RaceDialog::open() -{ - WindowModal::open(); - - updateRaces(); - updateSkills(); - updateSpellPowers(); - - mPreview = new MWRender::RaceSelectionPreview(); - mPreview->setup(); - mPreview->update (0); - - const ESM::NPC proto = mPreview->getPrototype(); - setRaceId(proto.mRace); - recountParts(); - - std::string index = proto.mHead.substr(proto.mHead.size() - 2, 2); - mFaceIndex = boost::lexical_cast(index) - 1; - - index = proto.mHair.substr(proto.mHair.size() - 2, 2); - mHairIndex = boost::lexical_cast(index) - 1; - - mPreviewImage->setImageTexture ("CharacterHeadPreview"); -} - - -void RaceDialog::setRaceId(const std::string &raceId) -{ - mCurrentRaceId = raceId; - mRaceList->setIndexSelected(MyGUI::ITEM_NONE); - size_t count = mRaceList->getItemCount(); - for (size_t i = 0; i < count; ++i) + RaceDialog::RaceDialog() + : WindowModal("openmw_chargen_race.layout") + , mGenderIndex(0) + , mFaceIndex(0) + , mHairIndex(0) + , mFaceCount(10) + , mHairCount(14) + , mCurrentAngle(0) { - if (boost::iequals(*mRaceList->getItemDataAt(i), raceId)) + // Centre dialog + center(); + + setText("AppearanceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu1", "Appearance")); + getWidget(mPreviewImage, "PreviewImage"); + + getWidget(mHeadRotate, "HeadRotate"); + mHeadRotate->setScrollRange(50); + mHeadRotate->setScrollPosition(25); + mHeadRotate->setScrollViewPage(10); + mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate); + + // Set up next/previous buttons + MyGUI::Button *prevButton, *nextButton; + + setText("GenderChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu2", "Change Sex")); + getWidget(prevButton, "PrevGenderButton"); + getWidget(nextButton, "NextGenderButton"); + prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousGender); + nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextGender); + + setText("FaceChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu3", "Change Face")); + getWidget(prevButton, "PrevFaceButton"); + getWidget(nextButton, "NextFaceButton"); + prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousFace); + nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextFace); + + setText("HairChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu4", "Change Hair")); + getWidget(prevButton, "PrevHairButton"); + getWidget(nextButton, "NextHairButton"); + prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousHair); + nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextHair); + + setText("RaceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu5", "Race")); + getWidget(mRaceList, "RaceList"); + mRaceList->setScrollVisible(true); + mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); + mRaceList->eventListMouseItemActivate += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); + mRaceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); + + setText("SkillsT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sBonusSkillTitle", "Skill Bonus")); + getWidget(mSkillList, "SkillList"); + setText("SpellPowerT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu7", "Specials")); + getWidget(mSpellPowerList, "SpellPowerList"); + + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onBackClicked); + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked); + + updateRaces(); + updateSkills(); + updateSpellPowers(); + } + + void RaceDialog::setNextButtonShow(bool shown) + { + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } + + void RaceDialog::open() + { + WindowModal::open(); + + updateRaces(); + updateSkills(); + updateSpellPowers(); + + mPreview = new MWRender::RaceSelectionPreview(); + mPreview->setup(); + mPreview->update (0); + + const ESM::NPC proto = mPreview->getPrototype(); + setRaceId(proto.mRace); + recountParts(); + + std::string index = proto.mHead.substr(proto.mHead.size() - 2, 2); + mFaceIndex = boost::lexical_cast(index) - 1; + + index = proto.mHair.substr(proto.mHair.size() - 2, 2); + mHairIndex = boost::lexical_cast(index) - 1; + + mPreviewImage->setImageTexture ("CharacterHeadPreview"); + } + + + void RaceDialog::setRaceId(const std::string &raceId) + { + mCurrentRaceId = raceId; + mRaceList->setIndexSelected(MyGUI::ITEM_NONE); + size_t count = mRaceList->getItemCount(); + for (size_t i = 0; i < count; ++i) { - mRaceList->setIndexSelected(i); - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - break; + if (boost::iequals(*mRaceList->getItemDataAt(i), raceId)) + { + mRaceList->setIndexSelected(i); + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + break; + } + } + + updateSkills(); + updateSpellPowers(); + } + + void RaceDialog::close() + { + delete mPreview; + mPreview = 0; + } + + // widget controls + + void RaceDialog::onOkClicked(MyGUI::Widget* _sender) + { + if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE) + return; + eventDone(this); + } + + void RaceDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } + + void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position) + { + float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2; + float diff = angle - mCurrentAngle; + mPreview->update (diff); + mCurrentAngle += diff; + } + + void RaceDialog::onSelectPreviousGender(MyGUI::Widget*) + { + mGenderIndex = wrap(mGenderIndex - 1, 2); + + recountParts(); + updatePreview(); + } + + void RaceDialog::onSelectNextGender(MyGUI::Widget*) + { + mGenderIndex = wrap(mGenderIndex + 1, 2); + + recountParts(); + updatePreview(); + } + + void RaceDialog::onSelectPreviousFace(MyGUI::Widget*) + { + do + mFaceIndex = wrap(mFaceIndex - 1, mFaceCount); + while (!isFacePlayable()); + updatePreview(); + } + + void RaceDialog::onSelectNextFace(MyGUI::Widget*) + { + do + mFaceIndex = wrap(mFaceIndex + 1, mFaceCount); + while (!isFacePlayable()); + updatePreview(); + } + + void RaceDialog::onSelectPreviousHair(MyGUI::Widget*) + { + do + mHairIndex = wrap(mHairIndex - 1, mHairCount); + while (!isHairPlayable()); + updatePreview(); + } + + void RaceDialog::onSelectNextHair(MyGUI::Widget*) + { + do + mHairIndex = wrap(mHairIndex + 1, mHairCount); + while (!isHairPlayable()); + updatePreview(); + } + + bool RaceDialog::isFacePlayable() + { + std::string prefix = + "b_n_" + mCurrentRaceId + ((mGenderIndex == 0) ? "_m_" : "_f_"); + + std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str(); + + const MWWorld::Store &parts = + MWBase::Environment::get().getWorld()->getStore().get(); + + if (parts.search(prefix + "head_" + headIndex) == 0) + return !(parts.find(prefix + "head" + headIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); + else + return !(parts.find(prefix + "head_" + headIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); + } + + bool RaceDialog::isHairPlayable() + { + std::string prefix = + "b_n_" + mCurrentRaceId + ((mGenderIndex == 0) ? "_m_" : "_f_"); + + std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str(); + + const MWWorld::Store &parts = + MWBase::Environment::get().getWorld()->getStore().get(); + if (parts.search(prefix + "hair_" + hairIndex) == 0) + return !(parts.find(prefix + "hair" + hairIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); + else + return !(parts.find(prefix + "hair_" + hairIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); + } + + void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index) + { + if (_index == MyGUI::ITEM_NONE) + return; + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + const std::string *raceId = mRaceList->getItemDataAt(_index); + if (boost::iequals(mCurrentRaceId, *raceId)) + return; + + mCurrentRaceId = *raceId; + + recountParts(); + + updatePreview(); + updateSkills(); + updateSpellPowers(); + } + + void RaceDialog::recountParts() + { + mFaceCount = countParts("head", mCurrentRaceId, mGenderIndex == 0); + mHairCount = countParts("hair", mCurrentRaceId, mGenderIndex == 0); + + mFaceIndex = 0; + mHairIndex = 0; + + while (!isHairPlayable()) + mHairIndex = wrap(mHairIndex + 1, mHairCount); + while (!isFacePlayable()) + mFaceIndex = wrap(mFaceIndex + 1, mFaceCount); + } + + // update widget content + + void RaceDialog::updatePreview() + { + ESM::NPC record = mPreview->getPrototype(); + record.mRace = mCurrentRaceId; + record.setIsMale(mGenderIndex == 0); + + std::string prefix = + "b_n_" + mCurrentRaceId + ((record.isMale()) ? "_m_" : "_f_"); + + std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str(); + std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str(); + + record.mHead = prefix + "head_" + headIndex; + record.mHair = prefix + "hair_" + hairIndex; + + const MWWorld::Store &parts = + MWBase::Environment::get().getWorld()->getStore().get(); + + if (parts.search(record.mHair) == 0) { + record.mHair = prefix + "hair" + hairIndex; + } + mPreview->setPrototype(record); + } + + void RaceDialog::updateRaces() + { + mRaceList->removeAllItems(); + + const MWWorld::Store &races = + MWBase::Environment::get().getWorld()->getStore().get(); + + + int index = 0; + MWWorld::Store::iterator it = races.begin(); + for (; it != races.end(); ++it) + { + bool playable = it->mData.mFlags & ESM::Race::Playable; + if (!playable) // Only display playable races + continue; + + mRaceList->addItem(it->mName, it->mId); + if (boost::iequals(it->mId, mCurrentRaceId)) + mRaceList->setIndexSelected(index); + ++index; } } - updateSkills(); - updateSpellPowers(); -} - -void RaceDialog::close() -{ - delete mPreview; - mPreview = 0; -} - -// widget controls - -void RaceDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE) - return; - eventDone(this); -} - -void RaceDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} - -void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position) -{ - float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2; - float diff = angle - mCurrentAngle; - mPreview->update (diff); - mCurrentAngle += diff; -} - -void RaceDialog::onSelectPreviousGender(MyGUI::Widget*) -{ - mGenderIndex = wrap(mGenderIndex - 1, 2); - - recountParts(); - updatePreview(); -} - -void RaceDialog::onSelectNextGender(MyGUI::Widget*) -{ - mGenderIndex = wrap(mGenderIndex + 1, 2); - - recountParts(); - updatePreview(); -} - -void RaceDialog::onSelectPreviousFace(MyGUI::Widget*) -{ - do - mFaceIndex = wrap(mFaceIndex - 1, mFaceCount); - while (!isFacePlayable()); - updatePreview(); -} - -void RaceDialog::onSelectNextFace(MyGUI::Widget*) -{ - do - mFaceIndex = wrap(mFaceIndex + 1, mFaceCount); - while (!isFacePlayable()); - updatePreview(); -} - -void RaceDialog::onSelectPreviousHair(MyGUI::Widget*) -{ - do - mHairIndex = wrap(mHairIndex - 1, mHairCount); - while (!isHairPlayable()); - updatePreview(); -} - -void RaceDialog::onSelectNextHair(MyGUI::Widget*) -{ - do - mHairIndex = wrap(mHairIndex + 1, mHairCount); - while (!isHairPlayable()); - updatePreview(); -} - -bool RaceDialog::isFacePlayable() -{ - std::string prefix = - "b_n_" + mCurrentRaceId + ((mGenderIndex == 0) ? "_m_" : "_f_"); - - std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str(); - - const MWWorld::Store &parts = - MWBase::Environment::get().getWorld()->getStore().get(); - - if (parts.search(prefix + "head_" + headIndex) == 0) - return !(parts.find(prefix + "head" + headIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); - else - return !(parts.find(prefix + "head_" + headIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); -} - -bool RaceDialog::isHairPlayable() -{ - std::string prefix = - "b_n_" + mCurrentRaceId + ((mGenderIndex == 0) ? "_m_" : "_f_"); - - std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str(); - - const MWWorld::Store &parts = - MWBase::Environment::get().getWorld()->getStore().get(); - if (parts.search(prefix + "hair_" + hairIndex) == 0) - return !(parts.find(prefix + "hair" + hairIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); - else - return !(parts.find(prefix + "hair_" + hairIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); -} - -void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index) -{ - if (_index == MyGUI::ITEM_NONE) - return; - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - const std::string *raceId = mRaceList->getItemDataAt(_index); - if (boost::iequals(mCurrentRaceId, *raceId)) - return; - - mCurrentRaceId = *raceId; - - recountParts(); - - updatePreview(); - updateSkills(); - updateSpellPowers(); -} - -void RaceDialog::recountParts() -{ - mFaceCount = countParts("head", mCurrentRaceId, mGenderIndex == 0); - mHairCount = countParts("hair", mCurrentRaceId, mGenderIndex == 0); - - mFaceIndex = 0; - mHairIndex = 0; - - while (!isHairPlayable()) - mHairIndex = wrap(mHairIndex + 1, mHairCount); - while (!isFacePlayable()) - mFaceIndex = wrap(mFaceIndex + 1, mFaceCount); -} - -// update widget content - -void RaceDialog::updatePreview() -{ - ESM::NPC record = mPreview->getPrototype(); - record.mRace = mCurrentRaceId; - record.setIsMale(mGenderIndex == 0); - - std::string prefix = - "b_n_" + mCurrentRaceId + ((record.isMale()) ? "_m_" : "_f_"); - - std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str(); - std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str(); - - record.mHead = prefix + "head_" + headIndex; - record.mHair = prefix + "hair_" + hairIndex; - - const MWWorld::Store &parts = - MWBase::Environment::get().getWorld()->getStore().get(); - - if (parts.search(record.mHair) == 0) { - record.mHair = prefix + "hair" + hairIndex; - } - mPreview->setPrototype(record); -} - -void RaceDialog::updateRaces() -{ - mRaceList->removeAllItems(); - - const MWWorld::Store &races = - MWBase::Environment::get().getWorld()->getStore().get(); - - - int index = 0; - MWWorld::Store::iterator it = races.begin(); - for (; it != races.end(); ++it) + void RaceDialog::updateSkills() { - bool playable = it->mData.mFlags & ESM::Race::Playable; - if (!playable) // Only display playable races - continue; + for (std::vector::iterator it = mSkillItems.begin(); it != mSkillItems.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mSkillItems.clear(); - mRaceList->addItem(it->mName, it->mId); - if (boost::iequals(it->mId, mCurrentRaceId)) - mRaceList->setIndexSelected(index); - ++index; - } -} - -void RaceDialog::updateSkills() -{ - for (std::vector::iterator it = mSkillItems.begin(); it != mSkillItems.end(); ++it) - { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - mSkillItems.clear(); - - if (mCurrentRaceId.empty()) - return; - - MWSkillPtr skillWidget; - const int lineHeight = 18; - MyGUI::IntCoord coord1(0, 0, mSkillList->getWidth(), 18); - - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const ESM::Race *race = store.get().find(mCurrentRaceId); - int count = sizeof(race->mData.mBonus)/sizeof(race->mData.mBonus[0]); // TODO: Find a portable macro for this ARRAYSIZE? - for (int i = 0; i < count; ++i) - { - int skillId = race->mData.mBonus[i].mSkill; - if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes - continue; - - skillWidget = mSkillList->createWidget("MW_StatNameValue", coord1, MyGUI::Align::Default, - std::string("Skill") + boost::lexical_cast(i)); - skillWidget->setSkillNumber(skillId); - skillWidget->setSkillValue(MWSkill::SkillValue(race->mData.mBonus[i].mBonus)); - ToolTips::createSkillToolTip(skillWidget, skillId); - - - mSkillItems.push_back(skillWidget); - - coord1.top += lineHeight; - } -} - -void RaceDialog::updateSpellPowers() -{ - for (std::vector::iterator it = mSpellPowerItems.begin(); it != mSpellPowerItems.end(); ++it) - { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - mSpellPowerItems.clear(); - - if (mCurrentRaceId.empty()) - return; - - MWSpellPtr spellPowerWidget; - const int lineHeight = 18; - MyGUI::IntCoord coord(0, 0, mSpellPowerList->getWidth(), 18); - - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const ESM::Race *race = store.get().find(mCurrentRaceId); - - std::vector::const_iterator it = race->mPowers.mList.begin(); - std::vector::const_iterator end = race->mPowers.mList.end(); - for (int i = 0; it != end; ++it) - { - const std::string &spellpower = *it; - spellPowerWidget = mSpellPowerList->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("SpellPower") + boost::lexical_cast(i)); - spellPowerWidget->setSpellId(spellpower); - spellPowerWidget->setUserString("ToolTipType", "Spell"); - spellPowerWidget->setUserString("Spell", spellpower); - - mSpellPowerItems.push_back(spellPowerWidget); - - coord.top += lineHeight; - ++i; + if (mCurrentRaceId.empty()) + return; + + Widgets::MWSkillPtr skillWidget; + const int lineHeight = 18; + MyGUI::IntCoord coord1(0, 0, mSkillList->getWidth(), 18); + + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Race *race = store.get().find(mCurrentRaceId); + int count = sizeof(race->mData.mBonus)/sizeof(race->mData.mBonus[0]); // TODO: Find a portable macro for this ARRAYSIZE? + for (int i = 0; i < count; ++i) + { + int skillId = race->mData.mBonus[i].mSkill; + if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes + continue; + + skillWidget = mSkillList->createWidget("MW_StatNameValue", coord1, MyGUI::Align::Default, + std::string("Skill") + boost::lexical_cast(i)); + skillWidget->setSkillNumber(skillId); + skillWidget->setSkillValue(Widgets::MWSkill::SkillValue(race->mData.mBonus[i].mBonus)); + ToolTips::createSkillToolTip(skillWidget, skillId); + + + mSkillItems.push_back(skillWidget); + + coord1.top += lineHeight; + } } + + void RaceDialog::updateSpellPowers() + { + for (std::vector::iterator it = mSpellPowerItems.begin(); it != mSpellPowerItems.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mSpellPowerItems.clear(); + + if (mCurrentRaceId.empty()) + return; + + Widgets::MWSpellPtr spellPowerWidget; + const int lineHeight = 18; + MyGUI::IntCoord coord(0, 0, mSpellPowerList->getWidth(), 18); + + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Race *race = store.get().find(mCurrentRaceId); + + std::vector::const_iterator it = race->mPowers.mList.begin(); + std::vector::const_iterator end = race->mPowers.mList.end(); + for (int i = 0; it != end; ++it) + { + const std::string &spellpower = *it; + spellPowerWidget = mSpellPowerList->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("SpellPower") + boost::lexical_cast(i)); + spellPowerWidget->setSpellId(spellpower); + spellPowerWidget->setUserString("ToolTipType", "Spell"); + spellPowerWidget->setUserString("Spell", spellpower); + + mSpellPowerItems.push_back(spellPowerWidget); + + coord.top += lineHeight; + ++i; + } + } + } diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 31f5d737b..824929b67 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -11,357 +11,359 @@ #undef min #undef max -using namespace MWGui; -using namespace Widgets; - -const int ReviewDialog::sLineHeight = 18; - -ReviewDialog::ReviewDialog() - : WindowModal("openmw_chargen_review.layout") - , mLastPos(0) +namespace MWGui { - // Centre dialog - center(); - // Setup static stats - MyGUI::Button* button; - getWidget(mNameWidget, "NameText"); - getWidget(button, "NameButton"); - adjustButtonSize(button); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onNameClicked);; + const int ReviewDialog::sLineHeight = 18; - getWidget(mRaceWidget, "RaceText"); - getWidget(button, "RaceButton"); - adjustButtonSize(button); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onRaceClicked);; - - getWidget(mClassWidget, "ClassText"); - getWidget(button, "ClassButton"); - adjustButtonSize(button); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onClassClicked);; - - getWidget(mBirthSignWidget, "SignText"); - getWidget(button, "SignButton"); - adjustButtonSize(button); - button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBirthSignClicked);; - - // Setup dynamic stats - getWidget(mHealth, "Health"); - mHealth->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sHealth", "")); - mHealth->setValue(45, 45); - - getWidget(mMagicka, "Magicka"); - mMagicka->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sMagic", "")); - mMagicka->setValue(50, 50); - - getWidget(mFatigue, "Fatigue"); - mFatigue->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sFatigue", "")); - mFatigue->setValue(160, 160); - - // Setup attributes - - MWAttributePtr attribute; - for (int idx = 0; idx < ESM::Attribute::Length; ++idx) + ReviewDialog::ReviewDialog() + : WindowModal("openmw_chargen_review.layout") + , mLastPos(0) { - getWidget(attribute, std::string("Attribute") + boost::lexical_cast(idx)); - mAttributeWidgets.insert(std::make_pair(static_cast(ESM::Attribute::sAttributeIds[idx]), attribute)); - attribute->setAttributeId(ESM::Attribute::sAttributeIds[idx]); - attribute->setAttributeValue(MWAttribute::AttributeValue(0, 0)); - } + // Centre dialog + center(); - // Setup skills - getWidget(mSkillView, "SkillView"); - mSkillView->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + // Setup static stats + MyGUI::Button* button; + getWidget(mNameWidget, "NameText"); + getWidget(button, "NameButton"); + adjustButtonSize(button); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onNameClicked);; - for (int i = 0; i < ESM::Skill::Length; ++i) - { - mSkillValues.insert(std::make_pair(i, MWMechanics::Stat())); - mSkillWidgetMap.insert(std::make_pair(i, static_cast (0))); - } + getWidget(mRaceWidget, "RaceText"); + getWidget(button, "RaceButton"); + adjustButtonSize(button); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onRaceClicked);; - MyGUI::Button* backButton; - getWidget(backButton, "BackButton"); - backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBackClicked); + getWidget(mClassWidget, "ClassText"); + getWidget(button, "ClassButton"); + adjustButtonSize(button); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onClassClicked);; - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onOkClicked); -} + getWidget(mBirthSignWidget, "SignText"); + getWidget(button, "SignButton"); + adjustButtonSize(button); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBirthSignClicked);; -void ReviewDialog::open() -{ - WindowModal::open(); - updateSkillArea(); -} + // Setup dynamic stats + getWidget(mHealth, "Health"); + mHealth->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sHealth", "")); + mHealth->setValue(45, 45); -void ReviewDialog::setPlayerName(const std::string &name) -{ - mNameWidget->setCaption(name); -} + getWidget(mMagicka, "Magicka"); + mMagicka->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sMagic", "")); + mMagicka->setValue(50, 50); -void ReviewDialog::setRace(const std::string &raceId) -{ - mRaceId = raceId; + getWidget(mFatigue, "Fatigue"); + mFatigue->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sFatigue", "")); + mFatigue->setValue(160, 160); - const ESM::Race *race = - MWBase::Environment::get().getWorld()->getStore().get().search(mRaceId); - if (race) - { - ToolTips::createRaceToolTip(mRaceWidget, race); - mRaceWidget->setCaption(race->mName); - } -} + // Setup attributes -void ReviewDialog::setClass(const ESM::Class& class_) -{ - mKlass = class_; - mClassWidget->setCaption(mKlass.mName); - ToolTips::createClassToolTip(mClassWidget, mKlass); -} - -void ReviewDialog::setBirthSign(const std::string& signId) -{ - mBirthSignId = signId; - - const ESM::BirthSign *sign = - MWBase::Environment::get().getWorld()->getStore().get().search(mBirthSignId); - if (sign) - { - mBirthSignWidget->setCaption(sign->mName); - ToolTips::createBirthsignToolTip(mBirthSignWidget, mBirthSignId); - } -} - -void ReviewDialog::setHealth(const MWMechanics::DynamicStat& value) -{ - mHealth->setValue(value.getCurrent(), value.getModified()); - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - mHealth->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); -} - -void ReviewDialog::setMagicka(const MWMechanics::DynamicStat& value) -{ - mMagicka->setValue(value.getCurrent(), value.getModified()); - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - mMagicka->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); -} - -void ReviewDialog::setFatigue(const MWMechanics::DynamicStat& value) -{ - mFatigue->setValue(value.getCurrent(), value.getModified()); - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - mFatigue->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); -} - -void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat& value) -{ - std::map::iterator attr = mAttributeWidgets.find(static_cast(attributeId)); - if (attr == mAttributeWidgets.end()) - return; - - attr->second->setAttributeValue(value); -} - -void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat& value) -{ - mSkillValues[skillId] = value; - MyGUI::TextBox* widget = mSkillWidgetMap[skillId]; - if (widget) - { - float modified = value.getModified(), base = value.getBase(); - std::string text = boost::lexical_cast(std::floor(modified)); - std::string state = "normal"; - if (modified > base) - state = "increased"; - else if (modified < base) - state = "decreased"; - - widget->setCaption(text); - widget->_setWidgetState(state); - } - -} - -void ReviewDialog::configureSkills(const std::vector& major, const std::vector& minor) -{ - mMajorSkills = major; - mMinorSkills = minor; - - // Update misc skills with the remaining skills not in major or minor - std::set skillSet; - std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); - std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); - boost::array::const_iterator end = ESM::Skill::sSkillIds.end(); - mMiscSkills.clear(); - for (boost::array::const_iterator it = ESM::Skill::sSkillIds.begin(); it != end; ++it) - { - int skill = *it; - if (skillSet.find(skill) == skillSet.end()) - mMiscSkills.push_back(skill); - } - - updateSkillArea(); -} - -void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::ImageBox* separator = mSkillView->createWidget("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default); - separator->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - - mSkillWidgets.push_back(separator); - - coord1.top += separator->getHeight(); - coord2.top += separator->getHeight(); -} - -void ReviewDialog::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox* groupWidget = mSkillView->createWidget("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default); - groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - groupWidget->setCaption(label); - mSkillWidgets.push_back(groupWidget); - - coord1.top += sLineHeight; - coord2.top += sLineHeight; -} - -MyGUI::TextBox* ReviewDialog::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox* skillNameWidget; - MyGUI::TextBox* skillValueWidget; - - skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Default); - skillNameWidget->setCaption(text); - skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - - skillValueWidget = mSkillView->createWidget("SandTextRight", coord2, MyGUI::Align::Top | MyGUI::Align::Right); - skillValueWidget->setCaption(value); - skillValueWidget->_setWidgetState(state); - skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - - mSkillWidgets.push_back(skillNameWidget); - mSkillWidgets.push_back(skillValueWidget); - - coord1.top += sLineHeight; - coord2.top += sLineHeight; - - return skillValueWidget; -} - -void ReviewDialog::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox* skillNameWidget; - - skillNameWidget = mSkillView->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); - skillNameWidget->setCaption(text); - skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - - mSkillWidgets.push_back(skillNameWidget); - - coord1.top += sLineHeight; - coord2.top += sLineHeight; -} - -void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - // Add a line separator if there are items above - if (!mSkillWidgets.empty()) - { - addSeparator(coord1, coord2); - } - - addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2); - - SkillList::const_iterator end = skills.end(); - for (SkillList::const_iterator it = skills.begin(); it != end; ++it) - { - int skillId = *it; - if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes - continue; - assert(skillId >= 0 && skillId < ESM::Skill::Length); - const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; - const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; - float base = stat.getBase(); - float modified = stat.getModified(); - - std::string state = "normal"; - if (modified > base) - state = "increased"; - else if (modified < base) - state = "decreased"; - MyGUI::TextBox* widget = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId), boost::lexical_cast(static_cast(modified)), state, coord1, coord2); - - for (int i=0; i<2; ++i) + Widgets::MWAttributePtr attribute; + for (int idx = 0; idx < ESM::Attribute::Length; ++idx) { - ToolTips::createSkillToolTip(mSkillWidgets[mSkillWidgets.size()-1-i], skillId); + getWidget(attribute, std::string("Attribute") + boost::lexical_cast(idx)); + mAttributeWidgets.insert(std::make_pair(static_cast(ESM::Attribute::sAttributeIds[idx]), attribute)); + attribute->setAttributeId(ESM::Attribute::sAttributeIds[idx]); + attribute->setAttributeValue(Widgets::MWAttribute::AttributeValue(0, 0)); } - mSkillWidgetMap[skillId] = widget; - } -} + // Setup skills + getWidget(mSkillView, "SkillView"); + mSkillView->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); -void ReviewDialog::updateSkillArea() -{ - for (std::vector::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it) + for (int i = 0; i < ESM::Skill::Length; ++i) + { + mSkillValues.insert(std::make_pair(i, MWMechanics::Stat())); + mSkillWidgetMap.insert(std::make_pair(i, static_cast (0))); + } + + MyGUI::Button* backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBackClicked); + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onOkClicked); + } + + void ReviewDialog::open() { - MyGUI::Gui::getInstance().destroyWidget(*it); + WindowModal::open(); + updateSkillArea(); } - mSkillWidgets.clear(); - const int valueSize = 40; - MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18); - MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); + void ReviewDialog::setPlayerName(const std::string &name) + { + mNameWidget->setCaption(name); + } - if (!mMajorSkills.empty()) - addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); + void ReviewDialog::setRace(const std::string &raceId) + { + mRaceId = raceId; - if (!mMinorSkills.empty()) - addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); + const ESM::Race *race = + MWBase::Environment::get().getWorld()->getStore().get().search(mRaceId); + if (race) + { + ToolTips::createRaceToolTip(mRaceWidget, race); + mRaceWidget->setCaption(race->mName); + } + } - if (!mMiscSkills.empty()) - addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); + void ReviewDialog::setClass(const ESM::Class& class_) + { + mKlass = class_; + mClassWidget->setCaption(mKlass.mName); + ToolTips::createClassToolTip(mClassWidget, mKlass); + } - mClientHeight = coord1.top; + void ReviewDialog::setBirthSign(const std::string& signId) + { + mBirthSignId = signId; + + const ESM::BirthSign *sign = + MWBase::Environment::get().getWorld()->getStore().get().search(mBirthSignId); + if (sign) + { + mBirthSignWidget->setCaption(sign->mName); + ToolTips::createBirthsignToolTip(mBirthSignWidget, mBirthSignId); + } + } + + void ReviewDialog::setHealth(const MWMechanics::DynamicStat& value) + { + mHealth->setValue(value.getCurrent(), value.getModified()); + std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + mHealth->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); + } + + void ReviewDialog::setMagicka(const MWMechanics::DynamicStat& value) + { + mMagicka->setValue(value.getCurrent(), value.getModified()); + std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + mMagicka->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); + } + + void ReviewDialog::setFatigue(const MWMechanics::DynamicStat& value) + { + mFatigue->setValue(value.getCurrent(), value.getModified()); + std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + mFatigue->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); + } + + void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat& value) + { + std::map::iterator attr = mAttributeWidgets.find(static_cast(attributeId)); + if (attr == mAttributeWidgets.end()) + return; + + attr->second->setAttributeValue(value); + } + + void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat& value) + { + mSkillValues[skillId] = value; + MyGUI::TextBox* widget = mSkillWidgetMap[skillId]; + if (widget) + { + float modified = value.getModified(), base = value.getBase(); + std::string text = boost::lexical_cast(std::floor(modified)); + std::string state = "normal"; + if (modified > base) + state = "increased"; + else if (modified < base) + state = "decreased"; + + widget->setCaption(text); + widget->_setWidgetState(state); + } + + } + + void ReviewDialog::configureSkills(const std::vector& major, const std::vector& minor) + { + mMajorSkills = major; + mMinorSkills = minor; + + // Update misc skills with the remaining skills not in major or minor + std::set skillSet; + std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); + std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); + boost::array::const_iterator end = ESM::Skill::sSkillIds.end(); + mMiscSkills.clear(); + for (boost::array::const_iterator it = ESM::Skill::sSkillIds.begin(); it != end; ++it) + { + int skill = *it; + if (skillSet.find(skill) == skillSet.end()) + mMiscSkills.push_back(skill); + } + + updateSkillArea(); + } + + void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::ImageBox* separator = mSkillView->createWidget("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default); + separator->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + + mSkillWidgets.push_back(separator); + + coord1.top += separator->getHeight(); + coord2.top += separator->getHeight(); + } + + void ReviewDialog::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox* groupWidget = mSkillView->createWidget("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default); + groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + groupWidget->setCaption(label); + mSkillWidgets.push_back(groupWidget); + + coord1.top += sLineHeight; + coord2.top += sLineHeight; + } + + MyGUI::TextBox* ReviewDialog::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox* skillNameWidget; + MyGUI::TextBox* skillValueWidget; + + skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Default); + skillNameWidget->setCaption(text); + skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + + skillValueWidget = mSkillView->createWidget("SandTextRight", coord2, MyGUI::Align::Top | MyGUI::Align::Right); + skillValueWidget->setCaption(value); + skillValueWidget->_setWidgetState(state); + skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + + mSkillWidgets.push_back(skillNameWidget); + mSkillWidgets.push_back(skillValueWidget); + + coord1.top += sLineHeight; + coord2.top += sLineHeight; + + return skillValueWidget; + } + + void ReviewDialog::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox* skillNameWidget; + + skillNameWidget = mSkillView->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); + skillNameWidget->setCaption(text); + skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + + mSkillWidgets.push_back(skillNameWidget); + + coord1.top += sLineHeight; + coord2.top += sLineHeight; + } + + void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + // Add a line separator if there are items above + if (!mSkillWidgets.empty()) + { + addSeparator(coord1, coord2); + } + + addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2); + + SkillList::const_iterator end = skills.end(); + for (SkillList::const_iterator it = skills.begin(); it != end; ++it) + { + int skillId = *it; + if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes + continue; + assert(skillId >= 0 && skillId < ESM::Skill::Length); + const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; + const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; + float base = stat.getBase(); + float modified = stat.getModified(); + + std::string state = "normal"; + if (modified > base) + state = "increased"; + else if (modified < base) + state = "decreased"; + MyGUI::TextBox* widget = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId), boost::lexical_cast(static_cast(modified)), state, coord1, coord2); + + for (int i=0; i<2; ++i) + { + ToolTips::createSkillToolTip(mSkillWidgets[mSkillWidgets.size()-1-i], skillId); + } + + mSkillWidgetMap[skillId] = widget; + } + } + + void ReviewDialog::updateSkillArea() + { + for (std::vector::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mSkillWidgets.clear(); + + const int valueSize = 40; + MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18); + MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); + + if (!mMajorSkills.empty()) + addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); + + if (!mMinorSkills.empty()) + addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); + + if (!mMiscSkills.empty()) + addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); + + mClientHeight = coord1.top; + + mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); + } + + // widget controls + + void ReviewDialog::onOkClicked(MyGUI::Widget* _sender) + { + eventDone(this); + } + + void ReviewDialog::onBackClicked(MyGUI::Widget* _sender) + { + eventBack(); + } + + void ReviewDialog::onNameClicked(MyGUI::Widget* _sender) + { + eventActivateDialog(NAME_DIALOG); + } + + void ReviewDialog::onRaceClicked(MyGUI::Widget* _sender) + { + eventActivateDialog(RACE_DIALOG); + } + + void ReviewDialog::onClassClicked(MyGUI::Widget* _sender) + { + eventActivateDialog(CLASS_DIALOG); + } + + void ReviewDialog::onBirthSignClicked(MyGUI::Widget* _sender) + { + eventActivateDialog(BIRTHSIGN_DIALOG); + } + + void ReviewDialog::onMouseWheel(MyGUI::Widget* _sender, int _rel) + { + if (mSkillView->getViewOffset().top + _rel*0.3 > 0) + mSkillView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3)); + } - mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); -} - -// widget controls - -void ReviewDialog::onOkClicked(MyGUI::Widget* _sender) -{ - eventDone(this); -} - -void ReviewDialog::onBackClicked(MyGUI::Widget* _sender) -{ - eventBack(); -} - -void ReviewDialog::onNameClicked(MyGUI::Widget* _sender) -{ - eventActivateDialog(NAME_DIALOG); -} - -void ReviewDialog::onRaceClicked(MyGUI::Widget* _sender) -{ - eventActivateDialog(RACE_DIALOG); -} - -void ReviewDialog::onClassClicked(MyGUI::Widget* _sender) -{ - eventActivateDialog(CLASS_DIALOG); -} - -void ReviewDialog::onBirthSignClicked(MyGUI::Widget* _sender) -{ - eventActivateDialog(BIRTHSIGN_DIALOG); -} - -void ReviewDialog::onMouseWheel(MyGUI::Widget* _sender, int _rel) -{ - if (mSkillView->getViewOffset().top + _rel*0.3 > 0) - mSkillView->setViewOffset(MyGUI::IntPoint(0, 0)); - else - mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3)); } diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 1935f7a9b..95bf17635 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -10,71 +10,73 @@ #include "formatting.hpp" -using namespace MWGui; - -ScrollWindow::ScrollWindow () - : WindowBase("openmw_scroll.layout") - , mTakeButtonShow(true) - , mTakeButtonAllowed(true) +namespace MWGui { - getWidget(mTextView, "TextView"); - getWidget(mCloseButton, "CloseButton"); - mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onCloseButtonClicked); + ScrollWindow::ScrollWindow () + : WindowBase("openmw_scroll.layout") + , mTakeButtonShow(true) + , mTakeButtonAllowed(true) + { + getWidget(mTextView, "TextView"); - getWidget(mTakeButton, "TakeButton"); - mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onTakeButtonClicked); + getWidget(mCloseButton, "CloseButton"); + mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onCloseButtonClicked); - center(); -} - -void ScrollWindow::open (MWWorld::Ptr scroll) -{ - // no 3d sounds because the object could be in a container. - MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0); - - mScroll = scroll; - - MWWorld::LiveCellRef *ref = mScroll.get(); - - BookTextParser parser; - MyGUI::IntSize size = parser.parse(ref->mBase->mText, mTextView, 390); - - if (size.height > mTextView->getSize().height) - mTextView->setCanvasSize(MyGUI::IntSize(410, size.height)); - else - mTextView->setCanvasSize(410, mTextView->getSize().height); - - mTextView->setViewOffset(MyGUI::IntPoint(0,0)); - - setTakeButtonShow(true); -} - -void ScrollWindow::setTakeButtonShow(bool show) -{ - mTakeButtonShow = show; - mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); -} - -void ScrollWindow::setInventoryAllowed(bool allowed) -{ - mTakeButtonAllowed = allowed; - mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); -} - -void ScrollWindow::onCloseButtonClicked (MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0); - - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); -} - -void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender) -{ - MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); - - MWWorld::ActionTake take(mScroll); - take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - - MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); + getWidget(mTakeButton, "TakeButton"); + mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onTakeButtonClicked); + + center(); + } + + void ScrollWindow::open (MWWorld::Ptr scroll) + { + // no 3d sounds because the object could be in a container. + MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0); + + mScroll = scroll; + + MWWorld::LiveCellRef *ref = mScroll.get(); + + BookTextParser parser; + MyGUI::IntSize size = parser.parse(ref->mBase->mText, mTextView, 390); + + if (size.height > mTextView->getSize().height) + mTextView->setCanvasSize(MyGUI::IntSize(410, size.height)); + else + mTextView->setCanvasSize(410, mTextView->getSize().height); + + mTextView->setViewOffset(MyGUI::IntPoint(0,0)); + + setTakeButtonShow(true); + } + + void ScrollWindow::setTakeButtonShow(bool show) + { + mTakeButtonShow = show; + mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); + } + + void ScrollWindow::setInventoryAllowed(bool allowed) + { + mTakeButtonAllowed = allowed; + mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed); + } + + void ScrollWindow::onCloseButtonClicked (MyGUI::Widget* _sender) + { + MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0); + + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); + } + + void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender) + { + MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + + MWWorld::ActionTake take(mScroll); + take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); + } } diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 3439ff31c..1134767f1 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -13,562 +13,565 @@ #include "tooltips.hpp" -using namespace MWGui; -const int StatsWindow::sLineHeight = 18; - -StatsWindow::StatsWindow () - : WindowPinnableBase("openmw_stats_window.layout") - , mSkillView(NULL) - , mClientHeight(0) - , mMajorSkills() - , mMinorSkills() - , mMiscSkills() - , mSkillValues() - , mSkillWidgetMap() - , mFactionWidgetMap() - , mFactions() - , mBirthSignId() - , mReputation(0) - , mBounty(0) - , mSkillWidgets() - , mChanged(true) +namespace MWGui { - setCoord(0,0,498, 342); - const char *names[][2] = + const int StatsWindow::sLineHeight = 18; + + StatsWindow::StatsWindow () + : WindowPinnableBase("openmw_stats_window.layout") + , mSkillView(NULL) + , mClientHeight(0) + , mMajorSkills() + , mMinorSkills() + , mMiscSkills() + , mSkillValues() + , mSkillWidgetMap() + , mFactionWidgetMap() + , mFactions() + , mBirthSignId() + , mReputation(0) + , mBounty(0) + , mSkillWidgets() + , mChanged(true) { - { "Attrib1", "sAttributeStrength" }, - { "Attrib2", "sAttributeIntelligence" }, - { "Attrib3", "sAttributeWillpower" }, - { "Attrib4", "sAttributeAgility" }, - { "Attrib5", "sAttributeSpeed" }, - { "Attrib6", "sAttributeEndurance" }, - { "Attrib7", "sAttributePersonality" }, - { "Attrib8", "sAttributeLuck" }, - { 0, 0 } - }; + setCoord(0,0,498, 342); - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - for (int i=0; names[i][0]; ++i) - { - setText (names[i][0], store.get().find (names[i][1])->getString()); - } - - getWidget(mSkillView, "SkillView"); - getWidget(mLeftPane, "LeftPane"); - getWidget(mRightPane, "RightPane"); - - for (int i = 0; i < ESM::Skill::Length; ++i) - { - mSkillValues.insert(std::pair >(i, MWMechanics::Stat())); - mSkillWidgetMap.insert(std::pair(i, (MyGUI::TextBox*)NULL)); - } - - MyGUI::WindowPtr t = static_cast(mMainWidget); - t->eventWindowChangeCoord += MyGUI::newDelegate(this, &StatsWindow::onWindowResize); -} - -void StatsWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) -{ - if (mSkillView->getViewOffset().top + _rel*0.3 > 0) - mSkillView->setViewOffset(MyGUI::IntPoint(0, 0)); - else - mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3)); -} - -void StatsWindow::onWindowResize(MyGUI::Window* window) -{ - mLeftPane->setCoord( MyGUI::IntCoord(0, 0, 0.44*window->getSize().width, window->getSize().height) ); - mRightPane->setCoord( MyGUI::IntCoord(0.44*window->getSize().width, 0, 0.56*window->getSize().width, window->getSize().height) ); - mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); -} - -void StatsWindow::setBar(const std::string& name, const std::string& tname, int val, int max) -{ - MyGUI::ProgressPtr pt; - getWidget(pt, name); - pt->setProgressRange(max); - pt->setProgressPosition(val); - - std::stringstream out; - out << val << "/" << max; - setText(tname, out.str().c_str()); -} - -void StatsWindow::setPlayerName(const std::string& playerName) -{ - static_cast(mMainWidget)->setCaption(playerName); - adjustWindowCaption(); -} - -void StatsWindow::setValue (const std::string& id, const MWMechanics::Stat& value) -{ - static const char *ids[] = - { - "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", - "AttribVal6", "AttribVal7", "AttribVal8", - 0 - }; - - for (int i=0; ids[i]; ++i) - if (ids[i]==id) + const char *names[][2] = { - std::ostringstream valueString; - valueString << value.getModified(); - setText (id, valueString.str()); + { "Attrib1", "sAttributeStrength" }, + { "Attrib2", "sAttributeIntelligence" }, + { "Attrib3", "sAttributeWillpower" }, + { "Attrib4", "sAttributeAgility" }, + { "Attrib5", "sAttributeSpeed" }, + { "Attrib6", "sAttributeEndurance" }, + { "Attrib7", "sAttributePersonality" }, + { "Attrib8", "sAttributeLuck" }, + { 0, 0 } + }; - MyGUI::TextBox* box; - getWidget(box, id); - - if (value.getModified()>value.getBase()) - box->_setWidgetState("increased"); - else if (value.getModified()_setWidgetState("decreased"); - else - box->_setWidgetState("normal"); - - break; + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + for (int i=0; names[i][0]; ++i) + { + setText (names[i][0], store.get().find (names[i][1])->getString()); } -} -void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicStat& value) -{ - static const char *ids[] = - { - "HBar", "MBar", "FBar", - 0 - }; + getWidget(mSkillView, "SkillView"); + getWidget(mLeftPane, "LeftPane"); + getWidget(mRightPane, "RightPane"); - for (int i=0; ids[i]; ++i) - { - if (ids[i]==id) + for (int i = 0; i < ESM::Skill::Length; ++i) { - std::string id (ids[i]); - setBar (id, id + "T", static_cast(value.getCurrent()), static_cast(value.getModified())); + mSkillValues.insert(std::pair >(i, MWMechanics::Stat())); + mSkillWidgetMap.insert(std::pair(i, (MyGUI::TextBox*)NULL)); + } - // health, magicka, fatigue tooltip - MyGUI::Widget* w; - std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - if (i==0) + MyGUI::WindowPtr t = static_cast(mMainWidget); + t->eventWindowChangeCoord += MyGUI::newDelegate(this, &StatsWindow::onWindowResize); + } + + void StatsWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) + { + if (mSkillView->getViewOffset().top + _rel*0.3 > 0) + mSkillView->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3)); + } + + void StatsWindow::onWindowResize(MyGUI::Window* window) + { + mLeftPane->setCoord( MyGUI::IntCoord(0, 0, 0.44*window->getSize().width, window->getSize().height) ); + mRightPane->setCoord( MyGUI::IntCoord(0.44*window->getSize().width, 0, 0.56*window->getSize().width, window->getSize().height) ); + mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); + } + + void StatsWindow::setBar(const std::string& name, const std::string& tname, int val, int max) + { + MyGUI::ProgressPtr pt; + getWidget(pt, name); + pt->setProgressRange(max); + pt->setProgressPosition(val); + + std::stringstream out; + out << val << "/" << max; + setText(tname, out.str().c_str()); + } + + void StatsWindow::setPlayerName(const std::string& playerName) + { + static_cast(mMainWidget)->setCaption(playerName); + adjustWindowCaption(); + } + + void StatsWindow::setValue (const std::string& id, const MWMechanics::Stat& value) + { + static const char *ids[] = + { + "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", + "AttribVal6", "AttribVal7", "AttribVal8", + 0 + }; + + for (int i=0; ids[i]; ++i) + if (ids[i]==id) { - getWidget(w, "Health"); - w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); + std::ostringstream valueString; + valueString << value.getModified(); + setText (id, valueString.str()); + + MyGUI::TextBox* box; + getWidget(box, id); + + if (value.getModified()>value.getBase()) + box->_setWidgetState("increased"); + else if (value.getModified()_setWidgetState("decreased"); + else + box->_setWidgetState("normal"); + + break; } - else if (i==1) + } + + void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicStat& value) + { + static const char *ids[] = + { + "HBar", "MBar", "FBar", + 0 + }; + + for (int i=0; ids[i]; ++i) + { + if (ids[i]==id) { - getWidget(w, "Magicka"); - w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); - } - else if (i==2) - { - getWidget(w, "Fatigue"); - w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); + std::string id (ids[i]); + setBar (id, id + "T", static_cast(value.getCurrent()), static_cast(value.getModified())); + + // health, magicka, fatigue tooltip + MyGUI::Widget* w; + std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); + if (i==0) + { + getWidget(w, "Health"); + w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); + } + else if (i==1) + { + getWidget(w, "Magicka"); + w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); + } + else if (i==2) + { + getWidget(w, "Fatigue"); + w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); + } } } } -} -void StatsWindow::setValue (const std::string& id, const std::string& value) -{ - if (id=="name") - setPlayerName (value); - else if (id=="race") - setText ("RaceText", value); - else if (id=="class") - setText ("ClassText", value); -} - -void StatsWindow::setValue (const std::string& id, int value) -{ - if (id=="level") + void StatsWindow::setValue (const std::string& id, const std::string& value) { - std::ostringstream text; - text << value; - setText("LevelText", text.str()); - } -} - -void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) -{ - mSkillValues[parSkill] = value; - MyGUI::TextBox* widget = mSkillWidgetMap[(int)parSkill]; - if (widget) - { - float modified = value.getModified(), base = value.getBase(); - std::string text = boost::lexical_cast(std::floor(modified)); - std::string state = "normal"; - if (modified > base) - state = "increased"; - else if (modified < base) - state = "decreased"; - - widget->setCaption(text); - widget->_setWidgetState(state); - } -} - -void StatsWindow::configureSkills (const std::vector& major, const std::vector& minor) -{ - mMajorSkills = major; - mMinorSkills = minor; - - // Update misc skills with the remaining skills not in major or minor - std::set skillSet; - std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); - std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); - boost::array::const_iterator end = ESM::Skill::sSkillIds.end(); - mMiscSkills.clear(); - for (boost::array::const_iterator it = ESM::Skill::sSkillIds.begin(); it != end; ++it) - { - int skill = *it; - if (skillSet.find(skill) == skillSet.end()) - mMiscSkills.push_back(skill); + if (id=="name") + setPlayerName (value); + else if (id=="race") + setText ("RaceText", value); + else if (id=="class") + setText ("ClassText", value); } - updateSkillArea(); -} - -void StatsWindow::onFrame () -{ - if (!mMainWidget->getVisible()) - return; - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); - - // level progress - MyGUI::Widget* levelWidget; - for (int i=0; i<2; ++i) + void StatsWindow::setValue (const std::string& id, int value) { - getWidget(levelWidget, i==0 ? "Level_str" : "LevelText"); - levelWidget->setUserString("RangePosition_LevelProgress", boost::lexical_cast(PCstats.getLevelProgress())); - levelWidget->setUserString("Caption_LevelProgressText", boost::lexical_cast(PCstats.getLevelProgress()) + "/10"); + if (id=="level") + { + std::ostringstream text; + text << value; + setText("LevelText", text.str()); + } } - setFactions(PCstats.getFactionRanks()); - setExpelled(PCstats.getExpelled ()); + void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) + { + mSkillValues[parSkill] = value; + MyGUI::TextBox* widget = mSkillWidgetMap[(int)parSkill]; + if (widget) + { + float modified = value.getModified(), base = value.getBase(); + std::string text = boost::lexical_cast(std::floor(modified)); + std::string state = "normal"; + if (modified > base) + state = "increased"; + else if (modified < base) + state = "decreased"; - const std::string &signId = - MWBase::Environment::get().getWorld()->getPlayer().getBirthSign(); + widget->setCaption(text); + widget->_setWidgetState(state); + } + } - setBirthSign(signId); - setReputation (PCstats.getReputation ()); - setBounty (PCstats.getBounty ()); + void StatsWindow::configureSkills (const std::vector& major, const std::vector& minor) + { + mMajorSkills = major; + mMinorSkills = minor; + + // Update misc skills with the remaining skills not in major or minor + std::set skillSet; + std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); + std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); + boost::array::const_iterator end = ESM::Skill::sSkillIds.end(); + mMiscSkills.clear(); + for (boost::array::const_iterator it = ESM::Skill::sSkillIds.begin(); it != end; ++it) + { + int skill = *it; + if (skillSet.find(skill) == skillSet.end()) + mMiscSkills.push_back(skill); + } - if (mChanged) updateSkillArea(); -} - -void StatsWindow::setFactions (const FactionList& factions) -{ - if (mFactions != factions) - { - mFactions = factions; - mChanged = true; - } -} - -void StatsWindow::setExpelled (const std::set& expelled) -{ - if (mExpelled != expelled) - { - mExpelled = expelled; - mChanged = true; - } -} - -void StatsWindow::setBirthSign (const std::string& signId) -{ - if (signId != mBirthSignId) - { - mBirthSignId = signId; - mChanged = true; - } -} - -void StatsWindow::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::ImageBox* separator = mSkillView->createWidget("MW_HLine", - MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), - MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); - separator->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - mSkillWidgets.push_back(separator); - - coord1.top += separator->getHeight(); - coord2.top += separator->getHeight(); -} - -void StatsWindow::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox* groupWidget = mSkillView->createWidget("SandBrightText", - MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), - MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); - groupWidget->setCaption(label); - groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - mSkillWidgets.push_back(groupWidget); - - coord1.top += sLineHeight; - coord2.top += sLineHeight; -} - -MyGUI::TextBox* StatsWindow::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox *skillNameWidget, *skillValueWidget; - - skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); - skillNameWidget->setCaption(text); - skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - - skillValueWidget = mSkillView->createWidget("SandTextRight", coord2, MyGUI::Align::Right | MyGUI::Align::Top); - skillValueWidget->setCaption(value); - skillValueWidget->_setWidgetState(state); - skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - - mSkillWidgets.push_back(skillNameWidget); - mSkillWidgets.push_back(skillValueWidget); - - coord1.top += sLineHeight; - coord2.top += sLineHeight; - - return skillValueWidget; -} - -MyGUI::Widget* StatsWindow::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - MyGUI::TextBox* skillNameWidget; - - skillNameWidget = mSkillView->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); - skillNameWidget->setCaption(text); - skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - - mSkillWidgets.push_back(skillNameWidget); - - coord1.top += sLineHeight; - coord2.top += sLineHeight; - - return skillNameWidget; -} - -void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) -{ - // Add a line separator if there are items above - if (!mSkillWidgets.empty()) - { - addSeparator(coord1, coord2); } - addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2); - - SkillList::const_iterator end = skills.end(); - for (SkillList::const_iterator it = skills.begin(); it != end; ++it) + void StatsWindow::onFrame () { - int skillId = *it; - if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes - continue; - assert(skillId >= 0 && skillId < ESM::Skill::Length); - const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; - const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; - float base = stat.getBase(); - float modified = stat.getModified(); - int progressPercent = (modified - float(static_cast(modified))) * 100; + if (!mMainWidget->getVisible()) + return; - const MWWorld::ESMStore &esmStore = - MWBase::Environment::get().getWorld()->getStore(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); - const ESM::Skill* skill = esmStore.get().find(skillId); - assert(skill); + // level progress + MyGUI::Widget* levelWidget; + for (int i=0; i<2; ++i) + { + getWidget(levelWidget, i==0 ? "Level_str" : "LevelText"); + levelWidget->setUserString("RangePosition_LevelProgress", boost::lexical_cast(PCstats.getLevelProgress())); + levelWidget->setUserString("Caption_LevelProgressText", boost::lexical_cast(PCstats.getLevelProgress()) + "/10"); + } - std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; + setFactions(PCstats.getFactionRanks()); + setExpelled(PCstats.getExpelled ()); - const ESM::Attribute* attr = - esmStore.get().find(skill->mData.mAttribute); - assert(attr); + const std::string &signId = + MWBase::Environment::get().getWorld()->getPlayer().getBirthSign(); - std::string state = "normal"; - if (modified > base) - state = "increased"; - else if (modified < base) - state = "decreased"; - MyGUI::TextBox* widget = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId), - boost::lexical_cast(static_cast(modified)), state, coord1, coord2); + setBirthSign(signId); + setReputation (PCstats.getReputation ()); + setBounty (PCstats.getBounty ()); + + if (mChanged) + updateSkillArea(); + } + + void StatsWindow::setFactions (const FactionList& factions) + { + if (mFactions != factions) + { + mFactions = factions; + mChanged = true; + } + } + + void StatsWindow::setExpelled (const std::set& expelled) + { + if (mExpelled != expelled) + { + mExpelled = expelled; + mChanged = true; + } + } + + void StatsWindow::setBirthSign (const std::string& signId) + { + if (signId != mBirthSignId) + { + mBirthSignId = signId; + mChanged = true; + } + } + + void StatsWindow::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::ImageBox* separator = mSkillView->createWidget("MW_HLine", + MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), + MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); + separator->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + mSkillWidgets.push_back(separator); + + coord1.top += separator->getHeight(); + coord2.top += separator->getHeight(); + } + + void StatsWindow::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox* groupWidget = mSkillView->createWidget("SandBrightText", + MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), + MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); + groupWidget->setCaption(label); + groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + mSkillWidgets.push_back(groupWidget); + + coord1.top += sLineHeight; + coord2.top += sLineHeight; + } + + MyGUI::TextBox* StatsWindow::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox *skillNameWidget, *skillValueWidget; + + skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); + skillNameWidget->setCaption(text); + skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + + skillValueWidget = mSkillView->createWidget("SandTextRight", coord2, MyGUI::Align::Right | MyGUI::Align::Top); + skillValueWidget->setCaption(value); + skillValueWidget->_setWidgetState(state); + skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + + mSkillWidgets.push_back(skillNameWidget); + mSkillWidgets.push_back(skillValueWidget); + + coord1.top += sLineHeight; + coord2.top += sLineHeight; + + return skillValueWidget; + } + + MyGUI::Widget* StatsWindow::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + MyGUI::TextBox* skillNameWidget; + + skillNameWidget = mSkillView->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); + skillNameWidget->setCaption(text); + skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + + mSkillWidgets.push_back(skillNameWidget); + + coord1.top += sLineHeight; + coord2.top += sLineHeight; + + return skillNameWidget; + } + + void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) + { + // Add a line separator if there are items above + if (!mSkillWidgets.empty()) + { + addSeparator(coord1, coord2); + } + + addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2); + + SkillList::const_iterator end = skills.end(); + for (SkillList::const_iterator it = skills.begin(); it != end; ++it) + { + int skillId = *it; + if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes + continue; + assert(skillId >= 0 && skillId < ESM::Skill::Length); + const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; + const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; + float base = stat.getBase(); + float modified = stat.getModified(); + int progressPercent = (modified - float(static_cast(modified))) * 100; + + const MWWorld::ESMStore &esmStore = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::Skill* skill = esmStore.get().find(skillId); + assert(skill); + + std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; + + const ESM::Attribute* attr = + esmStore.get().find(skill->mData.mAttribute); + assert(attr); + + std::string state = "normal"; + if (modified > base) + state = "increased"; + else if (modified < base) + state = "decreased"; + MyGUI::TextBox* widget = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId), + boost::lexical_cast(static_cast(modified)), state, coord1, coord2); + + for (int i=0; i<2; ++i) + { + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "SkillToolTip"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillName", "#{"+skillNameId+"}"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillDescription", skill->mDescription); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillAttribute", "#{sGoverningAttribute}: #{" + attr->mName + "}"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ImageTexture_SkillImage", icon); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillProgressText", boost::lexical_cast(progressPercent)+"/100"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Range_SkillProgress", "100"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("RangePosition_SkillProgress", boost::lexical_cast(progressPercent)); + } + + mSkillWidgetMap[skillId] = widget; + } + } + + void StatsWindow::updateSkillArea() + { + mChanged = false; + + for (std::vector::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + mSkillWidgets.clear(); + + mSkillView->setViewOffset (MyGUI::IntPoint(0,0)); + mClientHeight = 0; + + const int valueSize = 40; + MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18); + MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); + + if (!mMajorSkills.empty()) + addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); + + if (!mMinorSkills.empty()) + addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); + + if (!mMiscSkills.empty()) + addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); + + MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::ESMStore &store = world->getStore(); + const ESM::NPC *player = + world->getPlayer().getPlayer().get()->mBase; + + // race tooltip + const ESM::Race* playerRace = store.get().find(player->mRace); + + MyGUI::Widget* raceWidget; + getWidget(raceWidget, "RaceText"); + ToolTips::createRaceToolTip(raceWidget, playerRace); + getWidget(raceWidget, "Race_str"); + ToolTips::createRaceToolTip(raceWidget, playerRace); + + // class tooltip + MyGUI::Widget* classWidget; + + const ESM::Class *playerClass = + store.get().find(player->mClass); + + getWidget(classWidget, "ClassText"); + ToolTips::createClassToolTip(classWidget, *playerClass); + getWidget(classWidget, "Class_str"); + ToolTips::createClassToolTip(classWidget, *playerClass); + + if (!mFactions.empty()) + { + // Add a line separator if there are items above + if (!mSkillWidgets.empty()) + addSeparator(coord1, coord2); + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); + std::set& expelled = PCstats.getExpelled (); + + addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString("sFaction", "Faction"), coord1, coord2); + FactionList::const_iterator end = mFactions.end(); + for (FactionList::const_iterator it = mFactions.begin(); it != end; ++it) + { + const ESM::Faction *faction = + store.get().find(it->first); + MyGUI::Widget* w = addItem(faction->mName, coord1, coord2); + + std::string text; + + text += std::string("#DDC79E") + faction->mName; + + if (expelled.find(it->first) != expelled.end()) + text += "\n#{sExpelled}"; + else + { + text += std::string("\n#BF9959") + faction->mRanks[it->second]; + + if (it->second < 9) + { + // player doesn't have max rank yet + text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->mRanks[it->second+1]; + + ESM::RankData rankData = faction->mData.mRankData[it->second+1]; + const ESM::Attribute* attr1 = store.get().find(faction->mData.mAttribute[0]); + const ESM::Attribute* attr2 = store.get().find(faction->mData.mAttribute[1]); + assert(attr1 && attr2); + + text += "\n#BF9959#{" + attr1->mName + "}: " + boost::lexical_cast(rankData.mAttribute1) + + ", #{" + attr2->mName + "}: " + boost::lexical_cast(rankData.mAttribute2); + + text += "\n\n#DDC79E#{sFavoriteSkills}"; + text += "\n#BF9959"; + for (int i=0; i<6; ++i) + { + text += "#{"+ESM::Skill::sSkillNameIds[faction->mData.mSkills[i]]+"}"; + if (i<5) + text += ", "; + } + + text += "\n"; + + if (rankData.mSkill1 > 0) + text += "\n#{sNeedOneSkill} " + boost::lexical_cast(rankData.mSkill1); + if (rankData.mSkill2 > 0) + text += "\n#{sNeedTwoSkills} " + boost::lexical_cast(rankData.mSkill2); + } + } + + w->setUserString("ToolTipType", "Layout"); + w->setUserString("ToolTipLayout", "TextToolTip"); + w->setUserString("Caption_Text", text); + } + } + + if (!mBirthSignId.empty()) + { + // Add a line separator if there are items above + if (!mSkillWidgets.empty()) + addSeparator(coord1, coord2); + + addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBirthSign", "Sign"), coord1, coord2); + const ESM::BirthSign *sign = + store.get().find(mBirthSignId); + MyGUI::Widget* w = addItem(sign->mName, coord1, coord2); + + ToolTips::createBirthsignToolTip(w, mBirthSignId); + } + + // Add a line separator if there are items above + if (!mSkillWidgets.empty()) + addSeparator(coord1, coord2); + + addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString("sReputation", "Reputation"), + boost::lexical_cast(static_cast(mReputation)), "normal", coord1, coord2); for (int i=0; i<2; ++i) { mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "SkillToolTip"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillName", "#{"+skillNameId+"}"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillDescription", skill->mDescription); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillAttribute", "#{sGoverningAttribute}: #{" + attr->mName + "}"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ImageTexture_SkillImage", icon); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillProgressText", boost::lexical_cast(progressPercent)+"/100"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Range_SkillProgress", "100"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("RangePosition_SkillProgress", boost::lexical_cast(progressPercent)); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sSkillsMenuReputationHelp}"); } - mSkillWidgetMap[skillId] = widget; - } -} + addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBounty", "Bounty"), + boost::lexical_cast(static_cast(mBounty)), "normal", coord1, coord2); -void StatsWindow::updateSkillArea() -{ - mChanged = false; - - for (std::vector::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it) - { - MyGUI::Gui::getInstance().destroyWidget(*it); - } - mSkillWidgets.clear(); - - mSkillView->setViewOffset (MyGUI::IntPoint(0,0)); - mClientHeight = 0; - - const int valueSize = 40; - MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18); - MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); - - if (!mMajorSkills.empty()) - addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); - - if (!mMinorSkills.empty()) - addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); - - if (!mMiscSkills.empty()) - addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); - - MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::ESMStore &store = world->getStore(); - const ESM::NPC *player = - world->getPlayer().getPlayer().get()->mBase; - - // race tooltip - const ESM::Race* playerRace = store.get().find(player->mRace); - - MyGUI::Widget* raceWidget; - getWidget(raceWidget, "RaceText"); - ToolTips::createRaceToolTip(raceWidget, playerRace); - getWidget(raceWidget, "Race_str"); - ToolTips::createRaceToolTip(raceWidget, playerRace); - - // class tooltip - MyGUI::Widget* classWidget; - - const ESM::Class *playerClass = - store.get().find(player->mClass); - - getWidget(classWidget, "ClassText"); - ToolTips::createClassToolTip(classWidget, *playerClass); - getWidget(classWidget, "Class_str"); - ToolTips::createClassToolTip(classWidget, *playerClass); - - if (!mFactions.empty()) - { - // Add a line separator if there are items above - if (!mSkillWidgets.empty()) - addSeparator(coord1, coord2); - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); - std::set& expelled = PCstats.getExpelled (); - - addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString("sFaction", "Faction"), coord1, coord2); - FactionList::const_iterator end = mFactions.end(); - for (FactionList::const_iterator it = mFactions.begin(); it != end; ++it) + for (int i=0; i<2; ++i) { - const ESM::Faction *faction = - store.get().find(it->first); - MyGUI::Widget* w = addItem(faction->mName, coord1, coord2); - - std::string text; - - text += std::string("#DDC79E") + faction->mName; - - if (expelled.find(it->first) != expelled.end()) - text += "\n#{sExpelled}"; - else - { - text += std::string("\n#BF9959") + faction->mRanks[it->second]; - - if (it->second < 9) - { - // player doesn't have max rank yet - text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->mRanks[it->second+1]; - - ESM::RankData rankData = faction->mData.mRankData[it->second+1]; - const ESM::Attribute* attr1 = store.get().find(faction->mData.mAttribute[0]); - const ESM::Attribute* attr2 = store.get().find(faction->mData.mAttribute[1]); - assert(attr1 && attr2); - - text += "\n#BF9959#{" + attr1->mName + "}: " + boost::lexical_cast(rankData.mAttribute1) - + ", #{" + attr2->mName + "}: " + boost::lexical_cast(rankData.mAttribute2); - - text += "\n\n#DDC79E#{sFavoriteSkills}"; - text += "\n#BF9959"; - for (int i=0; i<6; ++i) - { - text += "#{"+ESM::Skill::sSkillNameIds[faction->mData.mSkills[i]]+"}"; - if (i<5) - text += ", "; - } - - text += "\n"; - - if (rankData.mSkill1 > 0) - text += "\n#{sNeedOneSkill} " + boost::lexical_cast(rankData.mSkill1); - if (rankData.mSkill2 > 0) - text += "\n#{sNeedTwoSkills} " + boost::lexical_cast(rankData.mSkill2); - } - } - - w->setUserString("ToolTipType", "Layout"); - w->setUserString("ToolTipLayout", "TextToolTip"); - w->setUserString("Caption_Text", text); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sCrimeHelp}"); } + + mClientHeight = coord1.top; + + mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); } - if (!mBirthSignId.empty()) + void StatsWindow::onPinToggled() { - // Add a line separator if there are items above - if (!mSkillWidgets.empty()) - addSeparator(coord1, coord2); - - addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBirthSign", "Sign"), coord1, coord2); - const ESM::BirthSign *sign = - store.get().find(mBirthSignId); - MyGUI::Widget* w = addItem(sign->mName, coord1, coord2); - - ToolTips::createBirthsignToolTip(w, mBirthSignId); + MWBase::Environment::get().getWindowManager()->setHMSVisibility(!mPinned); } - - // Add a line separator if there are items above - if (!mSkillWidgets.empty()) - addSeparator(coord1, coord2); - - addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString("sReputation", "Reputation"), - boost::lexical_cast(static_cast(mReputation)), "normal", coord1, coord2); - - for (int i=0; i<2; ++i) - { - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sSkillsMenuReputationHelp}"); - } - - addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString("sBounty", "Bounty"), - boost::lexical_cast(static_cast(mBounty)), "normal", coord1, coord2); - - for (int i=0; i<2; ++i) - { - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); - mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sCrimeHelp}"); - } - - mClientHeight = coord1.top; - - mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); -} - -void StatsWindow::onPinToggled() -{ - MWBase::Environment::get().getWindowManager()->setHMSVisibility(!mPinned); } diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp index ab2936cfc..d4f8a2533 100644 --- a/apps/openmw/mwgui/textinput.cpp +++ b/apps/openmw/mwgui/textinput.cpp @@ -3,68 +3,71 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" -using namespace MWGui; - -TextInputDialog::TextInputDialog() - : WindowModal("openmw_text_input.layout") +namespace MWGui { - // Centre dialog - center(); - getWidget(mTextEdit, "TextEdit"); - mTextEdit->eventEditSelectAccept += newDelegate(this, &TextInputDialog::onTextAccepted); - - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TextInputDialog::onOkClicked); - - // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); -} - -void TextInputDialog::setNextButtonShow(bool shown) -{ - MyGUI::Button* okButton; - getWidget(okButton, "OKButton"); - - if (shown) - okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); - else - okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); -} - -void TextInputDialog::setTextLabel(const std::string &label) -{ - setText("LabelT", label); -} - -void TextInputDialog::open() -{ - WindowModal::open(); - // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); -} - -// widget controls - -void TextInputDialog::onOkClicked(MyGUI::Widget* _sender) -{ - if (mTextEdit->getCaption() == "") + TextInputDialog::TextInputDialog() + : WindowModal("openmw_text_input.layout") { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}"); - MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit); - } - else - eventDone(this); -} + // Centre dialog + center(); -void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender) -{ - if (mTextEdit->getCaption() == "") - { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}"); - MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit); + getWidget(mTextEdit, "TextEdit"); + mTextEdit->eventEditSelectAccept += newDelegate(this, &TextInputDialog::onTextAccepted); + + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TextInputDialog::onOkClicked); + + // Make sure the edit box has focus + MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); } - else - eventDone(this); + + void TextInputDialog::setNextButtonShow(bool shown) + { + MyGUI::Button* okButton; + getWidget(okButton, "OKButton"); + + if (shown) + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", "")); + else + okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + } + + void TextInputDialog::setTextLabel(const std::string &label) + { + setText("LabelT", label); + } + + void TextInputDialog::open() + { + WindowModal::open(); + // Make sure the edit box has focus + MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); + } + + // widget controls + + void TextInputDialog::onOkClicked(MyGUI::Widget* _sender) + { + if (mTextEdit->getCaption() == "") + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}"); + MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit); + } + else + eventDone(this); + } + + void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender) + { + if (mTextEdit->getCaption() == "") + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}"); + MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit); + } + else + eventDone(this); + } + } diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index b72d27aea..d86aa6941 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -9,761 +9,763 @@ #include "mapwindow.hpp" #include "inventorywindow.hpp" -using namespace MWGui; -using namespace MyGUI; - -ToolTips::ToolTips() : - Layout("openmw_tooltips.layout") - , mGameMode(true) - , mFullHelp(false) - , mEnabled(true) - , mFocusToolTipX(0.0) - , mFocusToolTipY(0.0) - , mDelay(0.0) - , mRemainingDelay(0.0) - , mLastMouseX(0) - , mLastMouseY(0) - , mHorizontalScrollIndex(0) -{ - getWidget(mDynamicToolTipBox, "DynamicToolTipBox"); - - mDynamicToolTipBox->setVisible(false); - - // turn off mouse focus so that getMouseFocusWidget returns the correct widget, - // even if the mouse is over the tooltip - mDynamicToolTipBox->setNeedMouseFocus(false); - mMainWidget->setNeedMouseFocus(false); - - mDelay = Settings::Manager::getFloat("tooltip delay", "GUI"); - mRemainingDelay = mDelay; - - for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i) - { - mMainWidget->getChildAt(i)->setVisible(false); - } -} - -void ToolTips::setEnabled(bool enabled) -{ - mEnabled = enabled; -} - -void ToolTips::onFrame(float frameDuration) +namespace MWGui { - while (mDynamicToolTipBox->getChildCount()) + ToolTips::ToolTips() : + Layout("openmw_tooltips.layout") + , mGameMode(true) + , mFullHelp(false) + , mEnabled(true) + , mFocusToolTipX(0.0) + , mFocusToolTipY(0.0) + , mDelay(0.0) + , mRemainingDelay(0.0) + , mLastMouseX(0) + , mLastMouseY(0) + , mHorizontalScrollIndex(0) { - MyGUI::Gui::getInstance().destroyWidget(mDynamicToolTipBox->getChildAt(0)); - } + getWidget(mDynamicToolTipBox, "DynamicToolTipBox"); - // start by hiding everything - for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i) - { - mMainWidget->getChildAt(i)->setVisible(false); - } + mDynamicToolTipBox->setVisible(false); - const IntSize &viewSize = RenderManager::getInstance().getViewSize(); + // turn off mouse focus so that getMouseFocusWidget returns the correct widget, + // even if the mouse is over the tooltip + mDynamicToolTipBox->setNeedMouseFocus(false); + mMainWidget->setNeedMouseFocus(false); - if (!mEnabled) - { - return; - } + mDelay = Settings::Manager::getFloat("tooltip delay", "GUI"); + mRemainingDelay = mDelay; - if (!mGameMode) - { - const MyGUI::IntPoint& mousePos = InputManager::getInstance().getMousePosition(); - - if (MWBase::Environment::get().getWindowManager()->getWorldMouseOver() && ((MWBase::Environment::get().getWindowManager()->getMode() == GM_Console) - || (MWBase::Environment::get().getWindowManager()->getMode() == GM_Container) - || (MWBase::Environment::get().getWindowManager()->getMode() == GM_Inventory))) + for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i) { - mFocusObject = MWBase::Environment::get().getWorld()->getFacedObject(); + mMainWidget->getChildAt(i)->setVisible(false); + } + } - if (mFocusObject.isEmpty ()) - return; + void ToolTips::setEnabled(bool enabled) + { + mEnabled = enabled; + } - const MWWorld::Class& objectclass = MWWorld::Class::get (mFocusObject); + void ToolTips::onFrame(float frameDuration) + { - IntSize tooltipSize; - if ((!objectclass.hasToolTip(mFocusObject))&&(MWBase::Environment::get().getWindowManager()->getMode() == GM_Console)) - { - setCoord(0, 0, 300, 300); - mDynamicToolTipBox->setVisible(true); - ToolTipInfo info; - info.caption=mFocusObject.getCellRef().mRefID; - info.icon=""; - tooltipSize = createToolTip(info); - } - else - tooltipSize = getToolTipViaPtr(true); - - IntPoint tooltipPosition = InputManager::getInstance().getMousePosition() + IntPoint(0, 24); - - // make the tooltip stay completely in the viewport - if ((tooltipPosition.left + tooltipSize.width) > viewSize.width) - { - tooltipPosition.left = viewSize.width - tooltipSize.width; - } - if ((tooltipPosition.top + tooltipSize.height) > viewSize.height) - { - tooltipPosition.top = viewSize.height - tooltipSize.height; - } - - setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); + while (mDynamicToolTipBox->getChildCount()) + { + MyGUI::Gui::getInstance().destroyWidget(mDynamicToolTipBox->getChildAt(0)); } + // start by hiding everything + for (unsigned int i=0; i < mMainWidget->getChildCount(); ++i) + { + mMainWidget->getChildAt(i)->setVisible(false); + } + + const MyGUI::IntSize &viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + + if (!mEnabled) + { + return; + } + + if (!mGameMode) + { + const MyGUI::IntPoint& mousePos = MyGUI::InputManager::getInstance().getMousePosition(); + + if (MWBase::Environment::get().getWindowManager()->getWorldMouseOver() && ((MWBase::Environment::get().getWindowManager()->getMode() == GM_Console) + || (MWBase::Environment::get().getWindowManager()->getMode() == GM_Container) + || (MWBase::Environment::get().getWindowManager()->getMode() == GM_Inventory))) + { + mFocusObject = MWBase::Environment::get().getWorld()->getFacedObject(); + + if (mFocusObject.isEmpty ()) + return; + + const MWWorld::Class& objectclass = MWWorld::Class::get (mFocusObject); + + MyGUI::IntSize tooltipSize; + if ((!objectclass.hasToolTip(mFocusObject))&&(MWBase::Environment::get().getWindowManager()->getMode() == GM_Console)) + { + setCoord(0, 0, 300, 300); + mDynamicToolTipBox->setVisible(true); + ToolTipInfo info; + info.caption=mFocusObject.getCellRef().mRefID; + info.icon=""; + tooltipSize = createToolTip(info); + } + else + tooltipSize = getToolTipViaPtr(true); + + MyGUI::IntPoint tooltipPosition = MyGUI::InputManager::getInstance().getMousePosition() + MyGUI::IntPoint(0, 24); + + // make the tooltip stay completely in the viewport + if ((tooltipPosition.left + tooltipSize.width) > viewSize.width) + { + tooltipPosition.left = viewSize.width - tooltipSize.width; + } + if ((tooltipPosition.top + tooltipSize.height) > viewSize.height) + { + tooltipPosition.top = viewSize.height - tooltipSize.height; + } + + setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); + } + + else + { + const MyGUI::IntPoint& lastPressed = MyGUI::InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); + + if (mousePos == lastPressed) // mouseclick makes tooltip disappear + return; + + if (mousePos.left == mLastMouseX && mousePos.top == mLastMouseY) + { + mRemainingDelay -= frameDuration; + } + else + { + mHorizontalScrollIndex = 0; + mRemainingDelay = mDelay; + } + mLastMouseX = mousePos.left; + mLastMouseY = mousePos.top; + + + if (mRemainingDelay > 0) + return; + + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget(); + if (focus == 0) + return; + + MyGUI::IntSize tooltipSize; + + // try to go 1 level up until there is a widget that has tooltip + // this is necessary because some skin elements are actually separate widgets + int i=0; + while (!focus->isUserString("ToolTipType")) + { + focus = focus->getParent(); + if (!focus) + return; + ++i; + } + + std::string type = focus->getUserString("ToolTipType"); + std::string text = focus->getUserString("ToolTipText"); + + if (type == "") + { + return; + } + + + // special handling for markers on the local map: the tooltip should only be visible + // if the marker is not hidden due to the fog of war. + if (focus->getUserString ("IsMarker") == "true") + { + LocalMapBase::MarkerPosition pos = *focus->getUserData(); + + if (!MWBase::Environment::get().getWorld ()->isPositionExplored (pos.nX, pos.nY, pos.cellX, pos.cellY, pos.interior)) + return; + } + + if (type == "ItemPtr") + { + mFocusObject = *focus->getUserData(); + tooltipSize = getToolTipViaPtr(false); + } + else if (type == "ToolTipInfo") + { + tooltipSize = createToolTip(*focus->getUserData()); + } + else if (type == "AvatarItemSelection") + { + MyGUI::IntCoord avatarPos = MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getAvatarScreenCoord (); + MyGUI::IntPoint relMousePos = MyGUI::InputManager::getInstance ().getMousePosition () - MyGUI::IntPoint(avatarPos.left, avatarPos.top); + int realX = int(float(relMousePos.left) / float(avatarPos.width) * 512.f ); + int realY = int(float(relMousePos.top) / float(avatarPos.height) * 1024.f ); + MWWorld::Ptr item = MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getAvatarSelectedItem (realX, realY); + + mFocusObject = item; + if (!mFocusObject.isEmpty ()) + tooltipSize = getToolTipViaPtr(false); + } + else if (type == "Spell") + { + ToolTipInfo info; + + const ESM::Spell *spell = + MWBase::Environment::get().getWorld()->getStore().get().find(focus->getUserString("Spell")); + info.caption = spell->mName; + Widgets::SpellEffectList effects; + std::vector::const_iterator end = spell->mEffects.mList.end(); + for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != end; ++it) + { + Widgets::SpellEffectParams params; + params.mEffectID = it->mEffectID; + params.mSkill = it->mSkill; + params.mAttribute = it->mAttribute; + params.mDuration = it->mDuration; + params.mMagnMin = it->mMagnMin; + params.mMagnMax = it->mMagnMax; + params.mRange = it->mRange; + params.mIsConstant = (spell->mData.mType == ESM::Spell::ST_Ability); + params.mNoTarget = false; + effects.push_back(params); + } + info.effects = effects; + tooltipSize = createToolTip(info); + } + else if (type == "Layout") + { + // tooltip defined in the layout + MyGUI::Widget* tooltip; + getWidget(tooltip, focus->getUserString("ToolTipLayout")); + + tooltip->setVisible(true); + + std::map userStrings = focus->getUserStrings(); + for (std::map::iterator it = userStrings.begin(); + it != userStrings.end(); ++it) + { + if (it->first == "ToolTipType" + || it->first == "ToolTipLayout" + || it->first == "IsMarker") + continue; + + + size_t underscorePos = it->first.find("_"); + std::string propertyKey = it->first.substr(0, underscorePos); + std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1)); + + MyGUI::Widget* w; + getWidget(w, widgetName); + w->setProperty(propertyKey, it->second); + } + + tooltipSize = tooltip->getSize(); + + tooltip->setCoord(0, 0, tooltipSize.width, tooltipSize.height); + } + else + throw std::runtime_error ("unknown tooltip type"); + + MyGUI::IntPoint tooltipPosition = MyGUI::InputManager::getInstance().getMousePosition() + MyGUI::IntPoint(0, 24); + + // make the tooltip stay completely in the viewport + if ((tooltipPosition.left + tooltipSize.width) > viewSize.width) + { + tooltipPosition.left = viewSize.width - tooltipSize.width; + } + if ((tooltipPosition.top + tooltipSize.height) > viewSize.height) + { + tooltipPosition.top = viewSize.height - tooltipSize.height; + } + + setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); + } + } else { - const MyGUI::IntPoint& lastPressed = InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); - - if (mousePos == lastPressed) // mouseclick makes tooltip disappear - return; - - if (mousePos.left == mLastMouseX && mousePos.top == mLastMouseY) + if (!mFocusObject.isEmpty()) { - mRemainingDelay -= frameDuration; + MyGUI::IntSize tooltipSize = getToolTipViaPtr(); + + setCoord(viewSize.width/2 - tooltipSize.width/2, + std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)), + tooltipSize.width, + tooltipSize.height); + + mDynamicToolTipBox->setVisible(true); } - else - { - mHorizontalScrollIndex = 0; - mRemainingDelay = mDelay; - } - mLastMouseX = mousePos.left; - mLastMouseY = mousePos.top; - - - if (mRemainingDelay > 0) - return; - - Widget* focus = InputManager::getInstance().getMouseFocusWidget(); - if (focus == 0) - return; - - IntSize tooltipSize; - - // try to go 1 level up until there is a widget that has tooltip - // this is necessary because some skin elements are actually separate widgets - int i=0; - while (!focus->isUserString("ToolTipType")) - { - focus = focus->getParent(); - if (!focus) - return; - ++i; - } - - std::string type = focus->getUserString("ToolTipType"); - std::string text = focus->getUserString("ToolTipText"); - - if (type == "") - { - return; - } - - - // special handling for markers on the local map: the tooltip should only be visible - // if the marker is not hidden due to the fog of war. - if (focus->getUserString ("IsMarker") == "true") - { - LocalMapBase::MarkerPosition pos = *focus->getUserData(); - - if (!MWBase::Environment::get().getWorld ()->isPositionExplored (pos.nX, pos.nY, pos.cellX, pos.cellY, pos.interior)) - return; - } - - if (type == "ItemPtr") - { - mFocusObject = *focus->getUserData(); - tooltipSize = getToolTipViaPtr(false); - } - else if (type == "ToolTipInfo") - { - tooltipSize = createToolTip(*focus->getUserData()); - } - else if (type == "AvatarItemSelection") - { - MyGUI::IntCoord avatarPos = MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getAvatarScreenCoord (); - MyGUI::IntPoint relMousePos = MyGUI::InputManager::getInstance ().getMousePosition () - MyGUI::IntPoint(avatarPos.left, avatarPos.top); - int realX = int(float(relMousePos.left) / float(avatarPos.width) * 512.f ); - int realY = int(float(relMousePos.top) / float(avatarPos.height) * 1024.f ); - MWWorld::Ptr item = MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getAvatarSelectedItem (realX, realY); - - mFocusObject = item; - if (!mFocusObject.isEmpty ()) - tooltipSize = getToolTipViaPtr(false); - } - else if (type == "Spell") - { - ToolTipInfo info; - - const ESM::Spell *spell = - MWBase::Environment::get().getWorld()->getStore().get().find(focus->getUserString("Spell")); - info.caption = spell->mName; - Widgets::SpellEffectList effects; - std::vector::const_iterator end = spell->mEffects.mList.end(); - for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != end; ++it) - { - Widgets::SpellEffectParams params; - params.mEffectID = it->mEffectID; - params.mSkill = it->mSkill; - params.mAttribute = it->mAttribute; - params.mDuration = it->mDuration; - params.mMagnMin = it->mMagnMin; - params.mMagnMax = it->mMagnMax; - params.mRange = it->mRange; - params.mIsConstant = (spell->mData.mType == ESM::Spell::ST_Ability); - params.mNoTarget = false; - effects.push_back(params); - } - info.effects = effects; - tooltipSize = createToolTip(info); - } - else if (type == "Layout") - { - // tooltip defined in the layout - MyGUI::Widget* tooltip; - getWidget(tooltip, focus->getUserString("ToolTipLayout")); - - tooltip->setVisible(true); - - std::map userStrings = focus->getUserStrings(); - for (std::map::iterator it = userStrings.begin(); - it != userStrings.end(); ++it) - { - if (it->first == "ToolTipType" - || it->first == "ToolTipLayout" - || it->first == "IsMarker") - continue; - - - size_t underscorePos = it->first.find("_"); - std::string propertyKey = it->first.substr(0, underscorePos); - std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1)); - - MyGUI::Widget* w; - getWidget(w, widgetName); - w->setProperty(propertyKey, it->second); - } - - tooltipSize = tooltip->getSize(); - - tooltip->setCoord(0, 0, tooltipSize.width, tooltipSize.height); - } - else - throw std::runtime_error ("unknown tooltip type"); - - IntPoint tooltipPosition = InputManager::getInstance().getMousePosition() + IntPoint(0, 24); - - // make the tooltip stay completely in the viewport - if ((tooltipPosition.left + tooltipSize.width) > viewSize.width) - { - tooltipPosition.left = viewSize.width - tooltipSize.width; - } - if ((tooltipPosition.top + tooltipSize.height) > viewSize.height) - { - tooltipPosition.top = viewSize.height - tooltipSize.height; - } - - setCoord(tooltipPosition.left, tooltipPosition.top, tooltipSize.width, tooltipSize.height); } } - else + + void ToolTips::enterGameMode() { - if (!mFocusObject.isEmpty()) + mGameMode = true; + } + + void ToolTips::enterGuiMode() + { + mGameMode = false; + } + + void ToolTips::setFocusObject(const MWWorld::Ptr& focus) + { + mFocusObject = focus; + } + + MyGUI::IntSize ToolTips::getToolTipViaPtr (bool image) + { + // this the maximum width of the tooltip before it starts word-wrapping + setCoord(0, 0, 300, 300); + + MyGUI::IntSize tooltipSize; + + const MWWorld::Class& object = MWWorld::Class::get (mFocusObject); + if (!object.hasToolTip(mFocusObject)) + { + mDynamicToolTipBox->setVisible(false); + } + else { - IntSize tooltipSize = getToolTipViaPtr(); - - setCoord(viewSize.width/2 - tooltipSize.width/2, - std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)), - tooltipSize.width, - tooltipSize.height); - mDynamicToolTipBox->setVisible(true); + + ToolTipInfo info = object.getToolTipInfo(mFocusObject); + if (!image) + info.icon = ""; + tooltipSize = createToolTip(info); + } + + return tooltipSize; + } + + void ToolTips::findImageExtension(std::string& image) + { + int len = image.size(); + if (len < 4) return; + + if (!Ogre::ResourceGroupManager::getSingleton().resourceExists(Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, image)) + { + // Change texture extension to .dds + image[len-3] = 'd'; + image[len-2] = 'd'; + image[len-1] = 's'; } } -} -void ToolTips::enterGameMode() -{ - mGameMode = true; -} - -void ToolTips::enterGuiMode() -{ - mGameMode = false; -} - -void ToolTips::setFocusObject(const MWWorld::Ptr& focus) -{ - mFocusObject = focus; -} - -IntSize ToolTips::getToolTipViaPtr (bool image) -{ - // this the maximum width of the tooltip before it starts word-wrapping - setCoord(0, 0, 300, 300); - - IntSize tooltipSize; - - const MWWorld::Class& object = MWWorld::Class::get (mFocusObject); - if (!object.hasToolTip(mFocusObject)) - { - mDynamicToolTipBox->setVisible(false); - } - else + MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) { mDynamicToolTipBox->setVisible(true); - ToolTipInfo info = object.getToolTipInfo(mFocusObject); - if (!image) - info.icon = ""; - tooltipSize = createToolTip(info); - } + std::string caption = info.caption; + std::string image = info.icon; + int imageSize = (image != "") ? info.imageSize : 0; + std::string text = info.text; - return tooltipSize; -} + // remove the first newline (easier this way) + if (text.size() > 0 && text[0] == '\n') + text.erase(0, 1); -void ToolTips::findImageExtension(std::string& image) -{ - int len = image.size(); - if (len < 4) return; + if(caption.size() > 0 && isalnum(caption[0])) + caption[0] = toupper(caption[0]); - if (!Ogre::ResourceGroupManager::getSingleton().resourceExists(Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, image)) - { - // Change texture extension to .dds - image[len-3] = 'd'; - image[len-2] = 'd'; - image[len-1] = 's'; - } -} - -IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) -{ - mDynamicToolTipBox->setVisible(true); - - std::string caption = info.caption; - std::string image = info.icon; - int imageSize = (image != "") ? info.imageSize : 0; - std::string text = info.text; - - // remove the first newline (easier this way) - if (text.size() > 0 && text[0] == '\n') - text.erase(0, 1); - - if(caption.size() > 0 && isalnum(caption[0])) - caption[0] = toupper(caption[0]); - - const ESM::Enchantment* enchant = 0; - const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - if (info.enchant != "") - { - enchant = store.get().find(info.enchant); - if (enchant->mData.mType == ESM::Enchantment::CastOnce) - text += "\n#{sItemCastOnce}"; - else if (enchant->mData.mType == ESM::Enchantment::WhenStrikes) - text += "\n#{sItemCastWhenStrikes}"; - else if (enchant->mData.mType == ESM::Enchantment::WhenUsed) - text += "\n#{sItemCastWhenUsed}"; - else if (enchant->mData.mType == ESM::Enchantment::ConstantEffect) - text += "\n#{sItemCastConstant}"; - } - - // this the maximum width of the tooltip before it starts word-wrapping - setCoord(0, 0, 300, 300); - - const IntPoint padding(8, 8); - - const int maximumWidth = 500; - - const int imageCaptionHPadding = (caption != "" ? 8 : 0); - const int imageCaptionVPadding = (caption != "" ? 4 : 0); - - std::string realImage = "icons\\" + image; - findImageExtension(realImage); - - EditBox* captionWidget = mDynamicToolTipBox->createWidget("NormalText", IntCoord(0, 0, 300, 300), Align::Left | Align::Top, "ToolTipCaption"); - captionWidget->setProperty("Static", "true"); - captionWidget->setCaptionWithReplacing(caption); - IntSize captionSize = captionWidget->getTextSize(); - - int captionHeight = std::max(caption != "" ? captionSize.height : 0, imageSize); - - EditBox* textWidget = mDynamicToolTipBox->createWidget("SandText", IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), Align::Stretch, "ToolTipText"); - textWidget->setProperty("Static", "true"); - textWidget->setProperty("MultiLine", "true"); - textWidget->setProperty("WordWrap", info.wordWrap ? "true" : "false"); - textWidget->setCaptionWithReplacing(text); - textWidget->setTextAlign(Align::HCenter | Align::Top); - IntSize textSize = textWidget->getTextSize(); - - captionSize += IntSize(imageSize, 0); // adjust for image - IntSize totalSize = IntSize( std::min(std::max(textSize.width,captionSize.width + ((image != "") ? imageCaptionHPadding : 0)),maximumWidth), - ((text != "") ? textSize.height + imageCaptionVPadding : 0) + captionHeight ); - - if (!info.effects.empty()) - { - Widget* effectArea = mDynamicToolTipBox->createWidget("", - IntCoord(0, totalSize.height, 300, 300-totalSize.height), - Align::Stretch, "ToolTipEffectArea"); - - IntCoord coord(0, 6, totalSize.width, 24); - - /** - * \todo - * the various potion effects should appear in the tooltip depending if the player - * has enough skill in alchemy to know about the effects of this potion. - */ - - Widgets::MWEffectListPtr effectsWidget = effectArea->createWidget - ("MW_StatName", coord, Align::Default, "ToolTipEffectsWidget"); - effectsWidget->setEffectList(info.effects); - - std::vector effectItems; - effectsWidget->createEffectWidgets(effectItems, effectArea, coord, true, info.isPotion ? Widgets::MWEffectList::EF_NoTarget : 0); - totalSize.height += coord.top-6; - totalSize.width = std::max(totalSize.width, coord.width); - } - - if (info.enchant != "") - { - assert(enchant); - Widget* enchantArea = mDynamicToolTipBox->createWidget("", - IntCoord(0, totalSize.height, 300, 300-totalSize.height), - Align::Stretch, "ToolTipEnchantArea"); - - IntCoord coord(0, 6, totalSize.width, 24); - - Widgets::MWEffectListPtr enchantWidget = enchantArea->createWidget - ("MW_StatName", coord, Align::Default, "ToolTipEnchantWidget"); - enchantWidget->setEffectList(Widgets::MWEffectList::effectListFromESM(&enchant->mEffects)); - - std::vector enchantEffectItems; - int flag = (enchant->mData.mType == ESM::Enchantment::ConstantEffect) ? Widgets::MWEffectList::EF_Constant : 0; - enchantWidget->createEffectWidgets(enchantEffectItems, enchantArea, coord, true, flag); - totalSize.height += coord.top-6; - totalSize.width = std::max(totalSize.width, coord.width); - - if (enchant->mData.mType == ESM::Enchantment::WhenStrikes - || enchant->mData.mType == ESM::Enchantment::WhenUsed) + const ESM::Enchantment* enchant = 0; + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + if (info.enchant != "") { - int maxCharge = enchant->mData.mCharge; - int charge = (info.remainingEnchantCharge == -1) ? maxCharge : info.remainingEnchantCharge; - - const int chargeWidth = 204; - - TextBox* chargeText = enchantArea->createWidget("SandText", IntCoord(0, 0, 10, 18), Align::Default, "ToolTipEnchantChargeText"); - chargeText->setCaptionWithReplacing("#{sCharges}"); - - const int chargeTextWidth = chargeText->getTextSize().width + 5; - - const int chargeAndTextWidth = chargeWidth + chargeTextWidth; - - totalSize.width = std::max(totalSize.width, chargeAndTextWidth); - - chargeText->setCoord((totalSize.width - chargeAndTextWidth)/2, coord.top+6, chargeTextWidth, 18); - - IntCoord chargeCoord; - if (totalSize.width < chargeWidth) - { - totalSize.width = chargeWidth; - chargeCoord = IntCoord(0, coord.top+6, chargeWidth, 18); - } - else - { - chargeCoord = IntCoord((totalSize.width - chargeAndTextWidth)/2 + chargeTextWidth, coord.top+6, chargeWidth, 18); - } - Widgets::MWDynamicStatPtr chargeWidget = enchantArea->createWidget - ("MW_ChargeBar", chargeCoord, Align::Default, "ToolTipEnchantCharge"); - chargeWidget->setValue(charge, charge); - totalSize.height += 24; + enchant = store.get().find(info.enchant); + if (enchant->mData.mType == ESM::Enchantment::CastOnce) + text += "\n#{sItemCastOnce}"; + else if (enchant->mData.mType == ESM::Enchantment::WhenStrikes) + text += "\n#{sItemCastWhenStrikes}"; + else if (enchant->mData.mType == ESM::Enchantment::WhenUsed) + text += "\n#{sItemCastWhenUsed}"; + else if (enchant->mData.mType == ESM::Enchantment::ConstantEffect) + text += "\n#{sItemCastConstant}"; } - } - captionWidget->setCoord( (totalSize.width - captionSize.width)/2 + imageSize, - (captionHeight-captionSize.height)/2, - captionSize.width-imageSize, - captionSize.height); + // this the maximum width of the tooltip before it starts word-wrapping + setCoord(0, 0, 300, 300); - //if its too long we do hscroll with the caption - if (captionSize.width > maximumWidth) - { - mHorizontalScrollIndex = mHorizontalScrollIndex + 2; - if (mHorizontalScrollIndex > captionSize.width){ - mHorizontalScrollIndex = -totalSize.width; - } - int horizontal_scroll = mHorizontalScrollIndex; - if (horizontal_scroll < 40){ - horizontal_scroll = 40; - }else{ - horizontal_scroll = 80 - mHorizontalScrollIndex; - } - captionWidget->setPosition (IntPoint(horizontal_scroll, captionWidget->getPosition().top + padding.top)); - } else { - captionWidget->setPosition (captionWidget->getPosition() + padding); - } + const MyGUI::IntPoint padding(8, 8); - textWidget->setPosition (textWidget->getPosition() + IntPoint(0, padding.top)); // only apply vertical padding, the horizontal works automatically due to Align::HCenter + const int maximumWidth = 500; - if (image != "") - { - ImageBox* imageWidget = mDynamicToolTipBox->createWidget("ImageBox", - IntCoord((totalSize.width - captionSize.width - imageCaptionHPadding)/2, 0, imageSize, imageSize), - Align::Left | Align::Top, "ToolTipImage"); - imageWidget->setImageTexture(realImage); - imageWidget->setPosition (imageWidget->getPosition() + padding); - } + const int imageCaptionHPadding = (caption != "" ? 8 : 0); + const int imageCaptionVPadding = (caption != "" ? 4 : 0); - totalSize += IntSize(padding.left*2, padding.top*2); + std::string realImage = "icons\\" + image; + findImageExtension(realImage); - return totalSize; -} + MyGUI::EditBox* captionWidget = mDynamicToolTipBox->createWidget("NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption"); + captionWidget->setProperty("Static", "true"); + captionWidget->setCaptionWithReplacing(caption); + MyGUI::IntSize captionSize = captionWidget->getTextSize(); -std::string ToolTips::toString(const float value) -{ - std::ostringstream stream; - stream << std::setprecision(3) << value; - return stream.str(); -} + int captionHeight = std::max(caption != "" ? captionSize.height : 0, imageSize); -std::string ToolTips::toString(const int value) -{ - std::ostringstream stream; - stream << value; - return stream.str(); -} + MyGUI::EditBox* textWidget = mDynamicToolTipBox->createWidget("SandText", MyGUI::IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), MyGUI::Align::Stretch, "ToolTipText"); + textWidget->setProperty("Static", "true"); + textWidget->setProperty("MultiLine", "true"); + textWidget->setProperty("WordWrap", info.wordWrap ? "true" : "false"); + textWidget->setCaptionWithReplacing(text); + textWidget->setTextAlign(MyGUI::Align::HCenter | MyGUI::Align::Top); + MyGUI::IntSize textSize = textWidget->getTextSize(); -std::string ToolTips::getValueString(const int value, const std::string& prefix) -{ - if (value == 0) - return ""; - else - return "\n" + prefix + ": " + toString(value); -} + captionSize += MyGUI::IntSize(imageSize, 0); // adjust for image + MyGUI::IntSize totalSize = MyGUI::IntSize( std::min(std::max(textSize.width,captionSize.width + ((image != "") ? imageCaptionHPadding : 0)),maximumWidth), + ((text != "") ? textSize.height + imageCaptionVPadding : 0) + captionHeight ); -std::string ToolTips::getMiscString(const std::string& text, const std::string& prefix) -{ - if (text == "") - return ""; - else - return "\n" + prefix + ": " + text; -} - -std::string ToolTips::getCountString(const int value) -{ - if (value == 1) - return ""; - else - return " (" + boost::lexical_cast(value) + ")"; -} - -void ToolTips::toggleFullHelp() -{ - mFullHelp = !mFullHelp; -} - -bool ToolTips::getFullHelp() const -{ - return mFullHelp; -} - -void ToolTips::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) -{ - mFocusToolTipX = (min_x + max_x) / 2; - mFocusToolTipY = min_y; -} - -void ToolTips::createSkillToolTip(MyGUI::Widget* widget, int skillId) -{ - if (skillId == -1) - return; - - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); - - const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; - const ESM::Skill* skill = store.get().find(skillId); - assert(skill); - - const ESM::Attribute* attr = - store.get().find(skill->mData.mAttribute); - assert(attr); - std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; - - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "SkillNoProgressToolTip"); - widget->setUserString("Caption_SkillNoProgressName", "#{"+skillNameId+"}"); - widget->setUserString("Caption_SkillNoProgressDescription", skill->mDescription); - widget->setUserString("Caption_SkillNoProgressAttribute", "#{sGoverningAttribute}: #{" + attr->mName + "}"); - widget->setUserString("ImageTexture_SkillNoProgressImage", icon); -} - -void ToolTips::createAttributeToolTip(MyGUI::Widget* widget, int attributeId) -{ - if (attributeId == -1) - return; - - std::string icon = ESM::Attribute::sAttributeIcons[attributeId]; - std::string name = ESM::Attribute::sGmstAttributeIds[attributeId]; - std::string desc = ESM::Attribute::sGmstAttributeDescIds[attributeId]; - - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "AttributeToolTip"); - widget->setUserString("Caption_AttributeName", "#{"+name+"}"); - widget->setUserString("Caption_AttributeDescription", "#{"+desc+"}"); - widget->setUserString("ImageTexture_AttributeImage", icon); -} - -void ToolTips::createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId) -{ - widget->setUserString("Caption_CenteredCaption", name); - std::string specText; - // get all skills of this specialisation - const MWWorld::Store &skills = - MWBase::Environment::get().getWorld()->getStore().get(); - - MWWorld::Store::iterator it = skills.begin(); - for (; it != skills.end(); ++it) - { - if (it->mData.mSpecialization == specId) - specText += std::string("\n#{") + ESM::Skill::sSkillNameIds[it->mIndex] + "}"; - } - widget->setUserString("Caption_CenteredCaptionText", specText); - widget->setUserString("ToolTipLayout", "TextWithCenteredCaptionToolTip"); - widget->setUserString("ToolTipType", "Layout"); -} - -void ToolTips::createBirthsignToolTip(MyGUI::Widget* widget, const std::string& birthsignId) -{ - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); - - const ESM::BirthSign *sign = store.get().find(birthsignId); - - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "BirthSignToolTip"); - std::string image = sign->mTexture; - image.replace(image.size()-3, 3, "dds"); - widget->setUserString("ImageTexture_BirthSignImage", "textures\\" + image); - std::string text; - - text += sign->mName; - text += "\n#BF9959" + sign->mDescription; - - std::vector abilities, powers, spells; - - std::vector::const_iterator it = sign->mPowers.mList.begin(); - std::vector::const_iterator end = sign->mPowers.mList.end(); - for (; it != end; ++it) - { - const std::string &spellId = *it; - const ESM::Spell *spell = store.get().search(spellId); - if (!spell) - continue; // Skip spells which cannot be found - ESM::Spell::SpellType type = static_cast(spell->mData.mType); - if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power) - continue; // We only want spell, ability and powers. - - if (type == ESM::Spell::ST_Ability) - abilities.push_back(spellId); - else if (type == ESM::Spell::ST_Power) - powers.push_back(spellId); - else if (type == ESM::Spell::ST_Spell) - spells.push_back(spellId); - } - - struct { - const std::vector &spells; - std::string label; - } - categories[3] = { - {abilities, "sBirthsignmenu1"}, - {powers, "sPowers"}, - {spells, "sBirthsignmenu2"} - }; - - for (int category = 0; category < 3; ++category) - { - for (std::vector::const_iterator it = categories[category].spells.begin(); it != categories[category].spells.end(); ++it) + if (!info.effects.empty()) { - if (it == categories[category].spells.begin()) - { - text += std::string("\n#DDC79E") + std::string("#{") + categories[category].label + "}"; - } + MyGUI::Widget* effectArea = mDynamicToolTipBox->createWidget("", + MyGUI::IntCoord(0, totalSize.height, 300, 300-totalSize.height), + MyGUI::Align::Stretch, "ToolTipEffectArea"); + MyGUI::IntCoord coord(0, 6, totalSize.width, 24); + + /** + * \todo + * the various potion effects should appear in the tooltip depending if the player + * has enough skill in alchemy to know about the effects of this potion. + */ + + Widgets::MWEffectListPtr effectsWidget = effectArea->createWidget + ("MW_StatName", coord, MyGUI::Align::Default, "ToolTipEffectsWidget"); + effectsWidget->setEffectList(info.effects); + + std::vector effectItems; + effectsWidget->createEffectWidgets(effectItems, effectArea, coord, true, info.isPotion ? Widgets::MWEffectList::EF_NoTarget : 0); + totalSize.height += coord.top-6; + totalSize.width = std::max(totalSize.width, coord.width); + } + + if (info.enchant != "") + { + assert(enchant); + MyGUI::Widget* enchantArea = mDynamicToolTipBox->createWidget("", + MyGUI::IntCoord(0, totalSize.height, 300, 300-totalSize.height), + MyGUI::Align::Stretch, "ToolTipEnchantArea"); + + MyGUI::IntCoord coord(0, 6, totalSize.width, 24); + + Widgets::MWEffectListPtr enchantWidget = enchantArea->createWidget + ("MW_StatName", coord, MyGUI::Align::Default, "ToolTipEnchantWidget"); + enchantWidget->setEffectList(Widgets::MWEffectList::effectListFromESM(&enchant->mEffects)); + + std::vector enchantEffectItems; + int flag = (enchant->mData.mType == ESM::Enchantment::ConstantEffect) ? Widgets::MWEffectList::EF_Constant : 0; + enchantWidget->createEffectWidgets(enchantEffectItems, enchantArea, coord, true, flag); + totalSize.height += coord.top-6; + totalSize.width = std::max(totalSize.width, coord.width); + + if (enchant->mData.mType == ESM::Enchantment::WhenStrikes + || enchant->mData.mType == ESM::Enchantment::WhenUsed) + { + int maxCharge = enchant->mData.mCharge; + int charge = (info.remainingEnchantCharge == -1) ? maxCharge : info.remainingEnchantCharge; + + const int chargeWidth = 204; + + MyGUI::TextBox* chargeText = enchantArea->createWidget("SandText", MyGUI::IntCoord(0, 0, 10, 18), MyGUI::Align::Default, "ToolTipEnchantChargeText"); + chargeText->setCaptionWithReplacing("#{sCharges}"); + + const int chargeTextWidth = chargeText->getTextSize().width + 5; + + const int chargeAndTextWidth = chargeWidth + chargeTextWidth; + + totalSize.width = std::max(totalSize.width, chargeAndTextWidth); + + chargeText->setCoord((totalSize.width - chargeAndTextWidth)/2, coord.top+6, chargeTextWidth, 18); + + MyGUI::IntCoord chargeCoord; + if (totalSize.width < chargeWidth) + { + totalSize.width = chargeWidth; + chargeCoord = MyGUI::IntCoord(0, coord.top+6, chargeWidth, 18); + } + else + { + chargeCoord = MyGUI::IntCoord((totalSize.width - chargeAndTextWidth)/2 + chargeTextWidth, coord.top+6, chargeWidth, 18); + } + Widgets::MWDynamicStatPtr chargeWidget = enchantArea->createWidget + ("MW_ChargeBar", chargeCoord, MyGUI::Align::Default, "ToolTipEnchantCharge"); + chargeWidget->setValue(charge, charge); + totalSize.height += 24; + } + } + + captionWidget->setCoord( (totalSize.width - captionSize.width)/2 + imageSize, + (captionHeight-captionSize.height)/2, + captionSize.width-imageSize, + captionSize.height); + + //if its too long we do hscroll with the caption + if (captionSize.width > maximumWidth) + { + mHorizontalScrollIndex = mHorizontalScrollIndex + 2; + if (mHorizontalScrollIndex > captionSize.width){ + mHorizontalScrollIndex = -totalSize.width; + } + int horizontal_scroll = mHorizontalScrollIndex; + if (horizontal_scroll < 40){ + horizontal_scroll = 40; + }else{ + horizontal_scroll = 80 - mHorizontalScrollIndex; + } + captionWidget->setPosition (MyGUI::IntPoint(horizontal_scroll, captionWidget->getPosition().top + padding.top)); + } else { + captionWidget->setPosition (captionWidget->getPosition() + padding); + } + + textWidget->setPosition (textWidget->getPosition() + MyGUI::IntPoint(0, padding.top)); // only apply vertical padding, the horizontal works automatically due to Align::HCenter + + if (image != "") + { + MyGUI::ImageBox* imageWidget = mDynamicToolTipBox->createWidget("ImageBox", + MyGUI::IntCoord((totalSize.width - captionSize.width - imageCaptionHPadding)/2, 0, imageSize, imageSize), + MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipImage"); + imageWidget->setImageTexture(realImage); + imageWidget->setPosition (imageWidget->getPosition() + padding); + } + + totalSize += MyGUI::IntSize(padding.left*2, padding.top*2); + + return totalSize; + } + + std::string ToolTips::toString(const float value) + { + std::ostringstream stream; + stream << std::setprecision(3) << value; + return stream.str(); + } + + std::string ToolTips::toString(const int value) + { + std::ostringstream stream; + stream << value; + return stream.str(); + } + + std::string ToolTips::getValueString(const int value, const std::string& prefix) + { + if (value == 0) + return ""; + else + return "\n" + prefix + ": " + toString(value); + } + + std::string ToolTips::getMiscString(const std::string& text, const std::string& prefix) + { + if (text == "") + return ""; + else + return "\n" + prefix + ": " + text; + } + + std::string ToolTips::getCountString(const int value) + { + if (value == 1) + return ""; + else + return " (" + boost::lexical_cast(value) + ")"; + } + + void ToolTips::toggleFullHelp() + { + mFullHelp = !mFullHelp; + } + + bool ToolTips::getFullHelp() const + { + return mFullHelp; + } + + void ToolTips::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) + { + mFocusToolTipX = (min_x + max_x) / 2; + mFocusToolTipY = min_y; + } + + void ToolTips::createSkillToolTip(MyGUI::Widget* widget, int skillId) + { + if (skillId == -1) + return; + + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; + const ESM::Skill* skill = store.get().find(skillId); + assert(skill); + + const ESM::Attribute* attr = + store.get().find(skill->mData.mAttribute); + assert(attr); + std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; + + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "SkillNoProgressToolTip"); + widget->setUserString("Caption_SkillNoProgressName", "#{"+skillNameId+"}"); + widget->setUserString("Caption_SkillNoProgressDescription", skill->mDescription); + widget->setUserString("Caption_SkillNoProgressAttribute", "#{sGoverningAttribute}: #{" + attr->mName + "}"); + widget->setUserString("ImageTexture_SkillNoProgressImage", icon); + } + + void ToolTips::createAttributeToolTip(MyGUI::Widget* widget, int attributeId) + { + if (attributeId == -1) + return; + + std::string icon = ESM::Attribute::sAttributeIcons[attributeId]; + std::string name = ESM::Attribute::sGmstAttributeIds[attributeId]; + std::string desc = ESM::Attribute::sGmstAttributeDescIds[attributeId]; + + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "AttributeToolTip"); + widget->setUserString("Caption_AttributeName", "#{"+name+"}"); + widget->setUserString("Caption_AttributeDescription", "#{"+desc+"}"); + widget->setUserString("ImageTexture_AttributeImage", icon); + } + + void ToolTips::createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId) + { + widget->setUserString("Caption_CenteredCaption", name); + std::string specText; + // get all skills of this specialisation + const MWWorld::Store &skills = + MWBase::Environment::get().getWorld()->getStore().get(); + + MWWorld::Store::iterator it = skills.begin(); + for (; it != skills.end(); ++it) + { + if (it->mData.mSpecialization == specId) + specText += std::string("\n#{") + ESM::Skill::sSkillNameIds[it->mIndex] + "}"; + } + widget->setUserString("Caption_CenteredCaptionText", specText); + widget->setUserString("ToolTipLayout", "TextWithCenteredCaptionToolTip"); + widget->setUserString("ToolTipType", "Layout"); + } + + void ToolTips::createBirthsignToolTip(MyGUI::Widget* widget, const std::string& birthsignId) + { + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::BirthSign *sign = store.get().find(birthsignId); + + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "BirthSignToolTip"); + std::string image = sign->mTexture; + image.replace(image.size()-3, 3, "dds"); + widget->setUserString("ImageTexture_BirthSignImage", "textures\\" + image); + std::string text; + + text += sign->mName; + text += "\n#BF9959" + sign->mDescription; + + std::vector abilities, powers, spells; + + std::vector::const_iterator it = sign->mPowers.mList.begin(); + std::vector::const_iterator end = sign->mPowers.mList.end(); + for (; it != end; ++it) + { const std::string &spellId = *it; + const ESM::Spell *spell = store.get().search(spellId); + if (!spell) + continue; // Skip spells which cannot be found + ESM::Spell::SpellType type = static_cast(spell->mData.mType); + if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power) + continue; // We only want spell, ability and powers. - const ESM::Spell *spell = store.get().find(spellId); - text += "\n#BF9959" + spell->mName; + if (type == ESM::Spell::ST_Ability) + abilities.push_back(spellId); + else if (type == ESM::Spell::ST_Power) + powers.push_back(spellId); + else if (type == ESM::Spell::ST_Spell) + spells.push_back(spellId); } + + struct { + const std::vector &spells; + std::string label; + } + categories[3] = { + {abilities, "sBirthsignmenu1"}, + {powers, "sPowers"}, + {spells, "sBirthsignmenu2"} + }; + + for (int category = 0; category < 3; ++category) + { + for (std::vector::const_iterator it = categories[category].spells.begin(); it != categories[category].spells.end(); ++it) + { + if (it == categories[category].spells.begin()) + { + text += std::string("\n#DDC79E") + std::string("#{") + categories[category].label + "}"; + } + + const std::string &spellId = *it; + + const ESM::Spell *spell = store.get().find(spellId); + text += "\n#BF9959" + spell->mName; + } + } + + widget->setUserString("Caption_BirthSignText", text); + } + + void ToolTips::createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace) + { + widget->setUserString("Caption_CenteredCaption", playerRace->mName); + widget->setUserString("Caption_CenteredCaptionText", playerRace->mDescription); + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "TextWithCenteredCaptionToolTip"); + } + + void ToolTips::createClassToolTip(MyGUI::Widget* widget, const ESM::Class& playerClass) + { + if (playerClass.mName == "") + return; + + int spec = playerClass.mData.mSpecialization; + std::string specStr; + if (spec == 0) + specStr = "#{sSpecializationCombat}"; + else if (spec == 1) + specStr = "#{sSpecializationMagic}"; + else if (spec == 2) + specStr = "#{sSpecializationStealth}"; + + widget->setUserString("Caption_ClassName", playerClass.mName); + widget->setUserString("Caption_ClassDescription", playerClass.mDescription); + widget->setUserString("Caption_ClassSpecialisation", "#{sSpecialization}: " + specStr); + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "ClassToolTip"); + } + + void ToolTips::createMagicEffectToolTip(MyGUI::Widget* widget, short id) + { + const ESM::MagicEffect* effect = + MWBase::Environment::get().getWorld ()->getStore ().get().find(id); + const std::string &name = ESM::MagicEffect::effectIdToString (id); + + std::string icon = effect->mIcon; + + int slashPos = icon.find("\\"); + icon.insert(slashPos+1, "b_"); + + icon[icon.size()-3] = 'd'; + icon[icon.size()-2] = 'd'; + icon[icon.size()-1] = 's'; + + icon = "icons\\" + icon; + + std::vector schools; + schools.push_back ("#{sSchoolAlteration}"); + schools.push_back ("#{sSchoolConjuration}"); + schools.push_back ("#{sSchoolDestruction}"); + schools.push_back ("#{sSchoolIllusion}"); + schools.push_back ("#{sSchoolMysticism}"); + schools.push_back ("#{sSchoolRestoration}"); + + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "MagicEffectToolTip"); + widget->setUserString("Caption_MagicEffectName", "#{" + name + "}"); + widget->setUserString("Caption_MagicEffectDescription", effect->mDescription); + widget->setUserString("Caption_MagicEffectSchool", "#{sSchool}: " + schools[effect->mData.mSchool]); + widget->setUserString("ImageTexture_MagicEffectImage", icon); + } + + void ToolTips::setDelay(float delay) + { + mDelay = delay; + mRemainingDelay = mDelay; } - widget->setUserString("Caption_BirthSignText", text); -} - -void ToolTips::createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace) -{ - widget->setUserString("Caption_CenteredCaption", playerRace->mName); - widget->setUserString("Caption_CenteredCaptionText", playerRace->mDescription); - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "TextWithCenteredCaptionToolTip"); -} - -void ToolTips::createClassToolTip(MyGUI::Widget* widget, const ESM::Class& playerClass) -{ - if (playerClass.mName == "") - return; - - int spec = playerClass.mData.mSpecialization; - std::string specStr; - if (spec == 0) - specStr = "#{sSpecializationCombat}"; - else if (spec == 1) - specStr = "#{sSpecializationMagic}"; - else if (spec == 2) - specStr = "#{sSpecializationStealth}"; - - widget->setUserString("Caption_ClassName", playerClass.mName); - widget->setUserString("Caption_ClassDescription", playerClass.mDescription); - widget->setUserString("Caption_ClassSpecialisation", "#{sSpecialization}: " + specStr); - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "ClassToolTip"); -} - -void ToolTips::createMagicEffectToolTip(MyGUI::Widget* widget, short id) -{ - const ESM::MagicEffect* effect = - MWBase::Environment::get().getWorld ()->getStore ().get().find(id); - const std::string &name = ESM::MagicEffect::effectIdToString (id); - - std::string icon = effect->mIcon; - - int slashPos = icon.find("\\"); - icon.insert(slashPos+1, "b_"); - - icon[icon.size()-3] = 'd'; - icon[icon.size()-2] = 'd'; - icon[icon.size()-1] = 's'; - - icon = "icons\\" + icon; - - std::vector schools; - schools.push_back ("#{sSchoolAlteration}"); - schools.push_back ("#{sSchoolConjuration}"); - schools.push_back ("#{sSchoolDestruction}"); - schools.push_back ("#{sSchoolIllusion}"); - schools.push_back ("#{sSchoolMysticism}"); - schools.push_back ("#{sSchoolRestoration}"); - - widget->setUserString("ToolTipType", "Layout"); - widget->setUserString("ToolTipLayout", "MagicEffectToolTip"); - widget->setUserString("Caption_MagicEffectName", "#{" + name + "}"); - widget->setUserString("Caption_MagicEffectDescription", effect->mDescription); - widget->setUserString("Caption_MagicEffectSchool", "#{sSchool}: " + schools[effect->mData.mSchool]); - widget->setUserString("ImageTexture_MagicEffectImage", icon); -} - -void ToolTips::setDelay(float delay) -{ - mDelay = delay; - mRemainingDelay = mDelay; } diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index e6c8b1d77..1662c0597 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -12,882 +12,886 @@ #undef min #undef max -using namespace MWGui; -using namespace MWGui::Widgets; - -/* Helper functions */ - -/* - * Fixes the filename of a texture path to use the correct .dds extension. - * This is needed on some ESM entries which point to a .tga file instead. - */ -void MWGui::Widgets::fixTexturePath(std::string &path) +namespace MWGui { - int offset = path.rfind("."); - if (offset < 0) - return; - path.replace(offset, path.length() - offset, ".dds"); -} - -/* MWSkill */ - -MWSkill::MWSkill() - : mSkillId(ESM::Skill::Length) - , mSkillNameWidget(NULL) - , mSkillValueWidget(NULL) -{ -} - -void MWSkill::setSkillId(ESM::Skill::SkillEnum skill) -{ - mSkillId = skill; - updateWidgets(); -} - -void MWSkill::setSkillNumber(int skill) -{ - if (skill < 0) - setSkillId(ESM::Skill::Length); - else if (skill < ESM::Skill::Length) - setSkillId(static_cast(skill)); - else - throw new std::runtime_error("Skill number out of range"); -} - -void MWSkill::setSkillValue(const SkillValue& value) -{ - mValue = value; - updateWidgets(); -} - -void MWSkill::updateWidgets() -{ - if (mSkillNameWidget) + namespace Widgets { - if (mSkillId == ESM::Skill::Length) + + /* Helper functions */ + + /* + * Fixes the filename of a texture path to use the correct .dds extension. + * This is needed on some ESM entries which point to a .tga file instead. + */ + void fixTexturePath(std::string &path) { - static_cast(mSkillNameWidget)->setCaption(""); - } - else - { - const std::string &name = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Skill::sSkillNameIds[mSkillId], ""); - static_cast(mSkillNameWidget)->setCaption(name); - } - } - if (mSkillValueWidget) - { - SkillValue::Type modified = mValue.getModified(), base = mValue.getBase(); - static_cast(mSkillValueWidget)->setCaption(boost::lexical_cast(modified)); - if (modified > base) - mSkillValueWidget->_setWidgetState("increased"); - else if (modified < base) - mSkillValueWidget->_setWidgetState("decreased"); - else - mSkillValueWidget->_setWidgetState("normal"); - } -} - -void MWSkill::onClicked(MyGUI::Widget* _sender) -{ - eventClicked(this); -} - -MWSkill::~MWSkill() -{ -} - -void MWSkill::initialiseOverride() -{ - Base::initialiseOverride(); - - assignWidget(mSkillNameWidget, "StatName"); - assignWidget(mSkillValueWidget, "StatValue"); - - MyGUI::Button* button; - assignWidget(button, "StatNameButton"); - if (button) - { - mSkillNameWidget = button; - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); - } - - button = 0; - assignWidget(button, "StatValueButton"); - if (button) - { - mSkillNameWidget = button; - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); - } -} - -/* MWAttribute */ - -MWAttribute::MWAttribute() - : mId(-1) - , mAttributeNameWidget(NULL) - , mAttributeValueWidget(NULL) -{ -} - -void MWAttribute::setAttributeId(int attributeId) -{ - mId = attributeId; - updateWidgets(); -} - -void MWAttribute::setAttributeValue(const AttributeValue& value) -{ - mValue = value; - updateWidgets(); -} - -void MWAttribute::onClicked(MyGUI::Widget* _sender) -{ - eventClicked(this); -} - -void MWAttribute::updateWidgets() -{ - if (mAttributeNameWidget) - { - if (mId < 0 || mId >= 8) - { - static_cast(mAttributeNameWidget)->setCaption(""); - } - else - { - static const char *attributes[8] = { - "sAttributeStrength", - "sAttributeIntelligence", - "sAttributeWillpower", - "sAttributeAgility", - "sAttributeSpeed", - "sAttributeEndurance", - "sAttributePersonality", - "sAttributeLuck" - }; - const std::string &name = MWBase::Environment::get().getWindowManager()->getGameSettingString(attributes[mId], ""); - static_cast(mAttributeNameWidget)->setCaption(name); - } - } - if (mAttributeValueWidget) - { - AttributeValue::Type modified = mValue.getModified(), base = mValue.getBase(); - static_cast(mAttributeValueWidget)->setCaption(boost::lexical_cast(modified)); - if (modified > base) - mAttributeValueWidget->_setWidgetState("increased"); - else if (modified < base) - mAttributeValueWidget->_setWidgetState("decreased"); - else - mAttributeValueWidget->_setWidgetState("normal"); - } -} - -MWAttribute::~MWAttribute() -{ -} - -void MWAttribute::initialiseOverride() -{ - Base::initialiseOverride(); - - assignWidget(mAttributeNameWidget, "StatName"); - assignWidget(mAttributeValueWidget, "StatValue"); - - MyGUI::Button* button; - assignWidget(button, "StatNameButton"); - if (button) - { - mAttributeNameWidget = button; - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); - } - - button = 0; - assignWidget(button, "StatValueButton"); - if (button) - { - mAttributeValueWidget = button; - button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); - } -} - -/* MWSpell */ - -MWSpell::MWSpell() - : mSpellNameWidget(NULL) -{ -} - -void MWSpell::setSpellId(const std::string &spellId) -{ - mId = spellId; - updateWidgets(); -} - -void MWSpell::createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, int flags) -{ - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); - - const ESM::Spell *spell = store.get().search(mId); - MYGUI_ASSERT(spell, "spell with id '" << mId << "' not found"); - - MWSpellEffectPtr effect = NULL; - std::vector::const_iterator end = spell->mEffects.mList.end(); - for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != end; ++it) - { - effect = creator->createWidget("MW_EffectImage", coord, MyGUI::Align::Default); - SpellEffectParams params; - params.mEffectID = it->mEffectID; - params.mSkill = it->mSkill; - params.mAttribute = it->mAttribute; - params.mDuration = it->mDuration; - params.mMagnMin = it->mMagnMin; - params.mMagnMax = it->mMagnMax; - params.mRange = it->mRange; - params.mIsConstant = (flags & MWEffectList::EF_Constant); - params.mNoTarget = (flags & MWEffectList::EF_NoTarget); - effect->setSpellEffect(params); - effects.push_back(effect); - coord.top += effect->getHeight(); - coord.width = std::max(coord.width, effect->getRequestedWidth()); - } -} - -void MWSpell::updateWidgets() -{ - if (mSpellNameWidget && MWBase::Environment::get().getWindowManager()) - { - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); - - const ESM::Spell *spell = store.get().search(mId); - if (spell) - static_cast(mSpellNameWidget)->setCaption(spell->mName); - else - static_cast(mSpellNameWidget)->setCaption(""); - } -} - -void MWSpell::initialiseOverride() -{ - Base::initialiseOverride(); - - assignWidget(mSpellNameWidget, "StatName"); -} - -MWSpell::~MWSpell() -{ -} - -/* MWEffectList */ - -MWEffectList::MWEffectList() - : mEffectList(0) -{ -} - -void MWEffectList::setEffectList(const SpellEffectList& list) -{ - mEffectList = list; - updateWidgets(); -} - -void MWEffectList::createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, bool center, int flags) -{ - // We don't know the width of all the elements beforehand, so we do it in - // 2 steps: first, create all widgets and check their width.... - MWSpellEffectPtr effect = NULL; - int maxwidth = coord.width; - - for (SpellEffectList::iterator it=mEffectList.begin(); - it != mEffectList.end(); ++it) - { - effect = creator->createWidget("MW_EffectImage", coord, MyGUI::Align::Default); - it->mIsConstant = (flags & EF_Constant) || it->mIsConstant; - it->mNoTarget = (flags & EF_NoTarget) || it->mNoTarget; - effect->setSpellEffect(*it); - effects.push_back(effect); - if (effect->getRequestedWidth() > maxwidth) - maxwidth = effect->getRequestedWidth(); - - coord.top += effect->getHeight(); - } - - // ... then adjust the size for all widgets - for (std::vector::iterator it = effects.begin(); it != effects.end(); ++it) - { - effect = static_cast(*it); - bool needcenter = center && (maxwidth > effect->getRequestedWidth()); - int diff = maxwidth - effect->getRequestedWidth(); - if (needcenter) - { - effect->setCoord(diff/2, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height); - } - else - { - effect->setCoord(0, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height); - } - } - - // inform the parent about width - coord.width = maxwidth; -} - -void MWEffectList::updateWidgets() -{ -} - -void MWEffectList::initialiseOverride() -{ - Base::initialiseOverride(); -} - -MWEffectList::~MWEffectList() -{ -} - -SpellEffectList MWEffectList::effectListFromESM(const ESM::EffectList* effects) -{ - SpellEffectList result; - std::vector::const_iterator end = effects->mList.end(); - for (std::vector::const_iterator it = effects->mList.begin(); it != end; ++it) - { - SpellEffectParams params; - params.mEffectID = it->mEffectID; - params.mSkill = it->mSkill; - params.mAttribute = it->mAttribute; - params.mDuration = it->mDuration; - params.mMagnMin = it->mMagnMin; - params.mMagnMax = it->mMagnMax; - params.mRange = it->mRange; - params.mArea = it->mArea; - result.push_back(params); - } - return result; -} - -/* MWSpellEffect */ - -MWSpellEffect::MWSpellEffect() - : mImageWidget(NULL) - , mTextWidget(NULL) - , mRequestedWidth(0) -{ -} - -void MWSpellEffect::setSpellEffect(const SpellEffectParams& params) -{ - mEffectParams = params; - updateWidgets(); -} - -void MWSpellEffect::updateWidgets() -{ - if (!mEffectParams.mKnown) - { - mTextWidget->setCaption ("?"); - mRequestedWidth = mTextWidget->getTextSize().width + 24; - mImageWidget->setImageTexture (""); - return; - } - - const MWWorld::ESMStore &store = - MWBase::Environment::get().getWorld()->getStore(); - - const ESM::MagicEffect *magicEffect = - store.get().search(mEffectParams.mEffectID); - - assert(magicEffect); - - std::string pt = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", ""); - std::string pts = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoints", ""); - std::string to = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "") + " "; - std::string sec = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("ssecond", ""); - std::string secs = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sseconds", ""); - - std::string effectIDStr = ESM::MagicEffect::effectIdToString(mEffectParams.mEffectID); - std::string spellLine = MWBase::Environment::get().getWindowManager()->getGameSettingString(effectIDStr, ""); - - if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) - { - spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Skill::sSkillNameIds[mEffectParams.mSkill], ""); - } - if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) - { - spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Attribute::sGmstAttributeIds[mEffectParams.mAttribute], ""); - } - - if ((mEffectParams.mMagnMin >= 0 || mEffectParams.mMagnMax >= 0) && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) - { - if (mEffectParams.mMagnMin == mEffectParams.mMagnMax) - spellLine += " " + boost::lexical_cast(mEffectParams.mMagnMin) + " " + ((mEffectParams.mMagnMin == 1) ? pt : pts); - else - { - spellLine += " " + boost::lexical_cast(mEffectParams.mMagnMin) + to + boost::lexical_cast(mEffectParams.mMagnMax) + " " + pts; - } - } - - // constant effects have no duration and no target - if (!mEffectParams.mIsConstant) - { - if (mEffectParams.mDuration >= 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) - { - spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sfor", "") + " " + boost::lexical_cast(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs); + int offset = path.rfind("."); + if (offset < 0) + return; + path.replace(offset, path.length() - offset, ".dds"); } - if (mEffectParams.mArea > 0) + /* MWSkill */ + + MWSkill::MWSkill() + : mSkillId(ESM::Skill::Length) + , mSkillNameWidget(NULL) + , mSkillValueWidget(NULL) { - spellLine += " #{sin} " + boost::lexical_cast(mEffectParams.mArea) + " #{sfootarea}"; } - // potions have no target - if (!mEffectParams.mNoTarget) + void MWSkill::setSkillId(ESM::Skill::SkillEnum skill) { - std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sonword", ""); - if (mEffectParams.mRange == ESM::RT_Self) - spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeSelf", ""); - else if (mEffectParams.mRange == ESM::RT_Touch) - spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeTouch", ""); - else if (mEffectParams.mRange == ESM::RT_Target) - spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeTarget", ""); + mSkillId = skill; + updateWidgets(); } - } - static_cast(mTextWidget)->setCaptionWithReplacing(spellLine); - mRequestedWidth = mTextWidget->getTextSize().width + 24; - - std::string path = std::string("icons\\") + magicEffect->mIcon; - fixTexturePath(path); - mImageWidget->setImageTexture(path); -} - -MWSpellEffect::~MWSpellEffect() -{ -} - -void MWSpellEffect::initialiseOverride() -{ - Base::initialiseOverride(); - - assignWidget(mTextWidget, "Text"); - assignWidget(mImageWidget, "Image"); -} - -/* MWDynamicStat */ - -MWDynamicStat::MWDynamicStat() -: mValue(0) -, mMax(1) -, mTextWidget(NULL) -, mBarWidget(NULL) -, mBarTextWidget(NULL) -{ -} - -void MWDynamicStat::setValue(int cur, int max) -{ - mValue = cur; - mMax = max; - - if (mBarWidget) - { - mBarWidget->setProgressRange(mMax); - mBarWidget->setProgressPosition(mValue); - } - - - if (mBarTextWidget) - { - if (mValue >= 0 && mMax > 0) + void MWSkill::setSkillNumber(int skill) { - std::stringstream out; - out << mValue << "/" << mMax; - static_cast(mBarTextWidget)->setCaption(out.str().c_str()); + if (skill < 0) + setSkillId(ESM::Skill::Length); + else if (skill < ESM::Skill::Length) + setSkillId(static_cast(skill)); + else + throw new std::runtime_error("Skill number out of range"); } - else - static_cast(mBarTextWidget)->setCaption(""); - } -} -void MWDynamicStat::setTitle(const std::string& text) -{ - if (mTextWidget) - static_cast(mTextWidget)->setCaption(text); -} -MWDynamicStat::~MWDynamicStat() -{ -} - -void MWDynamicStat::initialiseOverride() -{ - Base::initialiseOverride(); - - assignWidget(mTextWidget, "Text"); - assignWidget(mBarWidget, "Bar"); - assignWidget(mBarTextWidget, "BarText"); -} - - - - -// --------------------------------------------------------------------------------------------------------------------- - -void AutoSizedWidget::notifySizeChange (MyGUI::Widget* w) -{ - if (w->getParent () != 0) - { - Box* b = dynamic_cast(w->getParent()); - if (b) - b->notifyChildrenSizeChanged (); - else + void MWSkill::setSkillValue(const SkillValue& value) { - if (mExpandDirection == MyGUI::Align::Left) + mValue = value; + updateWidgets(); + } + + void MWSkill::updateWidgets() + { + if (mSkillNameWidget) { - int hdiff = getRequestedSize ().width - w->getSize().width; - w->setPosition(w->getPosition() - MyGUI::IntPoint(hdiff, 0)); + if (mSkillId == ESM::Skill::Length) + { + static_cast(mSkillNameWidget)->setCaption(""); + } + else + { + const std::string &name = MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Skill::sSkillNameIds[mSkillId], ""); + static_cast(mSkillNameWidget)->setCaption(name); + } + } + if (mSkillValueWidget) + { + SkillValue::Type modified = mValue.getModified(), base = mValue.getBase(); + static_cast(mSkillValueWidget)->setCaption(boost::lexical_cast(modified)); + if (modified > base) + mSkillValueWidget->_setWidgetState("increased"); + else if (modified < base) + mSkillValueWidget->_setWidgetState("decreased"); + else + mSkillValueWidget->_setWidgetState("normal"); } - w->setSize(getRequestedSize ()); } - } -} - -MyGUI::IntSize AutoSizedTextBox::getRequestedSize() -{ - return getTextSize(); -} - -void AutoSizedTextBox::setCaption(const MyGUI::UString& _value) -{ - TextBox::setCaption(_value); - - notifySizeChange (this); -} - -void AutoSizedTextBox::setPropertyOverride(const std::string& _key, const std::string& _value) -{ - if (_key == "ExpandDirection") - { - mExpandDirection = MyGUI::Align::parse (_value); - } - else - { - TextBox::setPropertyOverride (_key, _value); - } -} - -MyGUI::IntSize AutoSizedEditBox::getRequestedSize() -{ - if (getAlign().isHStretch()) - throw std::runtime_error("AutoSizedEditBox can't have HStretch align (" + getName() + ")"); - return MyGUI::IntSize(getSize().width, getTextSize().height); -} - -void AutoSizedEditBox::setCaption(const MyGUI::UString& _value) -{ - EditBox::setCaption(_value); - - notifySizeChange (this); -} - -void AutoSizedEditBox::setPropertyOverride(const std::string& _key, const std::string& _value) -{ - if (_key == "ExpandDirection") - { - mExpandDirection = MyGUI::Align::parse (_value); - } - else - { - EditBox::setPropertyOverride (_key, _value); - } -} - - -MyGUI::IntSize AutoSizedButton::getRequestedSize() -{ - MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(24,0); - size.height = std::max(24, size.height); - return size; -} - -void AutoSizedButton::setCaption(const MyGUI::UString& _value) -{ - Button::setCaption(_value); - - notifySizeChange (this); -} - -void AutoSizedButton::setPropertyOverride(const std::string& _key, const std::string& _value) -{ - if (_key == "ExpandDirection") - { - mExpandDirection = MyGUI::Align::parse (_value); - } - else - { - Button::setPropertyOverride (_key, _value); - } -} - -Box::Box() - : mSpacing(4) - , mPadding(0) - , mAutoResize(false) -{ - -} - -void Box::notifyChildrenSizeChanged () -{ - align(); -} - -void Box::_setPropertyImpl(const std::string& _key, const std::string& _value) -{ - if (_key == "Spacing") - mSpacing = MyGUI::utility::parseValue(_value); - else if (_key == "Padding") - mPadding = MyGUI::utility::parseValue(_value); - else if (_key == "AutoResize") - mAutoResize = MyGUI::utility::parseValue(_value); -} - -void HBox::align () -{ - unsigned int count = getChildCount (); - size_t h_stretched_count = 0; - int total_width = 0; - int total_height = 0; - std::vector< std::pair > sizes; - - for (unsigned int i = 0; i < count; ++i) - { - MyGUI::Widget* w = getChildAt(i); - bool hstretch = w->getUserString ("HStretch") == "true"; - h_stretched_count += hstretch; - AutoSizedWidget* aw = dynamic_cast(w); - if (aw) + void MWSkill::onClicked(MyGUI::Widget* _sender) { - sizes.push_back(std::make_pair(aw->getRequestedSize (), hstretch)); - total_width += aw->getRequestedSize ().width; - total_height = std::max(total_height, aw->getRequestedSize ().height); + eventClicked(this); } - else + + MWSkill::~MWSkill() { - sizes.push_back (std::make_pair(w->getSize(), hstretch)); - total_width += w->getSize().width; - if (!(w->getUserString("VStretch") == "true")) - total_height = std::max(total_height, w->getSize().height); } - if (i != count-1) - total_width += mSpacing; - } - - if (mAutoResize && (total_width+mPadding*2 != getSize().width || total_height+mPadding*2 != getSize().height)) - { - setSize(MyGUI::IntSize(total_width+mPadding*2, total_height+mPadding*2)); - return; - } - - - int curX = 0; - for (unsigned int i = 0; i < count; ++i) - { - if (i == 0) - curX += mPadding; - - MyGUI::Widget* w = getChildAt(i); - - bool vstretch = w->getUserString ("VStretch") == "true"; - int height = vstretch ? total_height : sizes[i].first.height; - - MyGUI::IntCoord widgetCoord; - widgetCoord.left = curX; - widgetCoord.top = mPadding + (getSize().height-mPadding*2 - height) / 2; - int width = sizes[i].second ? sizes[i].first.width + (getSize().width-mPadding*2 - total_width)/h_stretched_count - : sizes[i].first.width; - widgetCoord.width = width; - widgetCoord.height = height; - w->setCoord(widgetCoord); - curX += width; - - if (i != count-1) - curX += mSpacing; - } -} - -void HBox::setPropertyOverride(const std::string& _key, const std::string& _value) -{ - Box::_setPropertyImpl (_key, _value); -} - -void HBox::setSize (const MyGUI::IntSize& _value) -{ - MyGUI::Widget::setSize (_value); - align(); -} - -void HBox::setCoord (const MyGUI::IntCoord& _value) -{ - MyGUI::Widget::setCoord (_value); - align(); -} - -void HBox::onWidgetCreated(MyGUI::Widget* _widget) -{ - align(); -} - -MyGUI::IntSize HBox::getRequestedSize () -{ - MyGUI::IntSize size(0,0); - for (unsigned int i = 0; i < getChildCount (); ++i) - { - AutoSizedWidget* w = dynamic_cast(getChildAt(i)); - if (w) + void MWSkill::initialiseOverride() { - MyGUI::IntSize requested = w->getRequestedSize (); - size.height = std::max(size.height, requested.height); - size.width = size.width + requested.width; - if (i != getChildCount()-1) - size.width += mSpacing; + Base::initialiseOverride(); + + assignWidget(mSkillNameWidget, "StatName"); + assignWidget(mSkillValueWidget, "StatValue"); + + MyGUI::Button* button; + assignWidget(button, "StatNameButton"); + if (button) + { + mSkillNameWidget = button; + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); + } + + button = 0; + assignWidget(button, "StatValueButton"); + if (button) + { + mSkillNameWidget = button; + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); + } } - else + + /* MWAttribute */ + + MWAttribute::MWAttribute() + : mId(-1) + , mAttributeNameWidget(NULL) + , mAttributeValueWidget(NULL) { - MyGUI::IntSize requested = getChildAt(i)->getSize (); - size.height = std::max(size.height, requested.height); - - if (getChildAt(i)->getUserString("HStretch") != "true") - size.width = size.width + requested.width; - - if (i != getChildCount()-1) - size.width += mSpacing; } - size.height += mPadding*2; - size.width += mPadding*2; - } - return size; -} - - - -void VBox::align () -{ - unsigned int count = getChildCount (); - size_t v_stretched_count = 0; - int total_height = 0; - int total_width = 0; - std::vector< std::pair > sizes; - for (unsigned int i = 0; i < count; ++i) - { - MyGUI::Widget* w = getChildAt(i); - bool vstretch = w->getUserString ("VStretch") == "true"; - v_stretched_count += vstretch; - AutoSizedWidget* aw = dynamic_cast(w); - if (aw) + void MWAttribute::setAttributeId(int attributeId) { - sizes.push_back(std::make_pair(aw->getRequestedSize (), vstretch)); - total_height += aw->getRequestedSize ().height; - total_width = std::max(total_width, aw->getRequestedSize ().width); + mId = attributeId; + updateWidgets(); } - else + + void MWAttribute::setAttributeValue(const AttributeValue& value) { - sizes.push_back (std::make_pair(w->getSize(), vstretch)); - total_height += w->getSize().height; - - if (!(w->getUserString("HStretch") == "true")) - total_width = std::max(total_width, w->getSize().width); + mValue = value; + updateWidgets(); } - if (i != count-1) - total_height += mSpacing; - } + void MWAttribute::onClicked(MyGUI::Widget* _sender) + { + eventClicked(this); + } - if (mAutoResize && (total_width+mPadding*2 != getSize().width || total_height+mPadding*2 != getSize().height)) - { - setSize(MyGUI::IntSize(total_width+mPadding*2, total_height+mPadding*2)); - return; - } + void MWAttribute::updateWidgets() + { + if (mAttributeNameWidget) + { + if (mId < 0 || mId >= 8) + { + static_cast(mAttributeNameWidget)->setCaption(""); + } + else + { + static const char *attributes[8] = { + "sAttributeStrength", + "sAttributeIntelligence", + "sAttributeWillpower", + "sAttributeAgility", + "sAttributeSpeed", + "sAttributeEndurance", + "sAttributePersonality", + "sAttributeLuck" + }; + const std::string &name = MWBase::Environment::get().getWindowManager()->getGameSettingString(attributes[mId], ""); + static_cast(mAttributeNameWidget)->setCaption(name); + } + } + if (mAttributeValueWidget) + { + AttributeValue::Type modified = mValue.getModified(), base = mValue.getBase(); + static_cast(mAttributeValueWidget)->setCaption(boost::lexical_cast(modified)); + if (modified > base) + mAttributeValueWidget->_setWidgetState("increased"); + else if (modified < base) + mAttributeValueWidget->_setWidgetState("decreased"); + else + mAttributeValueWidget->_setWidgetState("normal"); + } + } + + MWAttribute::~MWAttribute() + { + } + + void MWAttribute::initialiseOverride() + { + Base::initialiseOverride(); + + assignWidget(mAttributeNameWidget, "StatName"); + assignWidget(mAttributeValueWidget, "StatValue"); + + MyGUI::Button* button; + assignWidget(button, "StatNameButton"); + if (button) + { + mAttributeNameWidget = button; + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); + } + + button = 0; + assignWidget(button, "StatValueButton"); + if (button) + { + mAttributeValueWidget = button; + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); + } + } + + /* MWSpell */ + + MWSpell::MWSpell() + : mSpellNameWidget(NULL) + { + } + + void MWSpell::setSpellId(const std::string &spellId) + { + mId = spellId; + updateWidgets(); + } + + void MWSpell::createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, int flags) + { + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::Spell *spell = store.get().search(mId); + MYGUI_ASSERT(spell, "spell with id '" << mId << "' not found"); + + MWSpellEffectPtr effect = NULL; + std::vector::const_iterator end = spell->mEffects.mList.end(); + for (std::vector::const_iterator it = spell->mEffects.mList.begin(); it != end; ++it) + { + effect = creator->createWidget("MW_EffectImage", coord, MyGUI::Align::Default); + SpellEffectParams params; + params.mEffectID = it->mEffectID; + params.mSkill = it->mSkill; + params.mAttribute = it->mAttribute; + params.mDuration = it->mDuration; + params.mMagnMin = it->mMagnMin; + params.mMagnMax = it->mMagnMax; + params.mRange = it->mRange; + params.mIsConstant = (flags & MWEffectList::EF_Constant); + params.mNoTarget = (flags & MWEffectList::EF_NoTarget); + effect->setSpellEffect(params); + effects.push_back(effect); + coord.top += effect->getHeight(); + coord.width = std::max(coord.width, effect->getRequestedWidth()); + } + } + + void MWSpell::updateWidgets() + { + if (mSpellNameWidget && MWBase::Environment::get().getWindowManager()) + { + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::Spell *spell = store.get().search(mId); + if (spell) + static_cast(mSpellNameWidget)->setCaption(spell->mName); + else + static_cast(mSpellNameWidget)->setCaption(""); + } + } + + void MWSpell::initialiseOverride() + { + Base::initialiseOverride(); + + assignWidget(mSpellNameWidget, "StatName"); + } + + MWSpell::~MWSpell() + { + } + + /* MWEffectList */ + + MWEffectList::MWEffectList() + : mEffectList(0) + { + } + + void MWEffectList::setEffectList(const SpellEffectList& list) + { + mEffectList = list; + updateWidgets(); + } + + void MWEffectList::createEffectWidgets(std::vector &effects, MyGUI::Widget* creator, MyGUI::IntCoord &coord, bool center, int flags) + { + // We don't know the width of all the elements beforehand, so we do it in + // 2 steps: first, create all widgets and check their width.... + MWSpellEffectPtr effect = NULL; + int maxwidth = coord.width; + + for (SpellEffectList::iterator it=mEffectList.begin(); + it != mEffectList.end(); ++it) + { + effect = creator->createWidget("MW_EffectImage", coord, MyGUI::Align::Default); + it->mIsConstant = (flags & EF_Constant) || it->mIsConstant; + it->mNoTarget = (flags & EF_NoTarget) || it->mNoTarget; + effect->setSpellEffect(*it); + effects.push_back(effect); + if (effect->getRequestedWidth() > maxwidth) + maxwidth = effect->getRequestedWidth(); + + coord.top += effect->getHeight(); + } + + // ... then adjust the size for all widgets + for (std::vector::iterator it = effects.begin(); it != effects.end(); ++it) + { + effect = static_cast(*it); + bool needcenter = center && (maxwidth > effect->getRequestedWidth()); + int diff = maxwidth - effect->getRequestedWidth(); + if (needcenter) + { + effect->setCoord(diff/2, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height); + } + else + { + effect->setCoord(0, effect->getCoord().top, effect->getRequestedWidth(), effect->getCoord().height); + } + } + + // inform the parent about width + coord.width = maxwidth; + } + + void MWEffectList::updateWidgets() + { + } + + void MWEffectList::initialiseOverride() + { + Base::initialiseOverride(); + } + + MWEffectList::~MWEffectList() + { + } + + SpellEffectList MWEffectList::effectListFromESM(const ESM::EffectList* effects) + { + SpellEffectList result; + std::vector::const_iterator end = effects->mList.end(); + for (std::vector::const_iterator it = effects->mList.begin(); it != end; ++it) + { + SpellEffectParams params; + params.mEffectID = it->mEffectID; + params.mSkill = it->mSkill; + params.mAttribute = it->mAttribute; + params.mDuration = it->mDuration; + params.mMagnMin = it->mMagnMin; + params.mMagnMax = it->mMagnMax; + params.mRange = it->mRange; + params.mArea = it->mArea; + result.push_back(params); + } + return result; + } + + /* MWSpellEffect */ + + MWSpellEffect::MWSpellEffect() + : mImageWidget(NULL) + , mTextWidget(NULL) + , mRequestedWidth(0) + { + } + + void MWSpellEffect::setSpellEffect(const SpellEffectParams& params) + { + mEffectParams = params; + updateWidgets(); + } + + void MWSpellEffect::updateWidgets() + { + if (!mEffectParams.mKnown) + { + mTextWidget->setCaption ("?"); + mRequestedWidth = mTextWidget->getTextSize().width + 24; + mImageWidget->setImageTexture (""); + return; + } + + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::MagicEffect *magicEffect = + store.get().search(mEffectParams.mEffectID); + + assert(magicEffect); + + std::string pt = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", ""); + std::string pts = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoints", ""); + std::string to = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "") + " "; + std::string sec = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("ssecond", ""); + std::string secs = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sseconds", ""); + + std::string effectIDStr = ESM::MagicEffect::effectIdToString(mEffectParams.mEffectID); + std::string spellLine = MWBase::Environment::get().getWindowManager()->getGameSettingString(effectIDStr, ""); + + if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) + { + spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Skill::sSkillNameIds[mEffectParams.mSkill], ""); + } + if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) + { + spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Attribute::sGmstAttributeIds[mEffectParams.mAttribute], ""); + } + + if ((mEffectParams.mMagnMin >= 0 || mEffectParams.mMagnMax >= 0) && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) + { + if (mEffectParams.mMagnMin == mEffectParams.mMagnMax) + spellLine += " " + boost::lexical_cast(mEffectParams.mMagnMin) + " " + ((mEffectParams.mMagnMin == 1) ? pt : pts); + else + { + spellLine += " " + boost::lexical_cast(mEffectParams.mMagnMin) + to + boost::lexical_cast(mEffectParams.mMagnMax) + " " + pts; + } + } + + // constant effects have no duration and no target + if (!mEffectParams.mIsConstant) + { + if (mEffectParams.mDuration >= 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) + { + spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sfor", "") + " " + boost::lexical_cast(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs); + } + + if (mEffectParams.mArea > 0) + { + spellLine += " #{sin} " + boost::lexical_cast(mEffectParams.mArea) + " #{sfootarea}"; + } + + // potions have no target + if (!mEffectParams.mNoTarget) + { + std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sonword", ""); + if (mEffectParams.mRange == ESM::RT_Self) + spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeSelf", ""); + else if (mEffectParams.mRange == ESM::RT_Touch) + spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeTouch", ""); + else if (mEffectParams.mRange == ESM::RT_Target) + spellLine += " " + on + " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sRangeTarget", ""); + } + } + + static_cast(mTextWidget)->setCaptionWithReplacing(spellLine); + mRequestedWidth = mTextWidget->getTextSize().width + 24; + + std::string path = std::string("icons\\") + magicEffect->mIcon; + fixTexturePath(path); + mImageWidget->setImageTexture(path); + } + + MWSpellEffect::~MWSpellEffect() + { + } + + void MWSpellEffect::initialiseOverride() + { + Base::initialiseOverride(); + + assignWidget(mTextWidget, "Text"); + assignWidget(mImageWidget, "Image"); + } + + /* MWDynamicStat */ + + MWDynamicStat::MWDynamicStat() + : mValue(0) + , mMax(1) + , mTextWidget(NULL) + , mBarWidget(NULL) + , mBarTextWidget(NULL) + { + } + + void MWDynamicStat::setValue(int cur, int max) + { + mValue = cur; + mMax = max; + + if (mBarWidget) + { + mBarWidget->setProgressRange(mMax); + mBarWidget->setProgressPosition(mValue); + } - int curY = 0; - for (unsigned int i = 0; i < count; ++i) - { - if (i==0) - curY += mPadding; + if (mBarTextWidget) + { + if (mValue >= 0 && mMax > 0) + { + std::stringstream out; + out << mValue << "/" << mMax; + static_cast(mBarTextWidget)->setCaption(out.str().c_str()); + } + else + static_cast(mBarTextWidget)->setCaption(""); + } + } + void MWDynamicStat::setTitle(const std::string& text) + { + if (mTextWidget) + static_cast(mTextWidget)->setCaption(text); + } - MyGUI::Widget* w = getChildAt(i); + MWDynamicStat::~MWDynamicStat() + { + } - bool hstretch = w->getUserString ("HStretch") == "true"; - int width = hstretch ? total_width : sizes[i].first.width; + void MWDynamicStat::initialiseOverride() + { + Base::initialiseOverride(); - MyGUI::IntCoord widgetCoord; - widgetCoord.top = curY; - widgetCoord.left = mPadding + (getSize().width-mPadding*2 - width) / 2; - int height = sizes[i].second ? sizes[i].first.height + (getSize().height-mPadding*2 - total_height)/v_stretched_count - : sizes[i].first.height; - widgetCoord.height = height; - widgetCoord.width = width; - w->setCoord(widgetCoord); - curY += height; + assignWidget(mTextWidget, "Text"); + assignWidget(mBarWidget, "Bar"); + assignWidget(mBarTextWidget, "BarText"); + } - if (i != count-1) - curY += mSpacing; + + + + // --------------------------------------------------------------------------------------------------------------------- + + void AutoSizedWidget::notifySizeChange (MyGUI::Widget* w) + { + if (w->getParent () != 0) + { + Box* b = dynamic_cast(w->getParent()); + if (b) + b->notifyChildrenSizeChanged (); + else + { + if (mExpandDirection == MyGUI::Align::Left) + { + int hdiff = getRequestedSize ().width - w->getSize().width; + w->setPosition(w->getPosition() - MyGUI::IntPoint(hdiff, 0)); + } + w->setSize(getRequestedSize ()); + } + } + } + + + MyGUI::IntSize AutoSizedTextBox::getRequestedSize() + { + return getTextSize(); + } + + void AutoSizedTextBox::setCaption(const MyGUI::UString& _value) + { + TextBox::setCaption(_value); + + notifySizeChange (this); + } + + void AutoSizedTextBox::setPropertyOverride(const std::string& _key, const std::string& _value) + { + if (_key == "ExpandDirection") + { + mExpandDirection = MyGUI::Align::parse (_value); + } + else + { + TextBox::setPropertyOverride (_key, _value); + } + } + + MyGUI::IntSize AutoSizedEditBox::getRequestedSize() + { + if (getAlign().isHStretch()) + throw std::runtime_error("AutoSizedEditBox can't have HStretch align (" + getName() + ")"); + return MyGUI::IntSize(getSize().width, getTextSize().height); + } + + void AutoSizedEditBox::setCaption(const MyGUI::UString& _value) + { + EditBox::setCaption(_value); + + notifySizeChange (this); + } + + void AutoSizedEditBox::setPropertyOverride(const std::string& _key, const std::string& _value) + { + if (_key == "ExpandDirection") + { + mExpandDirection = MyGUI::Align::parse (_value); + } + else + { + EditBox::setPropertyOverride (_key, _value); + } + } + + + MyGUI::IntSize AutoSizedButton::getRequestedSize() + { + MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(24,0); + size.height = std::max(24, size.height); + return size; + } + + void AutoSizedButton::setCaption(const MyGUI::UString& _value) + { + Button::setCaption(_value); + + notifySizeChange (this); + } + + void AutoSizedButton::setPropertyOverride(const std::string& _key, const std::string& _value) + { + if (_key == "ExpandDirection") + { + mExpandDirection = MyGUI::Align::parse (_value); + } + else + { + Button::setPropertyOverride (_key, _value); + } + } + + Box::Box() + : mSpacing(4) + , mPadding(0) + , mAutoResize(false) + { + + } + + void Box::notifyChildrenSizeChanged () + { + align(); + } + + void Box::_setPropertyImpl(const std::string& _key, const std::string& _value) + { + if (_key == "Spacing") + mSpacing = MyGUI::utility::parseValue(_value); + else if (_key == "Padding") + mPadding = MyGUI::utility::parseValue(_value); + else if (_key == "AutoResize") + mAutoResize = MyGUI::utility::parseValue(_value); + } + + void HBox::align () + { + unsigned int count = getChildCount (); + size_t h_stretched_count = 0; + int total_width = 0; + int total_height = 0; + std::vector< std::pair > sizes; + + for (unsigned int i = 0; i < count; ++i) + { + MyGUI::Widget* w = getChildAt(i); + bool hstretch = w->getUserString ("HStretch") == "true"; + h_stretched_count += hstretch; + AutoSizedWidget* aw = dynamic_cast(w); + if (aw) + { + sizes.push_back(std::make_pair(aw->getRequestedSize (), hstretch)); + total_width += aw->getRequestedSize ().width; + total_height = std::max(total_height, aw->getRequestedSize ().height); + } + else + { + sizes.push_back (std::make_pair(w->getSize(), hstretch)); + total_width += w->getSize().width; + if (!(w->getUserString("VStretch") == "true")) + total_height = std::max(total_height, w->getSize().height); + } + + if (i != count-1) + total_width += mSpacing; + } + + if (mAutoResize && (total_width+mPadding*2 != getSize().width || total_height+mPadding*2 != getSize().height)) + { + setSize(MyGUI::IntSize(total_width+mPadding*2, total_height+mPadding*2)); + return; + } + + + int curX = 0; + for (unsigned int i = 0; i < count; ++i) + { + if (i == 0) + curX += mPadding; + + MyGUI::Widget* w = getChildAt(i); + + bool vstretch = w->getUserString ("VStretch") == "true"; + int height = vstretch ? total_height : sizes[i].first.height; + + MyGUI::IntCoord widgetCoord; + widgetCoord.left = curX; + widgetCoord.top = mPadding + (getSize().height-mPadding*2 - height) / 2; + int width = sizes[i].second ? sizes[i].first.width + (getSize().width-mPadding*2 - total_width)/h_stretched_count + : sizes[i].first.width; + widgetCoord.width = width; + widgetCoord.height = height; + w->setCoord(widgetCoord); + curX += width; + + if (i != count-1) + curX += mSpacing; + } + } + + void HBox::setPropertyOverride(const std::string& _key, const std::string& _value) + { + Box::_setPropertyImpl (_key, _value); + } + + void HBox::setSize (const MyGUI::IntSize& _value) + { + MyGUI::Widget::setSize (_value); + align(); + } + + void HBox::setCoord (const MyGUI::IntCoord& _value) + { + MyGUI::Widget::setCoord (_value); + align(); + } + + void HBox::onWidgetCreated(MyGUI::Widget* _widget) + { + align(); + } + + MyGUI::IntSize HBox::getRequestedSize () + { + MyGUI::IntSize size(0,0); + for (unsigned int i = 0; i < getChildCount (); ++i) + { + AutoSizedWidget* w = dynamic_cast(getChildAt(i)); + if (w) + { + MyGUI::IntSize requested = w->getRequestedSize (); + size.height = std::max(size.height, requested.height); + size.width = size.width + requested.width; + if (i != getChildCount()-1) + size.width += mSpacing; + } + else + { + MyGUI::IntSize requested = getChildAt(i)->getSize (); + size.height = std::max(size.height, requested.height); + + if (getChildAt(i)->getUserString("HStretch") != "true") + size.width = size.width + requested.width; + + if (i != getChildCount()-1) + size.width += mSpacing; + } + size.height += mPadding*2; + size.width += mPadding*2; + } + return size; + } + + + + + void VBox::align () + { + unsigned int count = getChildCount (); + size_t v_stretched_count = 0; + int total_height = 0; + int total_width = 0; + std::vector< std::pair > sizes; + for (unsigned int i = 0; i < count; ++i) + { + MyGUI::Widget* w = getChildAt(i); + bool vstretch = w->getUserString ("VStretch") == "true"; + v_stretched_count += vstretch; + AutoSizedWidget* aw = dynamic_cast(w); + if (aw) + { + sizes.push_back(std::make_pair(aw->getRequestedSize (), vstretch)); + total_height += aw->getRequestedSize ().height; + total_width = std::max(total_width, aw->getRequestedSize ().width); + } + else + { + sizes.push_back (std::make_pair(w->getSize(), vstretch)); + total_height += w->getSize().height; + + if (!(w->getUserString("HStretch") == "true")) + total_width = std::max(total_width, w->getSize().width); + } + + if (i != count-1) + total_height += mSpacing; + } + + if (mAutoResize && (total_width+mPadding*2 != getSize().width || total_height+mPadding*2 != getSize().height)) + { + setSize(MyGUI::IntSize(total_width+mPadding*2, total_height+mPadding*2)); + return; + } + + + int curY = 0; + for (unsigned int i = 0; i < count; ++i) + { + if (i==0) + curY += mPadding; + + MyGUI::Widget* w = getChildAt(i); + + bool hstretch = w->getUserString ("HStretch") == "true"; + int width = hstretch ? total_width : sizes[i].first.width; + + MyGUI::IntCoord widgetCoord; + widgetCoord.top = curY; + widgetCoord.left = mPadding + (getSize().width-mPadding*2 - width) / 2; + int height = sizes[i].second ? sizes[i].first.height + (getSize().height-mPadding*2 - total_height)/v_stretched_count + : sizes[i].first.height; + widgetCoord.height = height; + widgetCoord.width = width; + w->setCoord(widgetCoord); + curY += height; + + if (i != count-1) + curY += mSpacing; + } + } + + void VBox::setPropertyOverride(const std::string& _key, const std::string& _value) + { + Box::_setPropertyImpl (_key, _value); + } + + void VBox::setSize (const MyGUI::IntSize& _value) + { + MyGUI::Widget::setSize (_value); + align(); + } + + void VBox::setCoord (const MyGUI::IntCoord& _value) + { + MyGUI::Widget::setCoord (_value); + align(); + } + + MyGUI::IntSize VBox::getRequestedSize () + { + MyGUI::IntSize size(0,0); + for (unsigned int i = 0; i < getChildCount (); ++i) + { + AutoSizedWidget* w = dynamic_cast(getChildAt(i)); + if (w) + { + MyGUI::IntSize requested = w->getRequestedSize (); + size.width = std::max(size.width, requested.width); + size.height = size.height + requested.height; + if (i != getChildCount()-1) + size.height += mSpacing; + } + else + { + MyGUI::IntSize requested = getChildAt(i)->getSize (); + size.width = std::max(size.width, requested.width); + + if (getChildAt(i)->getUserString("VStretch") != "true") + size.height = size.height + requested.height; + + if (i != getChildCount()-1) + size.height += mSpacing; + } + size.height += mPadding*2; + size.width += mPadding*2; + } + return size; + } + + void VBox::onWidgetCreated(MyGUI::Widget* _widget) + { + align(); + } } } - -void VBox::setPropertyOverride(const std::string& _key, const std::string& _value) -{ - Box::_setPropertyImpl (_key, _value); -} - -void VBox::setSize (const MyGUI::IntSize& _value) -{ - MyGUI::Widget::setSize (_value); - align(); -} - -void VBox::setCoord (const MyGUI::IntCoord& _value) -{ - MyGUI::Widget::setCoord (_value); - align(); -} - -MyGUI::IntSize VBox::getRequestedSize () -{ - MyGUI::IntSize size(0,0); - for (unsigned int i = 0; i < getChildCount (); ++i) - { - AutoSizedWidget* w = dynamic_cast(getChildAt(i)); - if (w) - { - MyGUI::IntSize requested = w->getRequestedSize (); - size.width = std::max(size.width, requested.width); - size.height = size.height + requested.height; - if (i != getChildCount()-1) - size.height += mSpacing; - } - else - { - MyGUI::IntSize requested = getChildAt(i)->getSize (); - size.width = std::max(size.width, requested.width); - - if (getChildAt(i)->getUserString("VStretch") != "true") - size.height = size.height + requested.height; - - if (i != getChildCount()-1) - size.height += mSpacing; - } - size.height += mPadding*2; - size.width += mPadding*2; - } - return size; -} - -void VBox::onWidgetCreated(MyGUI::Widget* _widget) -{ - align(); -} diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 154234bee..2171beaff 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -39,1143 +39,1146 @@ #include "companionwindow.hpp" #include "inventorywindow.hpp" -using namespace MWGui; - -WindowManager::WindowManager( - const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *ogre, - const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, - Translation::Storage& translationDataStorage) - : mGuiManager(NULL) - , mRendering(ogre) - , mHud(NULL) - , mMap(NULL) - , mMenu(NULL) - , mStatsWindow(NULL) - , mToolTips(NULL) - , mMessageBoxManager(NULL) - , mConsole(NULL) - , mJournal(NULL) - , mDialogueWindow(NULL) - , mBookWindow(NULL) - , mScrollWindow(NULL) - , mCountDialog(NULL) - , mTradeWindow(NULL) - , mSpellBuyingWindow(NULL) - , mTravelWindow(NULL) - , mSettingsWindow(NULL) - , mConfirmationDialog(NULL) - , mAlchemyWindow(NULL) - , mSpellWindow(NULL) - , mLoadingScreen(NULL) - , mCharGen(NULL) - , mLevelupDialog(NULL) - , mWaitDialog(NULL) - , mSpellCreationDialog(NULL) - , mEnchantingDialog(NULL) - , mTrainingWindow(NULL) - , mMerchantRepair(NULL) - , mRepair(NULL) - , mSoulgemDialog(NULL) - , mCompanionWindow(NULL) - , mPlayerName() - , mPlayerRaceId() - , mPlayerAttributes() - , mPlayerMajorSkills() - , mPlayerMinorSkills() - , mPlayerSkillValues() - , mPlayerHealth() - , mPlayerMagicka() - , mPlayerFatigue() - , mGui(NULL) - , mGarbageDialogs() - , mShown(GW_ALL) - , mAllowed(newGame ? GW_None : GW_ALL) - , mRestAllowed(newGame ? false : true) - , mShowFPSLevel(fpsLevel) - , mFPS(0.0f) - , mTriangleCount(0) - , mBatchCount(0) - , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) - , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) - , mHudEnabled(true) - , mTranslationDataStorage (translationDataStorage) +namespace MWGui { - // Set up the GUI system - mGuiManager = new OEngine::GUI::MyGUIManager(mRendering->getWindow(), mRendering->getScene(), false, logpath); - mGui = mGuiManager->getGui(); - //Register own widgets with MyGUI - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - - MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); - MyGUI::ResourceManager::getInstance().load("core.xml"); - - MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); - - // Get size info from the Gui object - assert(mGui); - int w = MyGUI::RenderManager::getInstance().getViewSize().width; - int h = MyGUI::RenderManager::getInstance().getViewSize().height; - - MyGUI::Widget* dragAndDropWidget = mGui->createWidgetT("Widget","",0,0,w,h,MyGUI::Align::Default,"DragAndDrop","DragAndDropWidget"); - dragAndDropWidget->setVisible(false); - - mDragAndDrop = new DragAndDrop(); - mDragAndDrop->mIsOnDragAndDrop = false; - mDragAndDrop->mDraggedWidget = 0; - mDragAndDrop->mDragAndDropWidget = dragAndDropWidget; - - mMenu = new MainMenu(w,h); - mMap = new MapWindow(cacheDir); - mStatsWindow = new StatsWindow(); - mConsole = new Console(w,h, consoleOnlyScripts); - mJournal = new JournalWindow(); - mMessageBoxManager = new MessageBoxManager(); - mInventoryWindow = new InventoryWindow(mDragAndDrop); - mTradeWindow = new TradeWindow(); - mSpellBuyingWindow = new SpellBuyingWindow(); - mTravelWindow = new TravelWindow(); - mDialogueWindow = new DialogueWindow(); - mContainerWindow = new ContainerWindow(mDragAndDrop); - mHud = new HUD(w,h, mShowFPSLevel, mDragAndDrop); - mToolTips = new ToolTips(); - mScrollWindow = new ScrollWindow(); - mBookWindow = new BookWindow(); - mCountDialog = new CountDialog(); - mSettingsWindow = new SettingsWindow(); - mConfirmationDialog = new ConfirmationDialog(); - mAlchemyWindow = new AlchemyWindow(); - mSpellWindow = new SpellWindow(); - mQuickKeysMenu = new QuickKeysMenu(); - mLevelupDialog = new LevelupDialog(); - mWaitDialog = new WaitDialog(); - mSpellCreationDialog = new SpellCreationDialog(); - mEnchantingDialog = new EnchantingDialog(); - mTrainingWindow = new TrainingWindow(); - mMerchantRepair = new MerchantRepair(); - mRepair = new Repair(); - mSoulgemDialog = new SoulgemDialog(mMessageBoxManager); - mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager); - - mLoadingScreen = new LoadingScreen(mRendering->getScene (), mRendering->getWindow ()); - mLoadingScreen->onResChange (w,h); - - mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); - - mCursor = new Cursor(); - - mHud->setVisible(mHudEnabled); - - mCharGen = new CharacterCreation(); - - // Setup player stats - for (int i = 0; i < ESM::Attribute::Length; ++i) + WindowManager::WindowManager( + const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *ogre, + const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, + Translation::Storage& translationDataStorage) + : mGuiManager(NULL) + , mRendering(ogre) + , mHud(NULL) + , mMap(NULL) + , mMenu(NULL) + , mStatsWindow(NULL) + , mToolTips(NULL) + , mMessageBoxManager(NULL) + , mConsole(NULL) + , mJournal(NULL) + , mDialogueWindow(NULL) + , mBookWindow(NULL) + , mScrollWindow(NULL) + , mCountDialog(NULL) + , mTradeWindow(NULL) + , mSpellBuyingWindow(NULL) + , mTravelWindow(NULL) + , mSettingsWindow(NULL) + , mConfirmationDialog(NULL) + , mAlchemyWindow(NULL) + , mSpellWindow(NULL) + , mLoadingScreen(NULL) + , mCharGen(NULL) + , mLevelupDialog(NULL) + , mWaitDialog(NULL) + , mSpellCreationDialog(NULL) + , mEnchantingDialog(NULL) + , mTrainingWindow(NULL) + , mMerchantRepair(NULL) + , mRepair(NULL) + , mSoulgemDialog(NULL) + , mCompanionWindow(NULL) + , mPlayerName() + , mPlayerRaceId() + , mPlayerAttributes() + , mPlayerMajorSkills() + , mPlayerMinorSkills() + , mPlayerSkillValues() + , mPlayerHealth() + , mPlayerMagicka() + , mPlayerFatigue() + , mGui(NULL) + , mGarbageDialogs() + , mShown(GW_ALL) + , mAllowed(newGame ? GW_None : GW_ALL) + , mRestAllowed(newGame ? false : true) + , mShowFPSLevel(fpsLevel) + , mFPS(0.0f) + , mTriangleCount(0) + , mBatchCount(0) + , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) + , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) + , mHudEnabled(true) + , mTranslationDataStorage (translationDataStorage) { - mPlayerAttributes.insert(std::make_pair(ESM::Attribute::sAttributeIds[i], MWMechanics::Stat())); - } + // Set up the GUI system + mGuiManager = new OEngine::GUI::MyGUIManager(mRendering->getWindow(), mRendering->getScene(), false, logpath); + mGui = mGuiManager->getGui(); - for (int i = 0; i < ESM::Skill::Length; ++i) - { - mPlayerSkillValues.insert(std::make_pair(ESM::Skill::sSkillIds[i], MWMechanics::Stat())); - } + //Register own widgets with MyGUI + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); - unsetSelectedSpell(); - unsetSelectedWeapon(); + MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); + MyGUI::ResourceManager::getInstance().load("core.xml"); - if (newGame) - disallowAll (); + MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); - // Set up visibility - updateVisible(); -} + // Get size info from the Gui object + assert(mGui); + int w = MyGUI::RenderManager::getInstance().getViewSize().width; + int h = MyGUI::RenderManager::getInstance().getViewSize().height; -WindowManager::~WindowManager() -{ - delete mConsole; - delete mMessageBoxManager; - delete mHud; - delete mMap; - delete mMenu; - delete mStatsWindow; - delete mJournal; - delete mDialogueWindow; - delete mContainerWindow; - delete mInventoryWindow; - delete mToolTips; - delete mCharGen; - delete mDragAndDrop; - delete mBookWindow; - delete mScrollWindow; - delete mTradeWindow; - delete mSpellBuyingWindow; - delete mTravelWindow; - delete mSettingsWindow; - delete mConfirmationDialog; - delete mAlchemyWindow; - delete mSpellWindow; - delete mLoadingScreen; - delete mLevelupDialog; - delete mWaitDialog; - delete mSpellCreationDialog; - delete mEnchantingDialog; - delete mTrainingWindow; - delete mCountDialog; - delete mQuickKeysMenu; - delete mMerchantRepair; - delete mRepair; - delete mSoulgemDialog; - delete mCursor; + MyGUI::Widget* dragAndDropWidget = mGui->createWidgetT("Widget","",0,0,w,h,MyGUI::Align::Default,"DragAndDrop","DragAndDropWidget"); + dragAndDropWidget->setVisible(false); - cleanupGarbage(); + mDragAndDrop = new DragAndDrop(); + mDragAndDrop->mIsOnDragAndDrop = false; + mDragAndDrop->mDraggedWidget = 0; + mDragAndDrop->mDragAndDropWidget = dragAndDropWidget; - delete mGuiManager; -} + mMenu = new MainMenu(w,h); + mMap = new MapWindow(cacheDir); + mStatsWindow = new StatsWindow(); + mConsole = new Console(w,h, consoleOnlyScripts); + mJournal = new JournalWindow(); + mMessageBoxManager = new MessageBoxManager(); + mInventoryWindow = new InventoryWindow(mDragAndDrop); + mTradeWindow = new TradeWindow(); + mSpellBuyingWindow = new SpellBuyingWindow(); + mTravelWindow = new TravelWindow(); + mDialogueWindow = new DialogueWindow(); + mContainerWindow = new ContainerWindow(mDragAndDrop); + mHud = new HUD(w,h, mShowFPSLevel, mDragAndDrop); + mToolTips = new ToolTips(); + mScrollWindow = new ScrollWindow(); + mBookWindow = new BookWindow(); + mCountDialog = new CountDialog(); + mSettingsWindow = new SettingsWindow(); + mConfirmationDialog = new ConfirmationDialog(); + mAlchemyWindow = new AlchemyWindow(); + mSpellWindow = new SpellWindow(); + mQuickKeysMenu = new QuickKeysMenu(); + mLevelupDialog = new LevelupDialog(); + mWaitDialog = new WaitDialog(); + mSpellCreationDialog = new SpellCreationDialog(); + mEnchantingDialog = new EnchantingDialog(); + mTrainingWindow = new TrainingWindow(); + mMerchantRepair = new MerchantRepair(); + mRepair = new Repair(); + mSoulgemDialog = new SoulgemDialog(mMessageBoxManager); + mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager); -void WindowManager::cleanupGarbage() -{ - // Delete any dialogs which are no longer in use - if (!mGarbageDialogs.empty()) - { - for (std::vector::iterator it = mGarbageDialogs.begin(); it != mGarbageDialogs.end(); ++it) + mLoadingScreen = new LoadingScreen(mRendering->getScene (), mRendering->getWindow ()); + mLoadingScreen->onResChange (w,h); + + mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); + + mCursor = new Cursor(); + + mHud->setVisible(mHudEnabled); + + mCharGen = new CharacterCreation(); + + // Setup player stats + for (int i = 0; i < ESM::Attribute::Length; ++i) { - delete *it; + mPlayerAttributes.insert(std::make_pair(ESM::Attribute::sAttributeIds[i], MWMechanics::Stat())); } - mGarbageDialogs.clear(); - } -} -void WindowManager::update() -{ - cleanupGarbage(); - - mHud->setFPS(mFPS); - mHud->setTriangleCount(mTriangleCount); - mHud->setBatchCount(mBatchCount); - - mHud->update(); - - mCursor->update(); -} - -void WindowManager::updateVisible() -{ - // Start out by hiding everything except the HUD - mMap->setVisible(false); - mMenu->setVisible(false); - mStatsWindow->setVisible(false); - mConsole->disable(); - mJournal->setVisible(false); - mDialogueWindow->setVisible(false); - mContainerWindow->setVisible(false); - mInventoryWindow->setVisible(false); - mScrollWindow->setVisible(false); - mBookWindow->setVisible(false); - mTradeWindow->setVisible(false); - mSpellBuyingWindow->setVisible(false); - mTravelWindow->setVisible(false); - mSettingsWindow->setVisible(false); - mAlchemyWindow->setVisible(false); - mSpellWindow->setVisible(false); - mQuickKeysMenu->setVisible(false); - mLevelupDialog->setVisible(false); - mWaitDialog->setVisible(false); - mSpellCreationDialog->setVisible(false); - mEnchantingDialog->setVisible(false); - mTrainingWindow->setVisible(false); - mMerchantRepair->setVisible(false); - mRepair->setVisible(false); - mCompanionWindow->setVisible(false); - - mHud->setVisible(mHudEnabled); - - bool gameMode = !isGuiMode(); - - mInputBlocker->setVisible (gameMode); - - if (gameMode) - mToolTips->enterGameMode(); - else - mToolTips->enterGuiMode(); - - if (gameMode) - MyGUI::InputManager::getInstance ().setKeyFocusWidget (NULL); - - setMinimapVisibility((mAllowed & GW_Map) && !mMap->pinned()); - setWeaponVisibility((mAllowed & GW_Inventory) && !mInventoryWindow->pinned()); - setSpellVisibility((mAllowed & GW_Magic) && !mSpellWindow->pinned()); - setHMSVisibility((mAllowed & GW_Stats) && !mStatsWindow->pinned()); - - // If in game mode, show only the pinned windows - if (gameMode) - { - mMap->setVisible(mMap->pinned()); - mStatsWindow->setVisible(mStatsWindow->pinned()); - mInventoryWindow->setVisible(mInventoryWindow->pinned()); - mSpellWindow->setVisible(mSpellWindow->pinned()); - - return; - } - - GuiMode mode = mGuiModes.back(); - - switch(mode) { - case GM_QuickKeysMenu: - mQuickKeysMenu->setVisible (true); - break; - case GM_MainMenu: - mMenu->setVisible(true); - break; - case GM_Settings: - mSettingsWindow->setVisible(true); - break; - case GM_Console: - // Show the pinned windows - mMap->setVisible(mMap->pinned()); - mStatsWindow->setVisible(mStatsWindow->pinned()); - mInventoryWindow->setVisible(mInventoryWindow->pinned()); - mSpellWindow->setVisible(mSpellWindow->pinned()); - - mConsole->enable(); - break; - case GM_Scroll: - mScrollWindow->setVisible(true); - break; - case GM_Book: - mBookWindow->setVisible(true); - break; - case GM_Alchemy: - mAlchemyWindow->setVisible(true); - break; - case GM_Rest: - mWaitDialog->setVisible(true); - break; - case GM_RestBed: - mWaitDialog->setVisible(true); - mWaitDialog->bedActivated(); - break; - case GM_Levelup: - mLevelupDialog->setVisible(true); - break; - case GM_Name: - case GM_Race: - case GM_Class: - case GM_ClassPick: - case GM_ClassCreate: - case GM_Birth: - case GM_ClassGenerate: - case GM_Review: - mCharGen->spawnDialog(mode); - break; - case GM_Inventory: + for (int i = 0; i < ESM::Skill::Length; ++i) { - // First, compute the effective set of windows to show. - // This is controlled both by what windows the - // user has opened/closed (the 'shown' variable) and by what - // windows we are allowed to show (the 'allowed' var.) - int eff = mShown & mAllowed; - - // Show the windows we want - mMap ->setVisible(eff & GW_Map); - mStatsWindow ->setVisible(eff & GW_Stats); - mInventoryWindow->setVisible(eff & GW_Inventory); - mSpellWindow ->setVisible(eff & GW_Magic); - break; + mPlayerSkillValues.insert(std::make_pair(ESM::Skill::sSkillIds[i], MWMechanics::Stat())); } - case GM_Container: - mContainerWindow->setVisible(true); - mInventoryWindow->setVisible(true); - break; - case GM_Companion: - mCompanionWindow->setVisible(true); - mInventoryWindow->setVisible(true); - break; - case GM_Dialogue: - mDialogueWindow->setVisible(true); - break; - case GM_Barter: - mInventoryWindow->setVisible(true); - mTradeWindow->setVisible(true); - break; - case GM_SpellBuying: - mSpellBuyingWindow->setVisible(true); - break; - case GM_Travel: - mTravelWindow->setVisible(true); - break; - case GM_SpellCreation: - mSpellCreationDialog->setVisible(true); - break; - case GM_Enchanting: - mEnchantingDialog->setVisible(true); - break; - case GM_Training: - mTrainingWindow->setVisible(true); - break; - case GM_MerchantRepair: - mMerchantRepair->setVisible(true); - break; - case GM_Repair: - mRepair->setVisible(true); - break; - case GM_Journal: - mJournal->setVisible(true); - break; - case GM_LoadingWallpaper: - mHud->setVisible(false); - mCursor->setVisible(false); - break; - case GM_Loading: - // Show the pinned windows - mMap->setVisible(mMap->pinned()); - mStatsWindow->setVisible(mStatsWindow->pinned()); - mInventoryWindow->setVisible(mInventoryWindow->pinned()); - mSpellWindow->setVisible(mSpellWindow->pinned()); - mCursor->setVisible(false); - break; - case GM_Video: - mCursor->setVisible(false); - mHud->setVisible(false); - break; - default: - // Unsupported mode, switch back to game - break; - } -} + unsetSelectedSpell(); + unsetSelectedWeapon(); -void WindowManager::setValue (const std::string& id, const MWMechanics::Stat& value) -{ - mStatsWindow->setValue (id, value); - mCharGen->setValue(id, value); + if (newGame) + disallowAll (); - static const char *ids[] = - { - "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", - "AttribVal6", "AttribVal7", "AttribVal8" - }; - static ESM::Attribute::AttributeID attributes[] = - { - ESM::Attribute::Strength, - ESM::Attribute::Intelligence, - ESM::Attribute::Willpower, - ESM::Attribute::Agility, - ESM::Attribute::Speed, - ESM::Attribute::Endurance, - ESM::Attribute::Personality, - ESM::Attribute::Luck - }; - for (size_t i = 0; i < sizeof(ids)/sizeof(ids[0]); ++i) - { - if (id != ids[i]) - continue; - mPlayerAttributes[attributes[i]] = value; - break; - } -} - - -void WindowManager::setValue (int parSkill, const MWMechanics::Stat& value) -{ - /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we - /// allow custom skills. - mStatsWindow->setValue(static_cast (parSkill), value); - mCharGen->setValue(static_cast (parSkill), value); - mPlayerSkillValues[parSkill] = value; -} - -void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) -{ - mStatsWindow->setValue (id, value); - mHud->setValue (id, value); - mCharGen->setValue(id, value); - if (id == "HBar") - { - mPlayerHealth = value; - mCharGen->setPlayerHealth (value); - } - else if (id == "MBar") - { - mPlayerMagicka = value; - mCharGen->setPlayerMagicka (value); - } - else if (id == "FBar") - { - mPlayerFatigue = value; - mCharGen->setPlayerFatigue (value); - } -} - -#if 0 -MWMechanics::DynamicStat WindowManager::getValue(const std::string& id) -{ - if(id == "HBar") - return layerHealth; - else if (id == "MBar") - return mPlayerMagicka; - else if (id == "FBar") - return mPlayerFatigue; -} -#endif - -void WindowManager::setValue (const std::string& id, const std::string& value) -{ - mStatsWindow->setValue (id, value); - if (id=="name") - mPlayerName = value; - else if (id=="race") - mPlayerRaceId = value; -} - -void WindowManager::setValue (const std::string& id, int value) -{ - mStatsWindow->setValue (id, value); -} - -void WindowManager::setPlayerClass (const ESM::Class &class_) -{ - mStatsWindow->setValue("class", class_.mName); -} - -void WindowManager::configureSkills (const SkillList& major, const SkillList& minor) -{ - mStatsWindow->configureSkills (major, minor); - mCharGen->configureSkills(major, minor); - mPlayerMajorSkills = major; - mPlayerMinorSkills = minor; -} - -void WindowManager::setReputation (int reputation) -{ - mStatsWindow->setReputation (reputation); -} - -void WindowManager::setBounty (int bounty) -{ - mStatsWindow->setBounty (bounty); -} - -void WindowManager::updateSkillArea() -{ - mStatsWindow->updateSkillArea(); -} - -void WindowManager::removeDialog(OEngine::GUI::Layout*dialog) -{ - if (!dialog) - return; - dialog->setVisible(false); - mGarbageDialogs.push_back(dialog); -} - -void WindowManager::messageBox (const std::string& message, const std::vector& buttons) -{ - if(buttons.empty()){ - /* If there are no buttons, and there is a dialogue window open, messagebox goes to the dialogue window */ - if(!mGuiModes.empty() && mGuiModes.back() == GM_Dialogue) - mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message)); - else - mMessageBoxManager->createMessageBox(message); + // Set up visibility + updateVisible(); } - else + WindowManager::~WindowManager() { - mMessageBoxManager->createInteractiveMessageBox(message, buttons); - MWBase::Environment::get().getInputManager()->changeInputMode(isGuiMode()); + delete mConsole; + delete mMessageBoxManager; + delete mHud; + delete mMap; + delete mMenu; + delete mStatsWindow; + delete mJournal; + delete mDialogueWindow; + delete mContainerWindow; + delete mInventoryWindow; + delete mToolTips; + delete mCharGen; + delete mDragAndDrop; + delete mBookWindow; + delete mScrollWindow; + delete mTradeWindow; + delete mSpellBuyingWindow; + delete mTravelWindow; + delete mSettingsWindow; + delete mConfirmationDialog; + delete mAlchemyWindow; + delete mSpellWindow; + delete mLoadingScreen; + delete mLevelupDialog; + delete mWaitDialog; + delete mSpellCreationDialog; + delete mEnchantingDialog; + delete mTrainingWindow; + delete mCountDialog; + delete mQuickKeysMenu; + delete mMerchantRepair; + delete mRepair; + delete mSoulgemDialog; + delete mCursor; + + cleanupGarbage(); + + delete mGuiManager; } -} -void WindowManager::enterPressed () -{ - mMessageBoxManager->enterPressed(); -} - -int WindowManager::readPressedButton () -{ - return mMessageBoxManager->readPressedButton(); -} - -std::string WindowManager::getGameSettingString(const std::string &id, const std::string &default_) -{ - const ESM::GameSetting *setting = - MWBase::Environment::get().getWorld()->getStore().get().search(id); - - if (setting && setting->mValue.getType()==ESM::VT_String) - return setting->mValue.getString(); - - return default_; -} - -void WindowManager::onDialogueWindowBye() -{ - if (mDialogueWindow) + void WindowManager::cleanupGarbage() { - //FIXME set some state and stuff? - //removeDialog(dialogueWindow); + // Delete any dialogs which are no longer in use + if (!mGarbageDialogs.empty()) + { + for (std::vector::iterator it = mGarbageDialogs.begin(); it != mGarbageDialogs.end(); ++it) + { + delete *it; + } + mGarbageDialogs.clear(); + } + } + + void WindowManager::update() + { + cleanupGarbage(); + + mHud->setFPS(mFPS); + mHud->setTriangleCount(mTriangleCount); + mHud->setBatchCount(mBatchCount); + + mHud->update(); + + mCursor->update(); + } + + void WindowManager::updateVisible() + { + // Start out by hiding everything except the HUD + mMap->setVisible(false); + mMenu->setVisible(false); + mStatsWindow->setVisible(false); + mConsole->disable(); + mJournal->setVisible(false); mDialogueWindow->setVisible(false); - } - removeGuiMode(GM_Dialogue); -} + mContainerWindow->setVisible(false); + mInventoryWindow->setVisible(false); + mScrollWindow->setVisible(false); + mBookWindow->setVisible(false); + mTradeWindow->setVisible(false); + mSpellBuyingWindow->setVisible(false); + mTravelWindow->setVisible(false); + mSettingsWindow->setVisible(false); + mAlchemyWindow->setVisible(false); + mSpellWindow->setVisible(false); + mQuickKeysMenu->setVisible(false); + mLevelupDialog->setVisible(false); + mWaitDialog->setVisible(false); + mSpellCreationDialog->setVisible(false); + mEnchantingDialog->setVisible(false); + mTrainingWindow->setVisible(false); + mMerchantRepair->setVisible(false); + mRepair->setVisible(false); + mCompanionWindow->setVisible(false); -void WindowManager::onFrame (float frameDuration) -{ - mMessageBoxManager->onFrame(frameDuration); + mHud->setVisible(mHudEnabled); - mToolTips->onFrame(frameDuration); + bool gameMode = !isGuiMode(); - if (mDragAndDrop->mIsOnDragAndDrop) - { - assert(mDragAndDrop->mDraggedWidget); - mDragAndDrop->mDraggedWidget->setPosition(MyGUI::InputManager::getInstance().getMousePosition()); - } + mInputBlocker->setVisible (gameMode); - mDialogueWindow->onFrame(); + if (gameMode) + mToolTips->enterGameMode(); + else + mToolTips->enterGuiMode(); - mInventoryWindow->onFrame(); + if (gameMode) + MyGUI::InputManager::getInstance ().setKeyFocusWidget (NULL); - mStatsWindow->onFrame(); + setMinimapVisibility((mAllowed & GW_Map) && !mMap->pinned()); + setWeaponVisibility((mAllowed & GW_Inventory) && !mInventoryWindow->pinned()); + setSpellVisibility((mAllowed & GW_Magic) && !mSpellWindow->pinned()); + setHMSVisibility((mAllowed & GW_Stats) && !mStatsWindow->pinned()); - mWaitDialog->onFrame(frameDuration); - - mHud->onFrame(frameDuration); - - mTrainingWindow->onFrame (frameDuration); - mTradeWindow->onFrame(frameDuration); - - mTrainingWindow->checkReferenceAvailable(); - mDialogueWindow->checkReferenceAvailable(); - mTradeWindow->checkReferenceAvailable(); - mSpellBuyingWindow->checkReferenceAvailable(); - mSpellCreationDialog->checkReferenceAvailable(); - mEnchantingDialog->checkReferenceAvailable(); - mContainerWindow->checkReferenceAvailable(); - mCompanionWindow->checkReferenceAvailable(); - mConsole->checkReferenceAvailable(); -} - -void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) -{ - if (cell->mCell->isExterior()) - { - std::string name; - if (cell->mCell->mName != "") + // If in game mode, show only the pinned windows + if (gameMode) { - name = cell->mCell->mName; - mMap->addVisitedLocation ("#{sCell=" + name + "}", cell->mCell->getGridX (), cell->mCell->getGridY ()); + mMap->setVisible(mMap->pinned()); + mStatsWindow->setVisible(mStatsWindow->pinned()); + mInventoryWindow->setVisible(mInventoryWindow->pinned()); + mSpellWindow->setVisible(mSpellWindow->pinned()); + + return; } + + GuiMode mode = mGuiModes.back(); + + switch(mode) { + case GM_QuickKeysMenu: + mQuickKeysMenu->setVisible (true); + break; + case GM_MainMenu: + mMenu->setVisible(true); + break; + case GM_Settings: + mSettingsWindow->setVisible(true); + break; + case GM_Console: + // Show the pinned windows + mMap->setVisible(mMap->pinned()); + mStatsWindow->setVisible(mStatsWindow->pinned()); + mInventoryWindow->setVisible(mInventoryWindow->pinned()); + mSpellWindow->setVisible(mSpellWindow->pinned()); + + mConsole->enable(); + break; + case GM_Scroll: + mScrollWindow->setVisible(true); + break; + case GM_Book: + mBookWindow->setVisible(true); + break; + case GM_Alchemy: + mAlchemyWindow->setVisible(true); + break; + case GM_Rest: + mWaitDialog->setVisible(true); + break; + case GM_RestBed: + mWaitDialog->setVisible(true); + mWaitDialog->bedActivated(); + break; + case GM_Levelup: + mLevelupDialog->setVisible(true); + break; + case GM_Name: + case GM_Race: + case GM_Class: + case GM_ClassPick: + case GM_ClassCreate: + case GM_Birth: + case GM_ClassGenerate: + case GM_Review: + mCharGen->spawnDialog(mode); + break; + case GM_Inventory: + { + // First, compute the effective set of windows to show. + // This is controlled both by what windows the + // user has opened/closed (the 'shown' variable) and by what + // windows we are allowed to show (the 'allowed' var.) + int eff = mShown & mAllowed; + + // Show the windows we want + mMap ->setVisible(eff & GW_Map); + mStatsWindow ->setVisible(eff & GW_Stats); + mInventoryWindow->setVisible(eff & GW_Inventory); + mSpellWindow ->setVisible(eff & GW_Magic); + break; + } + case GM_Container: + mContainerWindow->setVisible(true); + mInventoryWindow->setVisible(true); + break; + case GM_Companion: + mCompanionWindow->setVisible(true); + mInventoryWindow->setVisible(true); + break; + case GM_Dialogue: + mDialogueWindow->setVisible(true); + break; + case GM_Barter: + mInventoryWindow->setVisible(true); + mTradeWindow->setVisible(true); + break; + case GM_SpellBuying: + mSpellBuyingWindow->setVisible(true); + break; + case GM_Travel: + mTravelWindow->setVisible(true); + break; + case GM_SpellCreation: + mSpellCreationDialog->setVisible(true); + break; + case GM_Enchanting: + mEnchantingDialog->setVisible(true); + break; + case GM_Training: + mTrainingWindow->setVisible(true); + break; + case GM_MerchantRepair: + mMerchantRepair->setVisible(true); + break; + case GM_Repair: + mRepair->setVisible(true); + break; + case GM_Journal: + mJournal->setVisible(true); + break; + case GM_LoadingWallpaper: + mHud->setVisible(false); + mCursor->setVisible(false); + break; + case GM_Loading: + // Show the pinned windows + mMap->setVisible(mMap->pinned()); + mStatsWindow->setVisible(mStatsWindow->pinned()); + mInventoryWindow->setVisible(mInventoryWindow->pinned()); + mSpellWindow->setVisible(mSpellWindow->pinned()); + + mCursor->setVisible(false); + break; + case GM_Video: + mCursor->setVisible(false); + mHud->setVisible(false); + break; + default: + // Unsupported mode, switch back to game + break; + } + } + + void WindowManager::setValue (const std::string& id, const MWMechanics::Stat& value) + { + mStatsWindow->setValue (id, value); + mCharGen->setValue(id, value); + + static const char *ids[] = + { + "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", + "AttribVal6", "AttribVal7", "AttribVal8" + }; + static ESM::Attribute::AttributeID attributes[] = + { + ESM::Attribute::Strength, + ESM::Attribute::Intelligence, + ESM::Attribute::Willpower, + ESM::Attribute::Agility, + ESM::Attribute::Speed, + ESM::Attribute::Endurance, + ESM::Attribute::Personality, + ESM::Attribute::Luck + }; + for (size_t i = 0; i < sizeof(ids)/sizeof(ids[0]); ++i) + { + if (id != ids[i]) + continue; + mPlayerAttributes[attributes[i]] = value; + break; + } + } + + + void WindowManager::setValue (int parSkill, const MWMechanics::Stat& value) + { + /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we + /// allow custom skills. + mStatsWindow->setValue(static_cast (parSkill), value); + mCharGen->setValue(static_cast (parSkill), value); + mPlayerSkillValues[parSkill] = value; + } + + void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) + { + mStatsWindow->setValue (id, value); + mHud->setValue (id, value); + mCharGen->setValue(id, value); + if (id == "HBar") + { + mPlayerHealth = value; + mCharGen->setPlayerHealth (value); + } + else if (id == "MBar") + { + mPlayerMagicka = value; + mCharGen->setPlayerMagicka (value); + } + else if (id == "FBar") + { + mPlayerFatigue = value; + mCharGen->setPlayerFatigue (value); + } + } + + #if 0 + MWMechanics::DynamicStat WindowManager::getValue(const std::string& id) + { + if(id == "HBar") + return layerHealth; + else if (id == "MBar") + return mPlayerMagicka; + else if (id == "FBar") + return mPlayerFatigue; + } + #endif + + void WindowManager::setValue (const std::string& id, const std::string& value) + { + mStatsWindow->setValue (id, value); + if (id=="name") + mPlayerName = value; + else if (id=="race") + mPlayerRaceId = value; + } + + void WindowManager::setValue (const std::string& id, int value) + { + mStatsWindow->setValue (id, value); + } + + void WindowManager::setPlayerClass (const ESM::Class &class_) + { + mStatsWindow->setValue("class", class_.mName); + } + + void WindowManager::configureSkills (const SkillList& major, const SkillList& minor) + { + mStatsWindow->configureSkills (major, minor); + mCharGen->configureSkills(major, minor); + mPlayerMajorSkills = major; + mPlayerMinorSkills = minor; + } + + void WindowManager::setReputation (int reputation) + { + mStatsWindow->setReputation (reputation); + } + + void WindowManager::setBounty (int bounty) + { + mStatsWindow->setBounty (bounty); + } + + void WindowManager::updateSkillArea() + { + mStatsWindow->updateSkillArea(); + } + + void WindowManager::removeDialog(OEngine::GUI::Layout*dialog) + { + if (!dialog) + return; + dialog->setVisible(false); + mGarbageDialogs.push_back(dialog); + } + + void WindowManager::messageBox (const std::string& message, const std::vector& buttons) + { + if(buttons.empty()){ + /* If there are no buttons, and there is a dialogue window open, messagebox goes to the dialogue window */ + if(!mGuiModes.empty() && mGuiModes.back() == GM_Dialogue) + mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message)); + else + mMessageBoxManager->createMessageBox(message); + } + else { - const ESM::Region* region = - MWBase::Environment::get().getWorld()->getStore().get().search(cell->mCell->mRegion); - if (region) - name = region->mName; - else - name = getGameSettingString("sDefaultCellname", "Wilderness"); + mMessageBoxManager->createInteractiveMessageBox(message, buttons); + MWBase::Environment::get().getInputManager()->changeInputMode(isGuiMode()); } - - mMap->cellExplored(cell->mCell->getGridX(), cell->mCell->getGridY()); - - mMap->setCellName( name ); - mHud->setCellName( name ); - - mMap->setCellPrefix("Cell"); - mHud->setCellPrefix("Cell"); - mMap->setActiveCell( cell->mCell->getGridX(), cell->mCell->getGridY() ); - mHud->setActiveCell( cell->mCell->getGridX(), cell->mCell->getGridY() ); } - else + + void WindowManager::enterPressed () { - mMap->setCellName( cell->mCell->mName ); - mHud->setCellName( cell->mCell->mName ); - mMap->setCellPrefix( cell->mCell->mName ); - mHud->setCellPrefix( cell->mCell->mName ); + mMessageBoxManager->enterPressed(); } -} - -void WindowManager::setInteriorMapTexture(const int x, const int y) -{ - mMap->setActiveCell(x,y, true); - mHud->setActiveCell(x,y, true); -} - -void WindowManager::setPlayerPos(const float x, const float y) -{ - mMap->setPlayerPos(x,y); - mHud->setPlayerPos(x,y); -} - -void WindowManager::setPlayerDir(const float x, const float y) -{ - mMap->setPlayerDir(x,y); - mHud->setPlayerDir(x,y); -} - -void WindowManager::setHMSVisibility(bool visible) -{ - mHud->setHmsVisible (visible); -} - -void WindowManager::setMinimapVisibility(bool visible) -{ - mHud->setMinimapVisible (visible); -} - -void WindowManager::toggleFogOfWar() -{ - mMap->toggleFogOfWar(); - mHud->toggleFogOfWar(); -} - -void WindowManager::setFocusObject(const MWWorld::Ptr& focus) -{ - mToolTips->setFocusObject(focus); -} - -void WindowManager::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) -{ - mToolTips->setFocusObjectScreenCoords(min_x, min_y, max_x, max_y); -} - -void WindowManager::toggleFullHelp() -{ - mToolTips->toggleFullHelp(); -} - -bool WindowManager::getFullHelp() const -{ - return mToolTips->getFullHelp(); -} - -void WindowManager::setWeaponVisibility(bool visible) -{ - mHud->setWeapVisible (visible); -} - -void WindowManager::setSpellVisibility(bool visible) -{ - mHud->setSpellVisible (visible); - mHud->setEffectVisible (visible); -} - -void WindowManager::setMouseVisible(bool visible) -{ - mCursor->setVisible(visible); -} - -void WindowManager::setDragDrop(bool dragDrop) -{ - mToolTips->setEnabled(!dragDrop); - MWBase::Environment::get().getInputManager()->setDragDrop(dragDrop); -} - -void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result) -{ - std::string tag(_tag); - - std::string tokenToFind = "sCell="; - size_t tokenLength = tokenToFind.length(); - - if (tag.substr(0, tokenLength) == tokenToFind) + int WindowManager::readPressedButton () { - _result = mTranslationDataStorage.translateCellName(tag.substr(tokenLength)); + return mMessageBoxManager->readPressedButton(); } - else + + std::string WindowManager::getGameSettingString(const std::string &id, const std::string &default_) { const ESM::GameSetting *setting = - MWBase::Environment::get().getWorld()->getStore().get().find(tag); + MWBase::Environment::get().getWorld()->getStore().get().search(id); if (setting && setting->mValue.getType()==ESM::VT_String) - _result = setting->mValue.getString(); - else - _result = tag; + return setting->mValue.getString(); + + return default_; } -} -void WindowManager::processChangedSettings(const Settings::CategorySettingVector& changed) -{ - mHud->setFpsLevel(Settings::Manager::getInt("fps", "HUD")); - mToolTips->setDelay(Settings::Manager::getFloat("tooltip delay", "GUI")); - - bool changeRes = false; - bool windowRecreated = false; - for (Settings::CategorySettingVector::const_iterator it = changed.begin(); - it != changed.end(); ++it) + void WindowManager::onDialogueWindowBye() { - if (it->first == "Video" && ( - it->second == "resolution x" - || it->second == "resolution y")) + if (mDialogueWindow) { - changeRes = true; + //FIXME set some state and stuff? + //removeDialog(dialogueWindow); + mDialogueWindow->setVisible(false); } - else if (it->first == "Video" && it->second == "vsync") - windowRecreated = true; - else if (it->first == "HUD" && it->second == "crosshair") - mCrosshairEnabled = Settings::Manager::getBool ("crosshair", "HUD"); - else if (it->first == "GUI" && it->second == "subtitles") - mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI"); + removeGuiMode(GM_Dialogue); } - if (changeRes) + void WindowManager::onFrame (float frameDuration) { - int x = Settings::Manager::getInt("resolution x", "Video"); - int y = Settings::Manager::getInt("resolution y", "Video"); - mHud->onResChange(x, y); - mConsole->onResChange(x, y); - mMenu->onResChange(x, y); - mSettingsWindow->center(); - mAlchemyWindow->center(); - mScrollWindow->center(); - mBookWindow->center(); - mQuickKeysMenu->center(); - mSpellBuyingWindow->center(); - mLoadingScreen->onResChange (x,y); - mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); - mInputBlocker->setSize(MyGUI::IntSize(x,y)); - } - if (windowRecreated) - { - mGuiManager->updateWindow (mRendering->getWindow ()); - mLoadingScreen->updateWindow (mRendering->getWindow ()); - } -} + mMessageBoxManager->onFrame(frameDuration); -void WindowManager::pushGuiMode(GuiMode mode) -{ - if (mode==GM_Inventory && mAllowed==GW_None) - return; + mToolTips->onFrame(frameDuration); + if (mDragAndDrop->mIsOnDragAndDrop) + { + assert(mDragAndDrop->mDraggedWidget); + mDragAndDrop->mDraggedWidget->setPosition(MyGUI::InputManager::getInstance().getMousePosition()); + } - // If this mode already exists somewhere in the stack, just bring it to the front. - if (std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end()) - { - mGuiModes.erase(std::find(mGuiModes.begin(), mGuiModes.end(), mode)); + mDialogueWindow->onFrame(); + + mInventoryWindow->onFrame(); + + mStatsWindow->onFrame(); + + mWaitDialog->onFrame(frameDuration); + + mHud->onFrame(frameDuration); + + mTrainingWindow->onFrame (frameDuration); + mTradeWindow->onFrame(frameDuration); + + mTrainingWindow->checkReferenceAvailable(); + mDialogueWindow->checkReferenceAvailable(); + mTradeWindow->checkReferenceAvailable(); + mSpellBuyingWindow->checkReferenceAvailable(); + mSpellCreationDialog->checkReferenceAvailable(); + mEnchantingDialog->checkReferenceAvailable(); + mContainerWindow->checkReferenceAvailable(); + mCompanionWindow->checkReferenceAvailable(); + mConsole->checkReferenceAvailable(); } - mGuiModes.push_back(mode); - - bool gameMode = !isGuiMode(); - MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); - - updateVisible(); -} - -void WindowManager::popGuiMode() -{ - if (!mGuiModes.empty()) - mGuiModes.pop_back(); - - bool gameMode = !isGuiMode(); - MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); - - updateVisible(); -} - -void WindowManager::removeGuiMode(GuiMode mode) -{ - std::vector::iterator it = mGuiModes.begin(); - while (it != mGuiModes.end()) + void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) { - if (*it == mode) - it = mGuiModes.erase(it); + if (cell->mCell->isExterior()) + { + std::string name; + if (cell->mCell->mName != "") + { + name = cell->mCell->mName; + mMap->addVisitedLocation ("#{sCell=" + name + "}", cell->mCell->getGridX (), cell->mCell->getGridY ()); + } + else + { + const ESM::Region* region = + MWBase::Environment::get().getWorld()->getStore().get().search(cell->mCell->mRegion); + if (region) + name = region->mName; + else + name = getGameSettingString("sDefaultCellname", "Wilderness"); + } + + mMap->cellExplored(cell->mCell->getGridX(), cell->mCell->getGridY()); + + mMap->setCellName( name ); + mHud->setCellName( name ); + + mMap->setCellPrefix("Cell"); + mHud->setCellPrefix("Cell"); + mMap->setActiveCell( cell->mCell->getGridX(), cell->mCell->getGridY() ); + mHud->setActiveCell( cell->mCell->getGridX(), cell->mCell->getGridY() ); + } else - ++it; + { + mMap->setCellName( cell->mCell->mName ); + mHud->setCellName( cell->mCell->mName ); + mMap->setCellPrefix( cell->mCell->mName ); + mHud->setCellPrefix( cell->mCell->mName ); + } + } - bool gameMode = !isGuiMode(); - MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); - - updateVisible(); -} - -void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent) -{ - mHud->setSelectedSpell(spellId, successChancePercent); - - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(spellId); - - mSpellWindow->setTitle(spell->mName); -} - -void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item) -{ - const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get() - .find(MWWorld::Class::get(item).getEnchantment(item)); - - int chargePercent = item.getCellRef().mEnchantmentCharge / static_cast(ench->mData.mCharge) * 100; - mHud->setSelectedEnchantItem(item, chargePercent); - mSpellWindow->setTitle(MWWorld::Class::get(item).getName(item)); -} - -void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item) -{ - int durabilityPercent = item.getCellRef().mCharge / static_cast(MWWorld::Class::get(item).getItemMaxHealth(item)) * 100; - mHud->setSelectedWeapon(item, durabilityPercent); - mInventoryWindow->setTitle(MWWorld::Class::get(item).getName(item)); -} - -void WindowManager::unsetSelectedSpell() -{ - mHud->unsetSelectedSpell(); - mSpellWindow->setTitle("#{sNone}"); -} - -void WindowManager::unsetSelectedWeapon() -{ - mHud->unsetSelectedWeapon(); - mInventoryWindow->setTitle("#{sSkillHandtohand}"); -} - -void WindowManager::getMousePosition(int &x, int &y) -{ - const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); - x = pos.left; - y = pos.top; -} - -void WindowManager::getMousePosition(float &x, float &y) -{ - const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); - x = pos.left; - y = pos.top; - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - x /= viewSize.width; - y /= viewSize.height; -} - -bool WindowManager::getWorldMouseOver() -{ - return mHud->getWorldMouseOver(); -} - -void WindowManager::executeInConsole (const std::string& path) -{ - mConsole->executeFile (path); -} - -void WindowManager::wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) -{ - mFPS = fps; - mTriangleCount = triangleCount; - mBatchCount = batchCount; -} - -MyGUI::Gui* WindowManager::getGui() const { return mGui; } - -MWGui::DialogueWindow* WindowManager::getDialogueWindow() { return mDialogueWindow; } -MWGui::ContainerWindow* WindowManager::getContainerWindow() { return mContainerWindow; } -MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; } -MWGui::BookWindow* WindowManager::getBookWindow() { return mBookWindow; } -MWGui::ScrollWindow* WindowManager::getScrollWindow() { return mScrollWindow; } -MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } -MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } -MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; } -MWGui::SpellBuyingWindow* WindowManager::getSpellBuyingWindow() { return mSpellBuyingWindow; } -MWGui::TravelWindow* WindowManager::getTravelWindow() { return mTravelWindow; } -MWGui::SpellWindow* WindowManager::getSpellWindow() { return mSpellWindow; } -MWGui::Console* WindowManager::getConsole() { return mConsole; } - -bool WindowManager::isAllowed (GuiWindow wnd) const -{ - return mAllowed & wnd; -} - -void WindowManager::allow (GuiWindow wnd) -{ - mAllowed = (GuiWindow)(mAllowed | wnd); - - if (wnd & GW_Inventory) + void WindowManager::setInteriorMapTexture(const int x, const int y) { - mBookWindow->setInventoryAllowed (true); - mScrollWindow->setInventoryAllowed (true); + mMap->setActiveCell(x,y, true); + mHud->setActiveCell(x,y, true); + } + + void WindowManager::setPlayerPos(const float x, const float y) + { + mMap->setPlayerPos(x,y); + mHud->setPlayerPos(x,y); + } + + void WindowManager::setPlayerDir(const float x, const float y) + { + mMap->setPlayerDir(x,y); + mHud->setPlayerDir(x,y); + } + + void WindowManager::setHMSVisibility(bool visible) + { + mHud->setHmsVisible (visible); + } + + void WindowManager::setMinimapVisibility(bool visible) + { + mHud->setMinimapVisible (visible); + } + + void WindowManager::toggleFogOfWar() + { + mMap->toggleFogOfWar(); + mHud->toggleFogOfWar(); + } + + void WindowManager::setFocusObject(const MWWorld::Ptr& focus) + { + mToolTips->setFocusObject(focus); + } + + void WindowManager::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) + { + mToolTips->setFocusObjectScreenCoords(min_x, min_y, max_x, max_y); + } + + void WindowManager::toggleFullHelp() + { + mToolTips->toggleFullHelp(); + } + + bool WindowManager::getFullHelp() const + { + return mToolTips->getFullHelp(); + } + + void WindowManager::setWeaponVisibility(bool visible) + { + mHud->setWeapVisible (visible); + } + + void WindowManager::setSpellVisibility(bool visible) + { + mHud->setSpellVisible (visible); + mHud->setEffectVisible (visible); + } + + void WindowManager::setMouseVisible(bool visible) + { + mCursor->setVisible(visible); + } + + void WindowManager::setDragDrop(bool dragDrop) + { + mToolTips->setEnabled(!dragDrop); + MWBase::Environment::get().getInputManager()->setDragDrop(dragDrop); + } + + void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result) + { + std::string tag(_tag); + + std::string tokenToFind = "sCell="; + size_t tokenLength = tokenToFind.length(); + + if (tag.substr(0, tokenLength) == tokenToFind) + { + _result = mTranslationDataStorage.translateCellName(tag.substr(tokenLength)); + } + else + { + const ESM::GameSetting *setting = + MWBase::Environment::get().getWorld()->getStore().get().find(tag); + + if (setting && setting->mValue.getType()==ESM::VT_String) + _result = setting->mValue.getString(); + else + _result = tag; + } + } + + void WindowManager::processChangedSettings(const Settings::CategorySettingVector& changed) + { + mHud->setFpsLevel(Settings::Manager::getInt("fps", "HUD")); + mToolTips->setDelay(Settings::Manager::getFloat("tooltip delay", "GUI")); + + bool changeRes = false; + bool windowRecreated = false; + for (Settings::CategorySettingVector::const_iterator it = changed.begin(); + it != changed.end(); ++it) + { + if (it->first == "Video" && ( + it->second == "resolution x" + || it->second == "resolution y")) + { + changeRes = true; + } + else if (it->first == "Video" && it->second == "vsync") + windowRecreated = true; + else if (it->first == "HUD" && it->second == "crosshair") + mCrosshairEnabled = Settings::Manager::getBool ("crosshair", "HUD"); + else if (it->first == "GUI" && it->second == "subtitles") + mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI"); + } + + if (changeRes) + { + int x = Settings::Manager::getInt("resolution x", "Video"); + int y = Settings::Manager::getInt("resolution y", "Video"); + mHud->onResChange(x, y); + mConsole->onResChange(x, y); + mMenu->onResChange(x, y); + mSettingsWindow->center(); + mAlchemyWindow->center(); + mScrollWindow->center(); + mBookWindow->center(); + mQuickKeysMenu->center(); + mSpellBuyingWindow->center(); + mLoadingScreen->onResChange (x,y); + mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); + mInputBlocker->setSize(MyGUI::IntSize(x,y)); + } + if (windowRecreated) + { + mGuiManager->updateWindow (mRendering->getWindow ()); + mLoadingScreen->updateWindow (mRendering->getWindow ()); + } + } + + void WindowManager::pushGuiMode(GuiMode mode) + { + if (mode==GM_Inventory && mAllowed==GW_None) + return; + + + // If this mode already exists somewhere in the stack, just bring it to the front. + if (std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end()) + { + mGuiModes.erase(std::find(mGuiModes.begin(), mGuiModes.end(), mode)); + } + + mGuiModes.push_back(mode); + + bool gameMode = !isGuiMode(); + MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + + updateVisible(); + } + + void WindowManager::popGuiMode() + { + if (!mGuiModes.empty()) + mGuiModes.pop_back(); + + bool gameMode = !isGuiMode(); + MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + + updateVisible(); + } + + void WindowManager::removeGuiMode(GuiMode mode) + { + std::vector::iterator it = mGuiModes.begin(); + while (it != mGuiModes.end()) + { + if (*it == mode) + it = mGuiModes.erase(it); + else + ++it; + } + + bool gameMode = !isGuiMode(); + MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); + + updateVisible(); + } + + void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent) + { + mHud->setSelectedSpell(spellId, successChancePercent); + + const ESM::Spell* spell = + MWBase::Environment::get().getWorld()->getStore().get().find(spellId); + + mSpellWindow->setTitle(spell->mName); + } + + void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item) + { + const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get() + .find(MWWorld::Class::get(item).getEnchantment(item)); + + int chargePercent = item.getCellRef().mEnchantmentCharge / static_cast(ench->mData.mCharge) * 100; + mHud->setSelectedEnchantItem(item, chargePercent); + mSpellWindow->setTitle(MWWorld::Class::get(item).getName(item)); + } + + void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item) + { + int durabilityPercent = item.getCellRef().mCharge / static_cast(MWWorld::Class::get(item).getItemMaxHealth(item)) * 100; + mHud->setSelectedWeapon(item, durabilityPercent); + mInventoryWindow->setTitle(MWWorld::Class::get(item).getName(item)); + } + + void WindowManager::unsetSelectedSpell() + { + mHud->unsetSelectedSpell(); + mSpellWindow->setTitle("#{sNone}"); + } + + void WindowManager::unsetSelectedWeapon() + { + mHud->unsetSelectedWeapon(); + mInventoryWindow->setTitle("#{sSkillHandtohand}"); + } + + void WindowManager::getMousePosition(int &x, int &y) + { + const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); + x = pos.left; + y = pos.top; + } + + void WindowManager::getMousePosition(float &x, float &y) + { + const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); + x = pos.left; + y = pos.top; + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + x /= viewSize.width; + y /= viewSize.height; + } + + bool WindowManager::getWorldMouseOver() + { + return mHud->getWorldMouseOver(); + } + + void WindowManager::executeInConsole (const std::string& path) + { + mConsole->executeFile (path); + } + + void WindowManager::wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) + { + mFPS = fps; + mTriangleCount = triangleCount; + mBatchCount = batchCount; + } + + MyGUI::Gui* WindowManager::getGui() const { return mGui; } + + MWGui::DialogueWindow* WindowManager::getDialogueWindow() { return mDialogueWindow; } + MWGui::ContainerWindow* WindowManager::getContainerWindow() { return mContainerWindow; } + MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; } + MWGui::BookWindow* WindowManager::getBookWindow() { return mBookWindow; } + MWGui::ScrollWindow* WindowManager::getScrollWindow() { return mScrollWindow; } + MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } + MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } + MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; } + MWGui::SpellBuyingWindow* WindowManager::getSpellBuyingWindow() { return mSpellBuyingWindow; } + MWGui::TravelWindow* WindowManager::getTravelWindow() { return mTravelWindow; } + MWGui::SpellWindow* WindowManager::getSpellWindow() { return mSpellWindow; } + MWGui::Console* WindowManager::getConsole() { return mConsole; } + + bool WindowManager::isAllowed (GuiWindow wnd) const + { + return mAllowed & wnd; + } + + void WindowManager::allow (GuiWindow wnd) + { + mAllowed = (GuiWindow)(mAllowed | wnd); + + if (wnd & GW_Inventory) + { + mBookWindow->setInventoryAllowed (true); + mScrollWindow->setInventoryAllowed (true); + } + + updateVisible(); + } + + void WindowManager::disallowAll() + { + mAllowed = GW_None; + + mBookWindow->setInventoryAllowed (false); + mScrollWindow->setInventoryAllowed (false); + + updateVisible(); + } + + void WindowManager::toggleVisible (GuiWindow wnd) + { + mShown = (mShown & wnd) ? (GuiWindow) (mShown & ~wnd) : (GuiWindow) (mShown | wnd); + updateVisible(); + } + + bool WindowManager::isGuiMode() const + { + return !mGuiModes.empty() || mMessageBoxManager->isInteractiveMessageBox(); + } + + bool WindowManager::isConsoleMode() const + { + if (!mGuiModes.empty() && mGuiModes.back()==GM_Console) + return true; + return false; + } + + MWGui::GuiMode WindowManager::getMode() const + { + if (mGuiModes.empty()) + return GM_None; + return mGuiModes.back(); + } + + std::map > WindowManager::getPlayerSkillValues() + { + return mPlayerSkillValues; + } + + std::map > WindowManager::getPlayerAttributeValues() + { + return mPlayerAttributes; + } + + WindowManager::SkillList WindowManager::getPlayerMinorSkills() + { + return mPlayerMinorSkills; + } + + WindowManager::SkillList WindowManager::getPlayerMajorSkills() + { + return mPlayerMajorSkills; + } + + void WindowManager::disallowMouse() + { + mInputBlocker->setVisible (true); + } + + void WindowManager::allowMouse() + { + mInputBlocker->setVisible (!isGuiMode ()); + } + + void WindowManager::notifyInputActionBound () + { + mSettingsWindow->updateControlsBox (); + allowMouse(); + } + + void WindowManager::showCrosshair (bool show) + { + mHud->setCrosshairVisible (show && mCrosshairEnabled); + } + + void WindowManager::activateQuickKey (int index) + { + mQuickKeysMenu->activateQuickKey(index); + } + + bool WindowManager::getSubtitlesEnabled () + { + return mSubtitlesEnabled; + } + + void WindowManager::toggleHud () + { + mHudEnabled = !mHudEnabled; + mHud->setVisible (mHudEnabled); + } + + void WindowManager::setLoadingProgress (const std::string& stage, int depth, int current, int total) + { + mLoadingScreen->setLoadingProgress (stage, depth, current, total); + } + + void WindowManager::loadingDone () + { + mLoadingScreen->loadingDone (); + } + + bool WindowManager::getPlayerSleeping () + { + return mWaitDialog->getSleeping(); + } + + void WindowManager::wakeUpPlayer() + { + mWaitDialog->wakeUp(); + } + + void WindowManager::addVisitedLocation(const std::string& name, int x, int y) + { + mMap->addVisitedLocation (name, x, y); + } + + void WindowManager::startSpellMaking(MWWorld::Ptr actor) + { + mSpellCreationDialog->startSpellMaking (actor); + } + + void WindowManager::startEnchanting (MWWorld::Ptr actor) + { + mEnchantingDialog->startEnchanting (actor); + } + + void WindowManager::startSelfEnchanting(MWWorld::Ptr soulgem) + { + mEnchantingDialog->startSelfEnchanting(soulgem); + } + + void WindowManager::startTraining(MWWorld::Ptr actor) + { + mTrainingWindow->startTraining(actor); + } + + void WindowManager::startRepair(MWWorld::Ptr actor) + { + mMerchantRepair->startRepair(actor); + } + + void WindowManager::startRepairItem(MWWorld::Ptr item) + { + mRepair->startRepairItem(item); + } + + const Translation::Storage& WindowManager::getTranslationDataStorage() const + { + return mTranslationDataStorage; + } + + void WindowManager::showCompanionWindow(MWWorld::Ptr actor) + { + mCompanionWindow->open(actor); + } + + void WindowManager::changePointer(const std::string &name) + { + mCursor->onCursorChange(name); + } + + void WindowManager::showSoulgemDialog(MWWorld::Ptr item) + { + mSoulgemDialog->show(item); + } + + void WindowManager::frameStarted (float dt) + { + mInventoryWindow->doRenderUpdate (); } - updateVisible(); -} - -void WindowManager::disallowAll() -{ - mAllowed = GW_None; - - mBookWindow->setInventoryAllowed (false); - mScrollWindow->setInventoryAllowed (false); - - updateVisible(); -} - -void WindowManager::toggleVisible (GuiWindow wnd) -{ - mShown = (mShown & wnd) ? (GuiWindow) (mShown & ~wnd) : (GuiWindow) (mShown | wnd); - updateVisible(); -} - -bool WindowManager::isGuiMode() const -{ - return !mGuiModes.empty() || mMessageBoxManager->isInteractiveMessageBox(); -} - -bool WindowManager::isConsoleMode() const -{ - if (!mGuiModes.empty() && mGuiModes.back()==GM_Console) - return true; - return false; -} - -MWGui::GuiMode WindowManager::getMode() const -{ - if (mGuiModes.empty()) - return GM_None; - return mGuiModes.back(); -} - -std::map > WindowManager::getPlayerSkillValues() -{ - return mPlayerSkillValues; -} - -std::map > WindowManager::getPlayerAttributeValues() -{ - return mPlayerAttributes; -} - -WindowManager::SkillList WindowManager::getPlayerMinorSkills() -{ - return mPlayerMinorSkills; -} - -WindowManager::SkillList WindowManager::getPlayerMajorSkills() -{ - return mPlayerMajorSkills; -} - -void WindowManager::disallowMouse() -{ - mInputBlocker->setVisible (true); -} - -void WindowManager::allowMouse() -{ - mInputBlocker->setVisible (!isGuiMode ()); -} - -void WindowManager::notifyInputActionBound () -{ - mSettingsWindow->updateControlsBox (); - allowMouse(); -} - -void WindowManager::showCrosshair (bool show) -{ - mHud->setCrosshairVisible (show && mCrosshairEnabled); -} - -void WindowManager::activateQuickKey (int index) -{ - mQuickKeysMenu->activateQuickKey(index); -} - -bool WindowManager::getSubtitlesEnabled () -{ - return mSubtitlesEnabled; -} - -void WindowManager::toggleHud () -{ - mHudEnabled = !mHudEnabled; - mHud->setVisible (mHudEnabled); -} - -void WindowManager::setLoadingProgress (const std::string& stage, int depth, int current, int total) -{ - mLoadingScreen->setLoadingProgress (stage, depth, current, total); -} - -void WindowManager::loadingDone () -{ - mLoadingScreen->loadingDone (); -} - -bool WindowManager::getPlayerSleeping () -{ - return mWaitDialog->getSleeping(); -} - -void WindowManager::wakeUpPlayer() -{ - mWaitDialog->wakeUp(); -} - -void WindowManager::addVisitedLocation(const std::string& name, int x, int y) -{ - mMap->addVisitedLocation (name, x, y); -} - -void WindowManager::startSpellMaking(MWWorld::Ptr actor) -{ - mSpellCreationDialog->startSpellMaking (actor); -} - -void WindowManager::startEnchanting (MWWorld::Ptr actor) -{ - mEnchantingDialog->startEnchanting (actor); -} - -void WindowManager::startSelfEnchanting(MWWorld::Ptr soulgem) -{ - mEnchantingDialog->startSelfEnchanting(soulgem); -} - -void WindowManager::startTraining(MWWorld::Ptr actor) -{ - mTrainingWindow->startTraining(actor); -} - -void WindowManager::startRepair(MWWorld::Ptr actor) -{ - mMerchantRepair->startRepair(actor); -} - -void WindowManager::startRepairItem(MWWorld::Ptr item) -{ - mRepair->startRepairItem(item); -} - -const Translation::Storage& WindowManager::getTranslationDataStorage() const -{ - return mTranslationDataStorage; -} - -void WindowManager::showCompanionWindow(MWWorld::Ptr actor) -{ - mCompanionWindow->open(actor); -} - -void WindowManager::changePointer(const std::string &name) -{ - mCursor->onCursorChange(name); -} - -void WindowManager::showSoulgemDialog(MWWorld::Ptr item) -{ - mSoulgemDialog->show(item); -} - -void WindowManager::frameStarted (float dt) -{ - mInventoryWindow->doRenderUpdate (); } diff --git a/apps/openmw/mwgui/windowpinnablebase.cpp b/apps/openmw/mwgui/windowpinnablebase.cpp index 53ba1b9fd..e5a94fc72 100644 --- a/apps/openmw/mwgui/windowpinnablebase.cpp +++ b/apps/openmw/mwgui/windowpinnablebase.cpp @@ -2,25 +2,26 @@ #include "exposedwindow.hpp" -using namespace MWGui; - -WindowPinnableBase::WindowPinnableBase(const std::string& parLayout) - : WindowBase(parLayout), mPinned(false), mVisible(false) +namespace MWGui { - ExposedWindow* window = static_cast(mMainWidget); - mPinButton = window->getSkinWidget ("Button"); + WindowPinnableBase::WindowPinnableBase(const std::string& parLayout) + : WindowBase(parLayout), mPinned(false), mVisible(false) + { + ExposedWindow* window = static_cast(mMainWidget); + mPinButton = window->getSkinWidget ("Button"); - mPinButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WindowPinnableBase::onPinButtonClicked); -} - -void WindowPinnableBase::onPinButtonClicked(MyGUI::Widget* _sender) -{ - mPinned = !mPinned; - - if (mPinned) - mPinButton->changeWidgetSkin ("PinDown"); - else - mPinButton->changeWidgetSkin ("PinUp"); - - onPinToggled(); + mPinButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WindowPinnableBase::onPinButtonClicked); + } + + void WindowPinnableBase::onPinButtonClicked(MyGUI::Widget* _sender) + { + mPinned = !mPinned; + + if (mPinned) + mPinButton->changeWidgetSkin ("PinDown"); + else + mPinButton->changeWidgetSkin ("PinUp"); + + onPinToggled(); + } } From 3c5e4ceefd87140156bfa87c314242baef30676e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Apr 2013 16:46:32 +0200 Subject: [PATCH 17/99] Workaround for ambiguous "Chargen_plank" ID in chargen script (one at -22,16 and one at -2,-9) --- apps/openmw/mwworld/store.hpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index f69f606b4..fb3f8c482 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -415,8 +415,18 @@ namespace MWWorld } }; - typedef std::map DynamicInt; - typedef std::map, ESM::Cell> DynamicExt; + struct DynamicExtCmp + { + bool operator()(const std::pair &left, const std::pair &right) const { + if (left.first == right.first) { + return left.second < right.second; + } + return left.first < right.first; + } + }; + + typedef std::map DynamicInt; + typedef std::map, ESM::Cell, DynamicExtCmp> DynamicExt; DynamicInt mInt; DynamicExt mExt; From b34caccd2ec90a582e4732d29fb1fc7a10a86e92 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Apr 2013 16:46:38 +0200 Subject: [PATCH 18/99] Fix moving NPCs not getting their collision box moved --- libs/openengine/bullet/physic.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 524f57c1c..eaeae7dc3 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -511,8 +511,8 @@ namespace Physic void PhysicEngine::stepSimulation(double deltaT) { - // This isn't needed as there are no dynamic objects at this point - //dynamicsWorld->stepSimulation(deltaT,10, 1/60.0); + // This seems to be needed for character controller objects + dynamicsWorld->stepSimulation(deltaT,10, 1/60.0); if(isDebugCreated) { mDebugDrawer->step(); From c753eb4c287729759e897ef35fdd6b36b3acf429 Mon Sep 17 00:00:00 2001 From: gus Date: Thu, 18 Apr 2013 18:35:01 +0100 Subject: [PATCH 19/99] another way to do pathfinding. Slightly less powerfull algorithme in theory, but morrowind pathgrids are so simple it shouldn't be a problem. Hope it solves the bug for KittyCat --- apps/openmw/mwmechanics/pathfinding.cpp | 35 ++++++++++++++++++------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 7c22e5470..2f27d25f8 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -1,5 +1,5 @@ #include "pathfinding.hpp" -#include +#include #include #include "boost/tuple/tuple.hpp" #include "OgreMath.h" @@ -55,7 +55,7 @@ namespace struct found_path {}; - class goalVisited : public boost::default_astar_visitor + /*class goalVisited : public boost::default_astar_visitor { public: goalVisited(PointID goal) : mGoal(goal) {} @@ -69,7 +69,7 @@ namespace PointID mGoal; }; - class DistanceHeuristic : public boost::astar_heuristic + class DistanceHeuristic : public boost::atasr_heuristic { public: DistanceHeuristic(const PathGridGraph & l, PointID goal) @@ -87,11 +87,23 @@ namespace private: const PathGridGraph & mGraph; PointID mGoal; - }; -} + };*/ + + class goalVisited : public boost::default_dijkstra_visitor + { + public: + goalVisited(PointID goal) : mGoal(goal) {} + + void examine_vertex(PointID u, const PathGridGraph g) + { + if(u == mGoal) + throw found_path(); + } + private: + PointID mGoal; + }; + -namespace MWMechanics -{ PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid,float xCell = 0,float yCell = 0) { PathGridGraph graph; @@ -126,13 +138,12 @@ namespace MWMechanics std::list shortest_path; try { - boost::astar_search + boost::dijkstra_shortest_paths ( graph, start, - DistanceHeuristic(graph,end), boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end))//.weight_map(boost::get(&Edge::distance, graph)) - ); + ); } catch(found_path fg) { for(PointID v = end;; v = p[v]) { @@ -146,6 +157,10 @@ namespace MWMechanics //end of helpers functions +} + +namespace MWMechanics +{ PathFinder::PathFinder() { mIsPathConstructed = false; From f9deb593d1f5d6baba89b57cc8f5f3ba544917a6 Mon Sep 17 00:00:00 2001 From: Glorf Date: Thu, 18 Apr 2013 21:37:58 +0200 Subject: [PATCH 20/99] Bugfix #578 --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index d34c4b97f..66d20e662 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -548,7 +548,7 @@ namespace MWMechanics float bribeMod; if (type == PT_Bribe10) bribeMod = gmst.find("fBribe10Mod")->getFloat(); - if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->getFloat(); + else if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->getFloat(); else bribeMod = gmst.find("fBribe1000Mod")->getFloat(); float target3 = d * (playerRating3 - npcRating3 + 50) + bribeMod; From 3b3a052f4f7592b756583a0bc7fdadfe04b5a920 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 18 Apr 2013 22:36:48 +0200 Subject: [PATCH 21/99] increased version number --- CMakeLists.txt | 2 +- readme.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b6a101790..9f12b11fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ include (OpenMWMacros) # Version set (OPENMW_VERSION_MAJOR 0) -set (OPENMW_VERSION_MINOR 22) +set (OPENMW_VERSION_MINOR 23) set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") diff --git a/readme.txt b/readme.txt index 8edb0c4b3..9bce063fa 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.22.0 +Version: 0.23.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org From 257f9403660e82f3fed9231c6784429b13fdea38 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 18 Apr 2013 22:50:04 +0200 Subject: [PATCH 22/99] updated changelog --- readme.txt | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/readme.txt b/readme.txt index 9bce063fa..5f3845776 100644 --- a/readme.txt +++ b/readme.txt @@ -94,6 +94,65 @@ Allowed options: CHANGELOG +0.23.0 + +Bug #522: Player collides with placeable items +Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open +Bug #561: Tooltip word wrapping delay +Bug #578: Bribing works incorrectly +Bug #601: PositionCell fails on negative coordinates +Bug #606: Some NPCs hairs not rendered with Better Heads addon +Bug #609: Bad rendering of bone boots +Bug #613: Messagebox causing assert to fail +Bug #631: Segfault on shutdown +Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard +Bug #635: Scale NPCs depending on race +Bug #643: Dialogue Race select function is inverted +Bug #646: Twohanded weapons don't work properly +Bug #654: Crash when dropping objects without a collision shape +Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell +Bug #660: "g" in "change" cut off in Race Menu +Bug #661: Arrille sells me the key to his upstairs room +Bug #662: Day counter starts at 2 instead of 1 +Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur +Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window. +Bug #666: Looking up/down problem +Bug #667: Active effects border visible during loading +Bug #669: incorrect player position at new game start +Bug #670: race selection menu: sex, face and hair left button not totally clickable +Bug #671: new game: player is naked +Bug #674: buying or selling items doesn't change amount of gold +Bug #675: fatigue is not set to its maximum when starting a new game +Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly +Bug #680: different gold coins in Tel Mara +Bug #682: Race menu ignores playable flag for some hairs and faces +Bug #685: Script compiler does not accept ":" after a function name +Bug #688: dispose corpse makes cross-hair to disappear +Bug #691: Auto equipping ignores equipment conditions +Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder +Bug #696: Draugr incorrect head offset +Bug #697: Sail transparency issue +Bug #700: "On the rocks" mod does not load its UV coordinates correctly. +Bug #702: Some race mods don't work +Bug #711: Crash during character creation +Bug #715: Growing Tauryon +Feature #55/657: Item Repairing +Feature #62/87: Enchanting +Feature #99: Pathfinding +Feature #104: AI Package: Travel +Feature #129: Levelled items +Feature #204: Texture animations +Feature #239: Fallback-Settings +Feature #535: Console object selection improvements +Feature #629: Add levelup description in levelup layout dialog +Feature #630: Optional format subrecord in (tes3) header +Feature #641: Armor rating +Feature #645: OnDeath script function +Feature #683: Companion item UI +Feature #698: Basic Particles +Task #648: Split up components/esm/loadlocks +Task #695: mwgui cleanup + 0.22.0 Bug #311: Potential infinite recursion in script compiler From ef9afeb53e5b48fd3a903a1e26a8a869ef89e7ee Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Apr 2013 23:51:43 +0200 Subject: [PATCH 23/99] Auto calculate attributes if there are not specified in the NPC record --- apps/openmw/mwclass/npc.cpp | 66 +++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 7cda87bb1..ea49ae4a4 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -49,6 +49,67 @@ namespace { return new CustomData (*this); } + + void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats) + { + // race bonus + const ESM::Race *race = + MWBase::Environment::get().getWorld()->getStore().get().find(npc->mRace); + + bool male = (npc->mFlags & ESM::NPC::Female) == 0; + + int level = creatureStats.getLevel(); + + for (int i=0; imData.mAttributeValues[i]; + creatureStats.getAttribute(i).setBase (male ? attribute.mMale : attribute.mFemale); + } + + // class bonus + const ESM::Class *class_ = + MWBase::Environment::get().getWorld()->getStore().get().find(npc->mClass); + + for (int i=0; i<2; ++i) + { + int attribute = class_->mData.mAttribute[i]; + if (attribute>=0 && attribute<8) + { + creatureStats.getAttribute(attribute).setBase ( + creatureStats.getAttribute(attribute).getBase() + 10); + } + } + + // skill bonus + for (int attribute=0; attributegetStore().get().find(j); + + if (skill->mData.mAttribute != attribute) + continue; + + // is this a minor or major skill? + float add=0.2; + for (int k=0; k<5; ++k) + { + if (class_->mData.mSkills[k][0] == j) + add=0.5; + } + for (int k=0; k<5; ++k) + { + if (class_->mData.mSkills[k][1] == j) + add=1.0; + } + modifierSum += add; + } + creatureStats.getAttribute(attribute).setBase ( std::min(creatureStats.getAttribute(attribute).getBase() + + static_cast((level-1) * modifierSum+0.5), 100) ); + } + } } namespace MWClass @@ -126,15 +187,14 @@ namespace MWClass } else { - for (int i=0; i<8; ++i) - data->mCreatureStats.getAttribute (i).set (10); - for (int i=0; i<3; ++i) data->mCreatureStats.setDynamic (i, 10); data->mCreatureStats.setLevel(ref->mBase->mNpdt12.mLevel); data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt12.mDisposition); data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation); + + autoCalculateAttributes(ref->mBase, data->mCreatureStats); } data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello); From 92e06cebf3f6cbd0930529ab026221c5ff2c69ca Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 19 Apr 2013 00:26:36 +0200 Subject: [PATCH 24/99] last minute change to changelog --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index 5f3845776..d5a5dcc18 100644 --- a/readme.txt +++ b/readme.txt @@ -136,6 +136,7 @@ Bug #700: "On the rocks" mod does not load its UV coordinates correctly. Bug #702: Some race mods don't work Bug #711: Crash during character creation Bug #715: Growing Tauryon +Bug #725: Auto calculate stats Feature #55/657: Item Repairing Feature #62/87: Enchanting Feature #99: Pathfinding From e92897526c9fb6bc8fb1acf836b7b841374cc919 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 19 Apr 2013 01:33:04 +0200 Subject: [PATCH 25/99] Only use the shader based mygui manager if the fixed pipeline is unavailable --- libs/openengine/gui/manager.cpp | 30 ++++++++++++++++++++++++++---- libs/openengine/gui/manager.hpp | 5 +++-- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index f9117586f..a0a4ab0ae 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -559,6 +559,8 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool assert(mgr); mSceneMgr = mgr; + mShaderRenderManager = NULL; + mRenderManager = NULL; using namespace MyGUI; @@ -574,7 +576,10 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool // Set up OGRE platform (bypassing OgrePlatform). We might make this more generic later. mLogManager = new LogManager(); - mRenderManager = new MyGUI::ShaderBasedRenderManager(); + if (!Ogre::Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(Ogre::RSC_FIXED_FUNCTION)) + mShaderRenderManager = new MyGUI::ShaderBasedRenderManager(); + else + mRenderManager = new MyGUI::OgreRenderManager(); mDataManager = new MyGUI::FixedOgreDataManager(); LogManager::getInstance().setSTDOutputEnabled(logging); @@ -582,7 +587,10 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool if (!theLogFile.empty()) LogManager::getInstance().createDefaultSource(theLogFile); - mRenderManager->initialise(wnd, mgr); + if (mShaderRenderManager) + mShaderRenderManager->initialise(wnd, mgr); + else + mRenderManager->initialise(wnd, mgr); mDataManager->initialise("General"); // Create GUI @@ -592,8 +600,16 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool void MyGUIManager::updateWindow (Ogre::RenderWindow *wnd) { - mRenderManager->setRenderWindow (wnd); - mRenderManager->setActiveViewport(0); + if (mShaderRenderManager) + { + mShaderRenderManager->setRenderWindow (wnd); + mShaderRenderManager->setActiveViewport(0); + } + else + { + mRenderManager->setRenderWindow (wnd); + mRenderManager->setActiveViewport(0); + } } void MyGUIManager::shutdown() @@ -606,6 +622,12 @@ void MyGUIManager::shutdown() delete mRenderManager; mRenderManager = NULL; } + if(mShaderRenderManager) + { + mShaderRenderManager->shutdown(); + delete mShaderRenderManager; + mShaderRenderManager = NULL; + } if(mDataManager) { mDataManager->shutdown(); diff --git a/libs/openengine/gui/manager.hpp b/libs/openengine/gui/manager.hpp index eec867ff8..9535f2a24 100644 --- a/libs/openengine/gui/manager.hpp +++ b/libs/openengine/gui/manager.hpp @@ -8,6 +8,7 @@ namespace MyGUI class Gui; class LogManager; class OgreDataManager; + class OgreRenderManager; class ShaderBasedRenderManager; } @@ -25,12 +26,12 @@ namespace GUI MyGUI::Gui *mGui; MyGUI::LogManager* mLogManager; MyGUI::OgreDataManager* mDataManager; - MyGUI::ShaderBasedRenderManager* mRenderManager; + MyGUI::OgreRenderManager* mRenderManager; + MyGUI::ShaderBasedRenderManager* mShaderRenderManager; Ogre::SceneManager* mSceneMgr; public: - MyGUIManager() : mLogManager(NULL), mDataManager(NULL), mRenderManager(NULL), mGui(NULL) {} MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")) { setup(wnd,mgr,logging, logDir); From 50bb92beb349f340801923565db15c1c78832c38 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 19 Apr 2013 02:01:50 -0700 Subject: [PATCH 26/99] Use a method to set the visibility flags and render queue. --- apps/openmw/mwrender/activatoranimation.cpp | 24 +------------------ apps/openmw/mwrender/animation.cpp | 26 +++++++++++++++++++++ apps/openmw/mwrender/animation.hpp | 2 ++ apps/openmw/mwrender/creatureanimation.cpp | 25 +------------------- apps/openmw/mwrender/npcanimation.cpp | 21 ++++------------- 5 files changed, 35 insertions(+), 63 deletions(-) diff --git a/apps/openmw/mwrender/activatoranimation.cpp b/apps/openmw/mwrender/activatoranimation.cpp index 1f9a2e23c..c3a3045c2 100644 --- a/apps/openmw/mwrender/activatoranimation.cpp +++ b/apps/openmw/mwrender/activatoranimation.cpp @@ -1,10 +1,5 @@ #include "activatoranimation.hpp" -#include -#include -#include -#include - #include "renderconst.hpp" #include "../mwbase/world.hpp" @@ -27,24 +22,7 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) const std::string name = "meshes\\"+ref->mBase->mModel; addObjectList(mPtr.getRefData().getBaseNode(), name, false); - const NifOgre::ObjectList &objlist = mObjectLists.back(); - for(size_t i = 0;i < objlist.mEntities.size();i++) - { - Ogre::Entity *ent = objlist.mEntities[i]; - ent->setVisibilityFlags(RV_Misc); - - for(unsigned int j=0; j < ent->getNumSubEntities(); ++j) - { - Ogre::SubEntity* subEnt = ent->getSubEntity(j); - subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); - } - } - for(size_t i = 0;i < objlist.mParticles.size();i++) - { - Ogre::ParticleSystem *part = objlist.mParticles[i]; - part->setVisibilityFlags(RV_Misc); - part->setRenderQueueGroup(RQG_Alpha); - } + setRenderProperties(mObjectLists.back(), RV_Misc, RQG_Main, RQG_Alpha); } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 11876a247..8e5bd0108 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -150,6 +152,30 @@ void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, b mCurrentControllers = &objlist.mControllers; } +void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue) +{ + for(size_t i = 0;i < objlist.mEntities.size();i++) + { + Ogre::Entity *ent = objlist.mEntities[i]; + if(visflags != 0) + ent->setVisibilityFlags(visflags); + + for(unsigned int j = 0;j < ent->getNumSubEntities();++j) + { + Ogre::SubEntity* subEnt = ent->getSubEntity(j); + subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? transqueue : solidqueue); + } + } + for(size_t i = 0;i < objlist.mParticles.size();i++) + { + Ogre::ParticleSystem *part = objlist.mParticles[i]; + if(visflags != 0) + part->setVisibilityFlags(visflags); + // TODO: Check particle material for actual transparency + part->setRenderQueueGroup(transqueue); + } +} + Ogre::Node *Animation::getNode(const std::string &name) { diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index f8ddceeaa..db2195c34 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -85,6 +85,8 @@ protected: void addObjectList(Ogre::SceneNode *node, const std::string &model, bool baseonly); static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); + static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue); + public: Animation(const MWWorld::Ptr &ptr); virtual ~Animation(); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index a48b8da9f..51632b2ee 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -1,10 +1,5 @@ #include "creatureanimation.hpp" -#include -#include -#include -#include - #include "renderconst.hpp" #include "../mwbase/world.hpp" @@ -30,25 +25,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) addObjectList(mPtr.getRefData().getBaseNode(), "meshes\\base_anim.nif", true); addObjectList(mPtr.getRefData().getBaseNode(), model, false); - - const NifOgre::ObjectList &objlist = mObjectLists.back(); - for(size_t i = 0;i < objlist.mEntities.size();i++) - { - Ogre::Entity *ent = objlist.mEntities[i]; - ent->setVisibilityFlags(RV_Actors); - - for(unsigned int j=0; j < ent->getNumSubEntities(); ++j) - { - Ogre::SubEntity* subEnt = ent->getSubEntity(j); - subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); - } - } - for(size_t i = 0;i < objlist.mParticles.size();i++) - { - Ogre::ParticleSystem *part = objlist.mParticles[i]; - part->setVisibilityFlags(RV_Actors); - part->setRenderQueueGroup(RQG_Alpha); - } + setRenderProperties(mObjectLists.back(), RV_Actors, RQG_Main, RQG_Alpha); } } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index cea0225ef..0b05b4ba6 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -361,25 +361,13 @@ void NpcAnimation::updateParts(bool forceupdate) NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename) { NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); - for(size_t i = 0;i < objects.mEntities.size();i++) - { - objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); - if(mVisibilityFlags != 0) - objects.mEntities[i]->setVisibilityFlags(mVisibilityFlags); + setRenderProperties(objects, mVisibilityFlags, RQG_Main, RQG_Alpha); - for(unsigned int j=0; j < objects.mEntities[i]->getNumSubEntities(); ++j) - { - Ogre::SubEntity* subEnt = objects.mEntities[i]->getSubEntity(j); - subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); - } - } + for(size_t i = 0;i < objects.mEntities.size();i++) + objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); for(size_t i = 0;i < objects.mParticles.size();i++) - { objects.mParticles[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); - if(mVisibilityFlags != 0) - objects.mParticles[i]->setVisibilityFlags(mVisibilityFlags); - objects.mParticles[i]->setRenderQueueGroup(RQG_Alpha); - } + if(objects.mSkelBase) { Ogre::AnimationStateSet *aset = objects.mSkelBase->getAllAnimationStates(); @@ -395,6 +383,7 @@ NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, in while(boneiter.hasMoreElements()) boneiter.getNext()->setManuallyControlled(true); } + return objects; } From f13b0c884fa1eb4a93060a1722f45677bf458d08 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 19 Apr 2013 14:41:26 +0200 Subject: [PATCH 27/99] hotfix for CharacterPreview destructor --- apps/openmw/mwrender/characterpreview.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 8396acaea..41770d489 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -21,13 +21,15 @@ namespace MWRender CharacterPreview::CharacterPreview(MWWorld::Ptr character, int sizeX, int sizeY, const std::string& name, Ogre::Vector3 position, Ogre::Vector3 lookAt) - : mSizeX(sizeX) - , mSizeY(sizeY) - , mName(name) + + : mSceneMgr (0) , mPosition(position) , mLookAt(lookAt) , mCharacter(character) , mAnimation(NULL) + , mName(name) + , mSizeX(sizeX) + , mSizeY(sizeY) { } @@ -88,16 +90,20 @@ namespace MWRender CharacterPreview::~CharacterPreview () { - //Ogre::TextureManager::getSingleton().remove(mName); - mSceneMgr->destroyCamera (mName); - delete mAnimation; - Ogre::Root::getSingleton().destroySceneManager(mSceneMgr); + if (mSceneMgr) + { + //Ogre::TextureManager::getSingleton().remove(mName); + mSceneMgr->destroyAllCameras(); + delete mAnimation; + Ogre::Root::getSingleton().destroySceneManager(mSceneMgr); + } } void CharacterPreview::rebuild() { assert(mAnimation); delete mAnimation; + mAnimation = 0; mAnimation = new NpcAnimation(mCharacter, mNode, MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter), 0, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal)); From a8e3acd6c4db2c502e3f1de49bb054d53ba4ca5f Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 19 Apr 2013 17:57:22 +0200 Subject: [PATCH 28/99] Fix consecutive dialogue choices --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 9380ab76c..220adf566 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -426,6 +426,7 @@ namespace MWDialogue void DialogueManager::questionAnswered (const std::string& answer) { + if (mChoiceMap.find(answer) != mChoiceMap.end()) { mChoice = mChoiceMap[answer]; @@ -442,6 +443,10 @@ namespace MWDialogue std::string text = info->mResponse; parseText (text); + mChoiceMap.clear(); + mChoice = -1; + mIsInChoice = false; + MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (Interpreter::fixDefinesDialog(text, interpreterContext)); MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId); @@ -449,9 +454,6 @@ namespace MWDialogue mLastDialogue = *info; } } - mChoiceMap.clear(); - mChoice = -1; - mIsInChoice = false; } updateTopics(); From 1051fa51a71f964fa55a73d054c558862ee95ed0 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 19 Apr 2013 15:07:08 -0700 Subject: [PATCH 29/99] Recognize NiFlipController --- components/nif/controller.hpp | 23 +++++++++++++++++++++++ components/nif/niffile.cpp | 1 + components/nif/record.hpp | 1 + components/nif/recordptr.hpp | 1 + 4 files changed, 26 insertions(+) diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index aa6a9ef4f..011e0e445 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -303,5 +303,28 @@ public: } }; +class NiFlipController : public Controller +{ +public: + int mTexSlot; + float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources + NiSourceTextureList mSources; + + void read(NIFStream *nif) + { + Controller::read(nif); + mTexSlot = nif->getUInt(); + /*unknown=*/nif->getUInt();/*0?*/ + mDelta = nif->getFloat(); + mSources.read(nif); + } + + void post(NIFFile *nif) + { + Controller::post(nif); + mSources.post(nif); + } +}; + } // Namespace #endif diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 2c4f3506e..a66bd99db 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -235,6 +235,7 @@ static const RecordFactoryEntry recordFactories [] = { { "NiMaterialColorController", &construct , RC_NiMaterialColorController }, { "NiBSPArrayController", &construct , RC_NiBSPArrayController }, { "NiParticleSystemController", &construct , RC_NiParticleSystemController }, + { "NiFlipController", &construct , RC_NiFlipController }, { "NiAmbientLight", &construct , RC_NiLight }, { "NiDirectionalLight", &construct , RC_NiLight }, { "NiTextureEffect", &construct , RC_NiTextureEffect }, diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 97b10503e..3f852ed83 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -60,6 +60,7 @@ enum RecordType RC_NiMaterialColorController, RC_NiBSPArrayController, RC_NiParticleSystemController, + RC_NiFlipController, RC_NiBSAnimationNode, RC_NiLight, RC_NiTextureEffect, diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index c5bafea12..2ecde7f60 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -163,6 +163,7 @@ typedef RecordPtrT NiAutoNormalParticlesDataPtr; typedef RecordListT NodeList; typedef RecordListT PropertyList; +typedef RecordListT NiSourceTextureList; } // Namespace #endif From f37a71f02584dddfe50655dea4689aaea4bf89f3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 19 Apr 2013 23:56:47 -0700 Subject: [PATCH 30/99] Use a separate method to build node-based controllers --- components/nifogre/ogrenifloader.cpp | 77 +++++++++++++++------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index c5cc45ae9..21acfca0f 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -575,6 +575,44 @@ class NIFObjectLoader } + static void createNodeControllers(const std::string &name, Nif::ControllerPtr ctrl, ObjectList &objectlist, int animflags) + { + do { + if(ctrl->recType == Nif::RC_NiVisController) + { + const Nif::NiVisController *vis = static_cast(ctrl.getPtr()); + + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); + + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + else if(ctrl->recType == Nif::RC_NiKeyframeController) + { + const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); + if(!key->data.empty()) + { + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); + + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } + } + ctrl = ctrl->next; + } while(!ctrl.empty()); + } + + static void createObjects(const std::string &name, const std::string &group, Ogre::SceneManager *sceneMgr, const Nif::Node *node, ObjectList &objectlist, int flags, int animflags, int partflags) @@ -613,6 +651,9 @@ class NIFObjectLoader e = e->extra; } + if(!node->controller.empty()) + createNodeControllers(name, node->controller, objectlist, animflags); + if(node->recType == Nif::RC_NiCamera) { int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); @@ -620,42 +661,6 @@ class NIFObjectLoader objectlist.mCameras.push_back(trgtbone); } - Nif::ControllerPtr ctrl = node->controller; - while(!ctrl.empty()) - { - if(ctrl->recType == Nif::RC_NiVisController) - { - const Nif::NiVisController *vis = static_cast(ctrl.getPtr()); - - int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); - Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); - Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? - Ogre::ControllerManager::getSingleton().getFrameTimeSource() : - Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr())); - Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); - - objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); - } - else if(ctrl->recType == Nif::RC_NiKeyframeController) - { - const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); - if(!key->data.empty()) - { - int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex); - Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); - Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? - Ogre::ControllerManager::getSingleton().getFrameTimeSource() : - Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); - Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); - - objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); - } - } - ctrl = ctrl->next; - } - if(node->recType == Nif::RC_NiTriShape && !(flags&0x80000000)) { createEntity(name, group, sceneMgr, objectlist, node, flags, animflags); From ace7d647e5af1182a972505bb6f3aa1a01ee16d3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 20 Apr 2013 15:35:19 -0700 Subject: [PATCH 31/99] Add a method to apply a NodeTargetValue-based controller to a specified node. --- components/nifogre/ogrenifloader.cpp | 147 +++++++++++++++------------ components/nifogre/ogrenifloader.hpp | 2 + 2 files changed, 82 insertions(+), 67 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 21acfca0f..90147a43b 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -104,7 +104,7 @@ public: private: std::vector mData; - virtual bool calculate(Ogre::Real time) + bool calculate(Ogre::Real time) const { if(mData.size() == 0) return true; @@ -144,16 +144,21 @@ public: , mData(data->mVis) { } + virtual void applyToNode(Ogre::Node *node, float time) const + { + bool vis = calculate(time); + setVisible(node, vis); + } + virtual Ogre::Real getValue() const { // Should not be called - return 1.0f; + return 0.0f; } virtual void setValue(Ogre::Real time) { - bool vis = calculate(time); - setVisible(mNode, vis); + Value::applyToNode(mNode, time); } }; @@ -170,6 +175,66 @@ public: Nif::Vector3KeyList mTranslations; Nif::FloatKeyList mScales; + static float interpKey(const Nif::FloatKeyList::VecType &keys, float time) + { + if(time <= keys.front().mTime) + return keys.front().mValue; + if(time >= keys.back().mTime) + return keys.back().mValue; + + Nif::FloatKeyList::VecType::const_iterator iter(keys.begin()+1); + for(;iter != keys.end();iter++) + { + if(iter->mTime < time) + continue; + + Nif::FloatKeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + return last->mValue + ((iter->mValue - last->mValue)*a); + } + return keys.back().mValue; + } + + static Ogre::Vector3 interpKey(const Nif::Vector3KeyList::VecType &keys, float time) + { + if(time <= keys.front().mTime) + return keys.front().mValue; + if(time >= keys.back().mTime) + return keys.back().mValue; + + Nif::Vector3KeyList::VecType::const_iterator iter(keys.begin()+1); + for(;iter != keys.end();iter++) + { + if(iter->mTime < time) + continue; + + Nif::Vector3KeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + return last->mValue + ((iter->mValue - last->mValue)*a); + } + return keys.back().mValue; + } + + static Ogre::Quaternion interpKey(const Nif::QuaternionKeyList::VecType &keys, float time) + { + if(time <= keys.front().mTime) + return keys.front().mValue; + if(time >= keys.back().mTime) + return keys.back().mValue; + + Nif::QuaternionKeyList::VecType::const_iterator iter(keys.begin()+1); + for(;iter != keys.end();iter++) + { + if(iter->mTime < time) + continue; + + Nif::QuaternionKeyList::VecType::const_iterator last(iter-1); + float a = (time-last->mTime) / (iter->mTime-last->mTime); + return Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue); + } + return Ogre::Quaternion(); + } + public: Value(Ogre::Node *target, const Nif::NiKeyframeData *data) : NodeTargetValue(target) @@ -178,6 +243,16 @@ public: , mScales(data->mScales) { } + virtual void applyToNode(Ogre::Node *node, float time) const + { + if(mRotations.mKeys.size() > 0) + node->setOrientation(interpKey(mRotations.mKeys, time)); + if(mTranslations.mKeys.size() > 0) + node->setPosition(interpKey(mTranslations.mKeys, time)); + if(mScales.mKeys.size() > 0) + node->setScale(Ogre::Vector3(interpKey(mScales.mKeys, time))); + } + virtual Ogre::Real getValue() const { // Should not be called @@ -186,69 +261,7 @@ public: virtual void setValue(Ogre::Real time) { - if(mRotations.mKeys.size() > 0) - { - if(time <= mRotations.mKeys.front().mTime) - mNode->setOrientation(mRotations.mKeys.front().mValue); - else if(time >= mRotations.mKeys.back().mTime) - mNode->setOrientation(mRotations.mKeys.back().mValue); - else - { - Nif::QuaternionKeyList::VecType::const_iterator iter(mRotations.mKeys.begin()+1); - for(;iter != mRotations.mKeys.end();iter++) - { - if(iter->mTime < time) - continue; - - Nif::QuaternionKeyList::VecType::const_iterator last(iter-1); - float a = (time-last->mTime) / (iter->mTime-last->mTime); - mNode->setOrientation(Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue)); - break; - } - } - } - if(mTranslations.mKeys.size() > 0) - { - if(time <= mTranslations.mKeys.front().mTime) - mNode->setPosition(mTranslations.mKeys.front().mValue); - else if(time >= mTranslations.mKeys.back().mTime) - mNode->setPosition(mTranslations.mKeys.back().mValue); - else - { - Nif::Vector3KeyList::VecType::const_iterator iter(mTranslations.mKeys.begin()+1); - for(;iter != mTranslations.mKeys.end();iter++) - { - if(iter->mTime < time) - continue; - - Nif::Vector3KeyList::VecType::const_iterator last(iter-1); - float a = (time-last->mTime) / (iter->mTime-last->mTime); - mNode->setPosition(last->mValue + ((iter->mValue - last->mValue)*a)); - break; - } - } - } - if(mScales.mKeys.size() > 0) - { - if(time <= mScales.mKeys.front().mTime) - mNode->setScale(Ogre::Vector3(mScales.mKeys.front().mValue)); - else if(time >= mScales.mKeys.back().mTime) - mNode->setScale(Ogre::Vector3(mScales.mKeys.back().mValue)); - else - { - Nif::FloatKeyList::VecType::const_iterator iter(mScales.mKeys.begin()+1); - for(;iter != mScales.mKeys.end();iter++) - { - if(iter->mTime < time) - continue; - - Nif::FloatKeyList::VecType::const_iterator last(iter-1); - float a = (time-last->mTime) / (iter->mTime-last->mTime); - mNode->setScale(Ogre::Vector3(last->mValue + ((iter->mValue - last->mValue)*a))); - break; - } - } - } + Value::applyToNode(mNode, time); } }; diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index e3bb55064..b53815fa0 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -85,6 +85,8 @@ public: NodeTargetValue(Ogre::Node *target) : mNode(target) { } + virtual void applyToNode(Ogre::Node *node, float time) const = 0; + void setNode(Ogre::Node *target) { mNode = target; } Ogre::Node *getNode() const From 835ff8eb1eb36bc16cffaca85343ee86d1533275 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 20 Apr 2013 17:13:27 -0700 Subject: [PATCH 32/99] Avoid creating an AnimationValue per ObjectList --- apps/openmw/mwrender/animation.cpp | 6 +++--- apps/openmw/mwrender/animation.hpp | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8e5bd0108..56b601727 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -32,7 +32,8 @@ void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectL } Animation::Animation(const MWWorld::Ptr &ptr) - : mPtr(ptr) + : mAnimationBaseValuePtr(OGRE_NEW AnimationValue(this)) + , mPtr(ptr) , mController(NULL) , mInsert(NULL) , mSkelBase(NULL) @@ -70,7 +71,6 @@ void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, b mInsert = node->createChildSceneNode(); assert(mInsert); } - Ogre::SharedPtr > ctrlval(OGRE_NEW AnimationValue(this)); mObjectLists.push_back(!baseonly ? NifOgre::Loader::createObjects(mInsert, model) : NifOgre::Loader::createObjectBase(mInsert, model)); @@ -145,7 +145,7 @@ void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, b for(size_t i = 0;i < objlist.mControllers.size();i++) { if(objlist.mControllers[i].getSource().isNull()) - objlist.mControllers[i].setSource(ctrlval); + objlist.mControllers[i].setSource(mAnimationBaseValuePtr); } if(!mCurrentControllers || (*mCurrentControllers).size() == 0) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index db2195c34..d3cb6bebb 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -38,6 +38,7 @@ protected: mAnimation->mCurrentTime = value; } }; + Ogre::SharedPtr > mAnimationBaseValuePtr; MWWorld::Ptr mPtr; MWMechanics::CharacterController *mController; From 19325f9d40944df8c6b312d309469e59e3c6c520 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sun, 21 Apr 2013 11:41:09 -0700 Subject: [PATCH 33/99] fixed the type of iterator uses in MWWorld::Store The containers type used to declare some iterators was not an exact match for the type of the container the iterator was being initialized from. This was causing build failure on windows. --- apps/openmw/mwworld/store.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index fb3f8c482..2bd61c2b0 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -475,7 +475,7 @@ namespace MWWorld cell.mData.mX = x, cell.mData.mY = y; std::pair key(x, y); - std::map, ESM::Cell>::const_iterator it = mExt.find(key); + DynamicExt::const_iterator it = mExt.find(key); if (it != mExt.end()) { return &(it->second); } @@ -493,7 +493,7 @@ namespace MWWorld cell.mData.mX = x, cell.mData.mY = y; std::pair key(x, y); - std::map, ESM::Cell>::const_iterator it = mExt.find(key); + DynamicExt::const_iterator it = mExt.find(key); if (it != mExt.end()) { return &(it->second); } @@ -534,7 +534,7 @@ namespace MWWorld void setUp() { //typedef std::vector::iterator Iterator; - typedef std::map, ESM::Cell>::iterator ExtIterator; + typedef DynamicExt::iterator ExtIterator; typedef std::map::iterator IntIterator; //std::sort(mInt.begin(), mInt.end(), RecordCmp()); From 8bf1149cec0da1e8f7c59089c01b8804c9897cf9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 21 Apr 2013 21:24:48 +0200 Subject: [PATCH 34/99] Fix activation not working sometimes The current player cell was only being updated when the reference was not empty, causing it to incorrectly detect a cell change the first time something was activated in a newly visited cell, immediately closing the opened dialogue again. --- apps/openmw/mwgui/referenceinterface.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/referenceinterface.cpp b/apps/openmw/mwgui/referenceinterface.cpp index 66e036d92..86a85be18 100644 --- a/apps/openmw/mwgui/referenceinterface.cpp +++ b/apps/openmw/mwgui/referenceinterface.cpp @@ -18,17 +18,17 @@ namespace MWGui void ReferenceInterface::checkReferenceAvailable() { - if (mPtr.isEmpty()) - return; - MWWorld::Ptr::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); // check if player has changed cell, or count of the reference has become 0 if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL) - || mPtr.getRefData().getCount() == 0) + || (!mPtr.isEmpty() && mPtr.getRefData().getCount() == 0)) { - mPtr = MWWorld::Ptr(); - onReferenceUnavailable(); + if (!mPtr.isEmpty()) + { + mPtr = MWWorld::Ptr(); + onReferenceUnavailable(); + } } mCurrentPlayerCell = playerCell; From d7c9df16f81ceebc7ee5da0b5242d33814a7e85e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 21 Apr 2013 18:32:34 -0700 Subject: [PATCH 35/99] Store the current animation group --- apps/openmw/mwrender/animation.cpp | 3 ++- apps/openmw/mwrender/animation.hpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 56b601727..ea27cbb5e 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -441,9 +441,10 @@ void Animation::play(const std::string &groupname, const std::string &start, con Ogre::SkeletonInstance *skel = iter->mSkelBase->getSkeleton(); mCurrentAnim = skel->getAnimation(groupname); mCurrentKeys = &mTextKeys[groupname]; + mCurrentGroup = groupname; mCurrentControllers = &iter->mControllers; - mAnimVelocity = 0.0f; + mAnimVelocity = 0.0f; if(mNonAccumRoot) calcAnimVelocity(); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index d3cb6bebb..472a6289c 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -53,6 +53,7 @@ protected: Ogre::Vector3 mLastPosition; Ogre::Animation *mCurrentAnim; + std::string mCurrentGroup; std::vector > *mCurrentControllers; NifOgre::TextKeyMap *mCurrentKeys; NifOgre::TextKeyMap::const_iterator mStartKey; From 6c85d6763aec95b2f3367fea90437f4e818042e5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 21 Apr 2013 18:38:25 -0700 Subject: [PATCH 36/99] Pass "loop stop" to the character controller --- apps/openmw/mwmechanics/character.cpp | 9 +++++++++ apps/openmw/mwrender/animation.cpp | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 02a5aa100..16d811528 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -150,6 +150,15 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) void CharacterController::markerEvent(float time, const std::string &evt) { + if(evt == "loop stop") + { + if(mAnimQueue.size() >= 2 && mAnimQueue[0] == mAnimQueue[1]) + { + mAnimQueue.pop_front(); + mAnimation->play(mCurrentGroup, "loop start", "stop", false); + } + return; + } if(evt == "stop") { if(mAnimQueue.size() >= 2 && mAnimQueue[0] == mAnimQueue[1]) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index ea27cbb5e..68fae99dd 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -409,10 +409,10 @@ bool Animation::handleEvent(float time, const std::string &evt) reset("loop start"); if(mCurrentTime >= time) return false; + return true; } - return true; } - if(evt == "stop") + else if(evt == "stop") { if(mLooping) { From 7d59340ed6c27b4b895e9e9b7200c35bc612714c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 21 Apr 2013 22:13:49 -0700 Subject: [PATCH 37/99] Don't clip out the group name from the textkeys --- apps/openmw/mwrender/animation.cpp | 39 +++++++++++++++++++----------- components/nifogre/skeleton.cpp | 12 +-------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 68fae99dd..2f2ffccb5 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -243,14 +243,16 @@ void Animation::calcAnimVelocity() if(track && track->getNumKeyFrames() > 1) { + const std::string loopstart = mCurrentGroup+": loop start"; + const std::string loopstop = mCurrentGroup+": loop stop"; float loopstarttime = 0.0f; float loopstoptime = mCurrentAnim->getLength(); NifOgre::TextKeyMap::const_iterator keyiter = mCurrentKeys->begin(); while(keyiter != mCurrentKeys->end()) { - if(keyiter->second == "loop start") + if(keyiter->second == loopstart) loopstarttime = keyiter->first; - else if(keyiter->second == "loop stop") + else if(keyiter->second == loopstop) { loopstoptime = keyiter->first; break; @@ -383,12 +385,6 @@ void Animation::reset(const std::string &start, const std::string &stop) bool Animation::handleEvent(float time, const std::string &evt) { - if(evt == "start" || evt == "loop start") - { - /* Do nothing */ - return true; - } - if(evt.compare(0, 7, "sound: ") == 0) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); @@ -402,21 +398,36 @@ bool Animation::handleEvent(float time, const std::string &evt) return true; } - if(evt == "loop stop") + if(evt.compare(0, mCurrentGroup.size(), mCurrentGroup) != 0 || + evt.compare(mCurrentGroup.size(), 2, ": ") != 0) + { + // Not ours + return true; + } + size_t off = mCurrentGroup.size()+2; + size_t len = evt.size() - off; + + if(evt.compare(off, len, "start") == 0 || evt.compare(off, len, "loop start") == 0) + { + /* Do nothing */ + return true; + } + + if(evt.compare(off, len, "loop stop") == 0) { if(mLooping) { - reset("loop start"); + reset(mCurrentGroup+": loop start"); if(mCurrentTime >= time) return false; return true; } } - else if(evt == "stop") + else if(evt.compare(off, len, "stop") == 0) { if(mLooping) { - reset("loop start"); + reset(mCurrentGroup+": loop start"); if(mCurrentTime >= time) return false; return true; @@ -424,7 +435,7 @@ bool Animation::handleEvent(float time, const std::string &evt) // fall-through } if(mController) - mController->markerEvent(time, evt); + mController->markerEvent(time, evt.substr(off)); return true; } @@ -455,7 +466,7 @@ void Animation::play(const std::string &groupname, const std::string &start, con if(!found) throw std::runtime_error("Failed to find animation "+groupname); - reset(start, stop); + reset(mCurrentGroup+": "+start, mCurrentGroup+": "+stop); setLooping(loop); mPlaying = true; } diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index 75bc90715..5eb043614 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -282,17 +282,7 @@ void NIFSkeletonLoader::loadResource(Ogre::Resource *resource) buildAnimation(skel, currentgroup, ctrls, targets, keyiter->first, lastkeyiter->first); - TextKeyMap::const_iterator insiter(keyiter); - TextKeyMap groupkeys; - do { - sep = insiter->second.find(':'); - if(sep == currentgroup.length() && insiter->second.compare(0, sep, currentgroup) == 0) - groupkeys.insert(std::make_pair(insiter->first, insiter->second.substr(sep+2))); - else if((sep == sizeof("soundgen")-1 && insiter->second.compare(0, sep, "soundgen") == 0) || - (sep == sizeof("sound")-1 && insiter->second.compare(0, sep, "sound") == 0)) - groupkeys.insert(std::make_pair(insiter->first, insiter->second)); - } while(insiter++ != lastkeyiter); - + TextKeyMap groupkeys(keyiter, ++lastkeyiter); bindings.setUserAny(std::string(sTextKeyExtraDataID)+"@"+currentgroup, Ogre::Any(groupkeys)); } } From 2345b5e8b3d7ebe69ef901098ac9d4603973ad09 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 21 Apr 2013 22:56:40 -0700 Subject: [PATCH 38/99] Avoid storing text keys for each animation --- apps/openmw/mwrender/animation.cpp | 15 +++------------ apps/openmw/mwrender/animation.hpp | 1 - components/nifogre/ogrenifloader.hpp | 2 ++ components/nifogre/skeleton.cpp | 7 +------ 4 files changed, 6 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 2f2ffccb5..d51a30a77 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -121,24 +121,15 @@ void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, b Ogre::Bone *bone = boneiter.getNext(); Ogre::UserObjectBindings &bindings = bone->getUserObjectBindings(); const Ogre::Any &data = bindings.getUserAny(NifOgre::sTextKeyExtraDataID); - if(data.isEmpty() || !Ogre::any_cast(data)) - continue; + if(data.isEmpty()) continue; + objlist.mTextKeys[bone->getHandle()] = Ogre::any_cast(data); if(!mNonAccumRoot) { mAccumRoot = mInsert; mNonAccumRoot = mSkelBase->getSkeleton()->getBone(bone->getName()); } - for(int i = 0;i < skel->getNumAnimations();i++) - { - Ogre::Animation *anim = skel->getAnimation(i); - const Ogre::Any &groupdata = bindings.getUserAny(std::string(NifOgre::sTextKeyExtraDataID)+ - "@"+anim->getName()); - if(!groupdata.isEmpty()) - mTextKeys[anim->getName()] = Ogre::any_cast(groupdata); - } - break; } } @@ -451,7 +442,7 @@ void Animation::play(const std::string &groupname, const std::string &start, con { Ogre::SkeletonInstance *skel = iter->mSkelBase->getSkeleton(); mCurrentAnim = skel->getAnimation(groupname); - mCurrentKeys = &mTextKeys[groupname]; + mCurrentKeys = &iter->mTextKeys.begin()->second; mCurrentGroup = groupname; mCurrentControllers = &iter->mControllers; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 472a6289c..b60bd21a8 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -46,7 +46,6 @@ protected: Ogre::SceneNode *mInsert; Ogre::Entity *mSkelBase; std::vector mObjectLists; - std::map mTextKeys; Ogre::Node *mAccumRoot; Ogre::Bone *mNonAccumRoot; Ogre::Vector3 mAccumulate; diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index b53815fa0..8e220ea38 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -50,6 +50,8 @@ struct ObjectList { // bones in the mSkelBase which are NiCamera nodes. std::vector mCameras; + std::map mTextKeys; + std::vector > mControllers; ObjectList() : mSkelBase(0) diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index 5eb043614..3660be81b 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -193,6 +193,7 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); textkeys = extractTextKeys(tk); animroot = bone; + bone->getUserObjectBindings().setUserAny(sTextKeyExtraDataID, Ogre::Any(textkeys)); } e = e->extra; } @@ -255,9 +256,6 @@ void NIFSkeletonLoader::loadResource(Ogre::Resource *resource) return; } - Ogre::UserObjectBindings &bindings = animroot->getUserObjectBindings(); - bindings.setUserAny(sTextKeyExtraDataID, Ogre::Any(true)); - std::string currentgroup; TextKeyMap::const_iterator keyiter = textkeys.begin(); for(keyiter = textkeys.begin();keyiter != textkeys.end();keyiter++) @@ -281,9 +279,6 @@ void NIFSkeletonLoader::loadResource(Ogre::Resource *resource) } buildAnimation(skel, currentgroup, ctrls, targets, keyiter->first, lastkeyiter->first); - - TextKeyMap groupkeys(keyiter, ++lastkeyiter); - bindings.setUserAny(std::string(sTextKeyExtraDataID)+"@"+currentgroup, Ogre::Any(groupkeys)); } } From 56b871e362724008c8bd4c5a686f63ce730f2a36 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 22 Apr 2013 07:57:53 +0200 Subject: [PATCH 39/99] Rank condition should always fail if NPC is not in a faction --- apps/openmw/mwdialogue/filter.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 78969ffd0..52c7bd4f3 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -73,6 +73,11 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const if (iter->second < info.mData.mRank) return false; } + else if (info.mData.mRank != -1) + { + // if there is a rank condition, but the NPC is not in a faction, always fail + return false; + } // Gender if (!isCreature) From a3511c62cf2c120636b4960f34f0a4973292d1c1 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 22 Apr 2013 00:01:30 -0700 Subject: [PATCH 40/99] Don't store textkeys in user object bindings It's a bit unwieldy to have them stored in the 'skeleton master' instead of the skeleton instance. And although the text keys are extracted for each created instance now, this shouldn't be much worse than the multimap copying going on before. Plus, proper serialization can help for future optimizations. --- apps/openmw/mwrender/animation.cpp | 16 +++------------- components/nifogre/ogrenifloader.cpp | 10 +++++++++- components/nifogre/skeleton.cpp | 11 +++++------ components/nifogre/skeleton.hpp | 3 +-- 4 files changed, 18 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index d51a30a77..31f54107e 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -97,9 +97,9 @@ void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, b while(boneiter.hasMoreElements()) boneiter.getNext()->setManuallyControlled(true); + Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); if(mSkelBase != objlist.mSkelBase) { - Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); for(size_t i = 0;i < objlist.mControllers.size();i++) { NifOgre::NodeTargetValue *dstval; @@ -114,23 +114,13 @@ void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, b } } - Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(skelinst->getName()); - boneiter = skel->getBoneIterator(); - while(boneiter.hasMoreElements()) + if(objlist.mTextKeys.size() > 0) { - Ogre::Bone *bone = boneiter.getNext(); - Ogre::UserObjectBindings &bindings = bone->getUserObjectBindings(); - const Ogre::Any &data = bindings.getUserAny(NifOgre::sTextKeyExtraDataID); - if(data.isEmpty()) continue; - - objlist.mTextKeys[bone->getHandle()] = Ogre::any_cast(data); if(!mNonAccumRoot) { mAccumRoot = mInsert; - mNonAccumRoot = mSkelBase->getSkeleton()->getBone(bone->getName()); + mNonAccumRoot = baseinst->getBone(objlist.mTextKeys.begin()->first); } - - break; } } for(size_t i = 0;i < objlist.mControllers.size();i++) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 90147a43b..97dfc59ef 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -649,7 +649,14 @@ class NIFObjectLoader Nif::ExtraPtr e = node->extra; while(!e.empty()) { - if(e->recType == Nif::RC_NiStringExtraData) + if(e->recType == Nif::RC_NiTextKeyExtraData) + { + const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); + + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); + objectlist.mTextKeys[trgtid] = NIFSkeletonLoader::extractTextKeys(tk); + } + else if(e->recType == Nif::RC_NiStringExtraData) { const Nif::NiStringExtraData *sd = static_cast(e.getPtr()); // String markers may contain important information @@ -661,6 +668,7 @@ class NIFObjectLoader flags |= 0x80000000; } } + e = e->extra; } diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index 3660be81b..1809212a7 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -193,7 +193,6 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); textkeys = extractTextKeys(tk); animroot = bone; - bone->getUserObjectBindings().setUserAny(sTextKeyExtraDataID, Ogre::Any(textkeys)); } e = e->extra; } @@ -287,16 +286,16 @@ Ogre::SkeletonPtr NIFSkeletonLoader::createSkeleton(const std::string &name, con { /* We need to be a little aggressive here, since some NIFs have a crap-ton * of nodes and Ogre only supports 256 bones. We will skip a skeleton if: - * There are no bones used for skinning, there are no controllers on non- - * NiTriShape nodes, there are no nodes named "AttachLight", and the tree - * consists of NiNode, NiTriShape, and RootCollisionNode types only. + * There are no bones used for skinning, there are no controllers, there + * are no nodes named "AttachLight", and the tree consists of NiNode, + * NiTriShape, and RootCollisionNode types only. */ if(!node->boneTrafo) { - if(node->recType == Nif::RC_NiTriShape) - return Ogre::SkeletonPtr(); if(node->controller.empty() && node->name != "AttachLight") { + if(node->recType == Nif::RC_NiTriShape) + return Ogre::SkeletonPtr(); if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode) { const Nif::NiNode *ninode = static_cast(node); diff --git a/components/nifogre/skeleton.hpp b/components/nifogre/skeleton.hpp index c69c2a12f..e71dcfb15 100644 --- a/components/nifogre/skeleton.hpp +++ b/components/nifogre/skeleton.hpp @@ -37,8 +37,6 @@ class NIFSkeletonLoader : public Ogre::ManualResourceLoader } static void buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector &ctrls, const std::vector &targets, float startTime, float stopTime); - - static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk); void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector &ctrls, Ogre::Bone *parent=NULL); // Lookup to retrieve an Ogre bone handle for a given Nif record index @@ -48,6 +46,7 @@ class NIFSkeletonLoader : public Ogre::ManualResourceLoader static LoaderMap sLoaders; public: + static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk); void loadResource(Ogre::Resource *resource); static Ogre::SkeletonPtr createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node); From a7776e124cda07bc69bae504eebe6ccdb14bb07f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 22 Apr 2013 00:22:31 -0700 Subject: [PATCH 41/99] Only set mNonAccumRoot from mSkelBase --- apps/openmw/mwrender/animation.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 31f54107e..54416c45a 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -98,7 +98,15 @@ void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, b boneiter.getNext()->setManuallyControlled(true); Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); - if(mSkelBase != objlist.mSkelBase) + if(mSkelBase == objlist.mSkelBase) + { + if(objlist.mTextKeys.size() > 0) + { + mAccumRoot = mInsert; + mNonAccumRoot = baseinst->getBone(objlist.mTextKeys.begin()->first); + } + } + else { for(size_t i = 0;i < objlist.mControllers.size();i++) { @@ -113,15 +121,6 @@ void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, b dstval->setNode(bone); } } - - if(objlist.mTextKeys.size() > 0) - { - if(!mNonAccumRoot) - { - mAccumRoot = mInsert; - mNonAccumRoot = baseinst->getBone(objlist.mTextKeys.begin()->first); - } - } } for(size_t i = 0;i < objlist.mControllers.size();i++) { From a608e7ed06883ebc2b0ae09702bc133ffc2b1095 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 22 Apr 2013 09:56:11 +0200 Subject: [PATCH 42/99] updated changelog once more --- readme.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.txt b/readme.txt index d5a5dcc18..c033f9547 100644 --- a/readme.txt +++ b/readme.txt @@ -137,6 +137,8 @@ Bug #702: Some race mods don't work Bug #711: Crash during character creation Bug #715: Growing Tauryon Bug #725: Auto calculate stats +Bug #728: Failure to open container and talk dialogue +Bug #731: Crash with Mush-Mere's "background" topic Feature #55/657: Item Repairing Feature #62/87: Enchanting Feature #99: Pathfinding From 9485a4aa6df48e9370b87a9bee5aec812cdca003 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 22 Apr 2013 01:40:41 -0700 Subject: [PATCH 43/99] Look through the whole animation stack to find the "velocity" --- apps/openmw/mwrender/animation.cpp | 55 +++++++++++++++++------------- apps/openmw/mwrender/animation.hpp | 5 +-- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 54416c45a..d71b33cdd 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -209,26 +209,26 @@ void Animation::updatePtr(const MWWorld::Ptr &ptr) } -void Animation::calcAnimVelocity() +float Animation::calcAnimVelocity(Ogre::Animation *anim, const std::string &bonename, const std::string &groupname, const NifOgre::TextKeyMap *keys) { const Ogre::NodeAnimationTrack *track = 0; - Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); + Ogre::Animation::NodeTrackIterator trackiter = anim->getNodeTrackIterator(); while(!track && trackiter.hasMoreElements()) { const Ogre::NodeAnimationTrack *cur = trackiter.getNext(); - if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName()) + if(cur->getAssociatedNode()->getName() == bonename) track = cur; } if(track && track->getNumKeyFrames() > 1) { - const std::string loopstart = mCurrentGroup+": loop start"; - const std::string loopstop = mCurrentGroup+": loop stop"; + const std::string loopstart = groupname+": loop start"; + const std::string loopstop = groupname+": loop stop"; float loopstarttime = 0.0f; - float loopstoptime = mCurrentAnim->getLength(); - NifOgre::TextKeyMap::const_iterator keyiter = mCurrentKeys->begin(); - while(keyiter != mCurrentKeys->end()) + float loopstoptime = anim->getLength(); + NifOgre::TextKeyMap::const_iterator keyiter = keys->begin(); + while(keyiter != keys->end()) { if(keyiter->second == loopstart) loopstarttime = keyiter->first; @@ -245,13 +245,15 @@ void Animation::calcAnimVelocity() Ogre::TransformKeyFrame startkf(0, loopstarttime); Ogre::TransformKeyFrame endkf(0, loopstoptime); - track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstarttime), &startkf); - track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstoptime), &endkf); + track->getInterpolatedKeyFrame(anim->_getTimeIndex(loopstarttime), &startkf); + track->getInterpolatedKeyFrame(anim->_getTimeIndex(loopstoptime), &endkf); - mAnimVelocity = startkf.getTranslate().distance(endkf.getTranslate()) / - (loopstoptime-loopstarttime); + return startkf.getTranslate().distance(endkf.getTranslate()) / + (loopstoptime-loopstarttime); } } + + return 0.0f; } static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone) @@ -423,27 +425,34 @@ bool Animation::handleEvent(float time, const std::string &evt) void Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop) { try { - bool found = false; + bool foundanim = false; /* Look in reverse; last-inserted source has priority. */ for(std::vector::reverse_iterator iter(mObjectLists.rbegin());iter != mObjectLists.rend();iter++) { if(iter->mSkelBase && iter->mSkelBase->hasAnimationState(groupname)) { Ogre::SkeletonInstance *skel = iter->mSkelBase->getSkeleton(); - mCurrentAnim = skel->getAnimation(groupname); - mCurrentKeys = &iter->mTextKeys.begin()->second; - mCurrentGroup = groupname; - mCurrentControllers = &iter->mControllers; + Ogre::Animation *anim = skel->getAnimation(groupname); + const NifOgre::TextKeyMap *keys = &iter->mTextKeys.begin()->second; + if(!foundanim) + { + mCurrentAnim = anim; + mCurrentKeys = keys; + mCurrentGroup = groupname; + mCurrentControllers = &iter->mControllers; - mAnimVelocity = 0.0f; - if(mNonAccumRoot) - calcAnimVelocity(); + mAnimVelocity = 0.0f; + foundanim = true; + } - found = true; - break; + if(!mNonAccumRoot) + break; + + mAnimVelocity = calcAnimVelocity(anim, mNonAccumRoot->getName(), groupname, keys); + if(mAnimVelocity > 0.0f) break; } } - if(!found) + if(!foundanim) throw std::runtime_error("Failed to find animation "+groupname); reset(mCurrentGroup+": "+start, mCurrentGroup+": "+stop); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index b60bd21a8..cd2523b63 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -54,7 +54,7 @@ protected: std::string mCurrentGroup; std::vector > *mCurrentControllers; - NifOgre::TextKeyMap *mCurrentKeys; + const NifOgre::TextKeyMap *mCurrentKeys; NifOgre::TextKeyMap::const_iterator mStartKey; NifOgre::TextKeyMap::const_iterator mStopKey; NifOgre::TextKeyMap::const_iterator mNextKey; @@ -65,7 +65,8 @@ protected: float mAnimVelocity; float mAnimSpeedMult; - void calcAnimVelocity(); + static float calcAnimVelocity(Ogre::Animation *anim, const std::string &bonename, + const std::string &groupname, const NifOgre::TextKeyMap *keys); /* Updates a skeleton instance so that all bones matching the source skeleton (based on * bone names) are positioned identically. */ From d936291dbf2b7c9e5240aad64b1d338aed2207df Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 22 Apr 2013 02:53:32 -0700 Subject: [PATCH 44/99] Remove an unused ostream operator<< --- components/nifogre/ogrenifloader.cpp | 9 --------- components/nifogre/ogrenifloader.hpp | 10 ---------- 2 files changed, 19 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 97dfc59ef..dc6bb8d24 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -44,15 +44,6 @@ #include "material.hpp" #include "mesh.hpp" -namespace std -{ - -// TODO: Do something useful -ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&) -{ return o; } - -} - namespace NifOgre { diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index 8e220ea38..d0ff94217 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -98,14 +98,4 @@ typedef Ogre::SharedPtr > NodeTargetValueRealPtr; } -namespace std -{ - -// These operators allow extra data types to be stored in an Ogre::Any -// object, which can then be stored in user object bindings on the nodes - -ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&); - -} - #endif From 59137d93c99bdd9795a0dd0be7d3e9d4e63a2bb0 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 22 Apr 2013 03:24:02 -0700 Subject: [PATCH 45/99] Partially handle NiGeomMorpherController The morphs aren't actually applied yet, but the Ogre controller is set up so all that has to be done is to implement the setValue method. --- components/nifogre/mesh.cpp | 14 +++++++++ components/nifogre/ogrenifloader.cpp | 45 +++++++++++++++++++++++++++- components/nifogre/skeleton.cpp | 3 +- 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp index c0a7af5c3..851160de2 100644 --- a/components/nifogre/mesh.cpp +++ b/components/nifogre/mesh.cpp @@ -116,6 +116,20 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape std::vector srcNorms = data->normals; Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC; bool vertShadowBuffer = false; + + if(!shape->controller.empty()) + { + Nif::ControllerPtr ctrl = shape->controller; + do { + if(ctrl->recType == Nif::RC_NiGeomMorpherController) + { + vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; + vertShadowBuffer = true; + break; + } + } while(!(ctrl=ctrl->next).empty()); + } + if(skin != NULL) { vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index dc6bb8d24..942b3858a 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -293,7 +293,7 @@ public: } public: - Value(const Ogre::MaterialPtr &material, Nif::NiUVData *data) + Value(const Ogre::MaterialPtr &material, const Nif::NiUVData *data) : mMaterial(material) , mUTrans(data->mKeyList[0]) , mVTrans(data->mKeyList[1]) @@ -363,6 +363,36 @@ public: typedef DefaultFunction Function; }; +class GeomMorpherController +{ +public: + class Value : public Ogre::ControllerValue + { + private: + Ogre::SubEntity *mSubEntity; + std::vector mMorphs; + + public: + Value(Ogre::SubEntity *subent, const Nif::NiMorphData *data) + : mSubEntity(subent) + , mMorphs(data->mMorphs) + { } + + virtual Ogre::Real getValue() const + { + // Should not be called + return 0.0f; + } + + virtual void setValue(Ogre::Real value) + { + // TODO: Implement + } + }; + + typedef DefaultFunction Function; +}; + /** Object creator for NIFs. This is the main class responsible for creating * "live" Ogre objects (entities, particle systems, controllers, etc) from @@ -429,6 +459,19 @@ class NIFObjectLoader objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } + else if(ctrl->recType == Nif::RC_NiGeomMorpherController) + { + const Nif::NiGeomMorpherController *geom = static_cast(ctrl.getPtr()); + + Ogre::SubEntity *subent = entity->getSubEntity(0); + Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? + Ogre::ControllerManager::getSingleton().getFrameTimeSource() : + Ogre::ControllerValueRealPtr()); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(subent, geom->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); + + objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); + } ctrl = ctrl->next; } } diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index 1809212a7..1306d037c 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -179,7 +179,8 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, ctrls.push_back(static_cast(ctrl.getPtr())); else if(!(ctrl->recType == Nif::RC_NiParticleSystemController || ctrl->recType == Nif::RC_NiVisController || - ctrl->recType == Nif::RC_NiUVController + ctrl->recType == Nif::RC_NiUVController || + ctrl->recType == Nif::RC_NiGeomMorpherController )) warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName()); ctrl = ctrl->next; From adc6a948c7ed308c57c5996c57c8227c91d4af99 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 22 Apr 2013 03:41:38 -0700 Subject: [PATCH 46/99] Add methods to get the rotation, translation, and scale from a NodeTargetValue --- components/nifogre/ogrenifloader.cpp | 41 ++++++++++++++++++++-------- components/nifogre/ogrenifloader.hpp | 4 ++- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 942b3858a..451993cf4 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -135,11 +135,14 @@ public: , mData(data->mVis) { } - virtual void applyToNode(Ogre::Node *node, float time) const - { - bool vis = calculate(time); - setVisible(node, vis); - } + virtual Ogre::Quaternion getRotation(float time) const + { return Ogre::Quaternion(); } + + virtual Ogre::Vector3 getTranslation(float time) const + { return Ogre::Vector3(0.0f); } + + virtual Ogre::Vector3 getScale(float time) const + { return Ogre::Vector3(1.0f); } virtual Ogre::Real getValue() const { @@ -149,7 +152,8 @@ public: virtual void setValue(Ogre::Real time) { - Value::applyToNode(mNode, time); + bool vis = calculate(time); + setVisible(mNode, vis); } }; @@ -223,7 +227,7 @@ public: float a = (time-last->mTime) / (iter->mTime-last->mTime); return Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue); } - return Ogre::Quaternion(); + return keys.back().mValue; } public: @@ -234,14 +238,25 @@ public: , mScales(data->mScales) { } - virtual void applyToNode(Ogre::Node *node, float time) const + virtual Ogre::Quaternion getRotation(float time) const { if(mRotations.mKeys.size() > 0) - node->setOrientation(interpKey(mRotations.mKeys, time)); + return interpKey(mRotations.mKeys, time); + return Ogre::Quaternion(); + } + + virtual Ogre::Vector3 getTranslation(float time) const + { if(mTranslations.mKeys.size() > 0) - node->setPosition(interpKey(mTranslations.mKeys, time)); + return interpKey(mTranslations.mKeys, time); + return Ogre::Vector3(0.0f); + } + + virtual Ogre::Vector3 getScale(float time) const + { if(mScales.mKeys.size() > 0) - node->setScale(Ogre::Vector3(interpKey(mScales.mKeys, time))); + return Ogre::Vector3(interpKey(mScales.mKeys, time)); + return Ogre::Vector3(1.0f); } virtual Ogre::Real getValue() const @@ -252,7 +267,9 @@ public: virtual void setValue(Ogre::Real time) { - Value::applyToNode(mNode, time); + mNode->setOrientation(Value::getRotation(time)); + mNode->setPosition(Value::getTranslation(time)); + mNode->setScale(Value::getScale(time)); } }; diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index d0ff94217..40577e451 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -87,7 +87,9 @@ public: NodeTargetValue(Ogre::Node *target) : mNode(target) { } - virtual void applyToNode(Ogre::Node *node, float time) const = 0; + virtual Ogre::Quaternion getRotation(T value) const = 0; + virtual Ogre::Vector3 getTranslation(T value) const = 0; + virtual Ogre::Vector3 getScale(T value) const = 0; void setNode(Ogre::Node *target) { mNode = target; } From cc70c6263bf377f600d8af023985ec8a092c3f5a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 22 Apr 2013 04:10:46 -0700 Subject: [PATCH 47/99] Use the NodeTargetValue for the NonAccum root --- apps/openmw/mwrender/animation.cpp | 114 ++++++++++------------------- apps/openmw/mwrender/animation.hpp | 4 +- 2 files changed, 42 insertions(+), 76 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index d71b33cdd..5700af3d6 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -41,12 +41,12 @@ Animation::Animation(const MWWorld::Ptr &ptr) , mNonAccumRoot(NULL) , mAccumulate(0.0f) , mLastPosition(0.0f) - , mCurrentAnim(NULL) , mCurrentControllers(NULL) , mCurrentKeys(NULL) , mCurrentTime(0.0f) , mPlaying(false) , mLooping(false) + , mNonAccumCtrl(NULL) , mAnimVelocity(0.0f) , mAnimSpeedMult(1.0f) { @@ -209,48 +209,31 @@ void Animation::updatePtr(const MWWorld::Ptr &ptr) } -float Animation::calcAnimVelocity(Ogre::Animation *anim, const std::string &bonename, const std::string &groupname, const NifOgre::TextKeyMap *keys) +float Animation::calcAnimVelocity(NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname, const NifOgre::TextKeyMap *keys) { - const Ogre::NodeAnimationTrack *track = 0; - - Ogre::Animation::NodeTrackIterator trackiter = anim->getNodeTrackIterator(); - while(!track && trackiter.hasMoreElements()) + const std::string loopstart = groupname+": loop start"; + const std::string loopstop = groupname+": loop stop"; + float loopstarttime = 0.0f; + float loopstoptime = std::numeric_limits::max(); + NifOgre::TextKeyMap::const_iterator keyiter = keys->begin(); + while(keyiter != keys->end()) { - const Ogre::NodeAnimationTrack *cur = trackiter.getNext(); - if(cur->getAssociatedNode()->getName() == bonename) - track = cur; + if(keyiter->second == loopstart) + loopstarttime = keyiter->first; + else if(keyiter->second == loopstop) + { + loopstoptime = keyiter->first; + break; + } + keyiter++; } - if(track && track->getNumKeyFrames() > 1) + if(loopstoptime > loopstarttime) { - const std::string loopstart = groupname+": loop start"; - const std::string loopstop = groupname+": loop stop"; - float loopstarttime = 0.0f; - float loopstoptime = anim->getLength(); - NifOgre::TextKeyMap::const_iterator keyiter = keys->begin(); - while(keyiter != keys->end()) - { - if(keyiter->second == loopstart) - loopstarttime = keyiter->first; - else if(keyiter->second == loopstop) - { - loopstoptime = keyiter->first; - break; - } - keyiter++; - } + Ogre::Vector3 startpos = nonaccumctrl->getTranslation(loopstarttime); + Ogre::Vector3 endpos = nonaccumctrl->getTranslation(loopstarttime); - if(loopstoptime > loopstarttime) - { - Ogre::TransformKeyFrame startkf(0, loopstarttime); - Ogre::TransformKeyFrame endkf(0, loopstoptime); - - track->getInterpolatedKeyFrame(anim->_getTimeIndex(loopstarttime), &startkf); - track->getInterpolatedKeyFrame(anim->_getTimeIndex(loopstoptime), &endkf); - - return startkf.getTranslate().distance(endkf.getTranslate()) / - (loopstoptime-loopstarttime); - } + return startpos.distance(endpos) / (loopstoptime-loopstarttime); } return 0.0f; @@ -298,20 +281,8 @@ Ogre::Vector3 Animation::updatePosition() { Ogre::Vector3 posdiff; - Ogre::TransformKeyFrame kf(0, mCurrentTime); - Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); - while(trackiter.hasMoreElements()) - { - const Ogre::NodeAnimationTrack *track = trackiter.getNext(); - if(track->getAssociatedNode()->getName() == mNonAccumRoot->getName()) - { - track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf); - break; - } - } - /* Get the non-accumulation root's difference from the last update. */ - posdiff = (kf.getTranslate() - mLastPosition) * mAccumulate; + posdiff = (mNonAccumCtrl->getTranslation(mCurrentTime) - mLastPosition) * mAccumulate; /* Translate the accumulation root back to compensate for the move. */ mLastPosition += posdiff; @@ -344,24 +315,9 @@ void Animation::reset(const std::string &start, const std::string &stop) mStopKey--; } - if(mNonAccumRoot) - { - const Ogre::NodeAnimationTrack *track = 0; - Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); - while(!track && trackiter.hasMoreElements()) - { - const Ogre::NodeAnimationTrack *cur = trackiter.getNext(); - if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName()) - track = cur; - } - - if(track) - { - Ogre::TransformKeyFrame kf(0, mCurrentTime); - track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf); - mLastPosition = kf.getTranslate() * mAccumulate; - } - } + if(mNonAccumCtrl) + mLastPosition = mNonAccumCtrl->getTranslation(mCurrentTime) * + mAccumulate; } @@ -431,24 +387,34 @@ void Animation::play(const std::string &groupname, const std::string &start, con { if(iter->mSkelBase && iter->mSkelBase->hasAnimationState(groupname)) { - Ogre::SkeletonInstance *skel = iter->mSkelBase->getSkeleton(); - Ogre::Animation *anim = skel->getAnimation(groupname); const NifOgre::TextKeyMap *keys = &iter->mTextKeys.begin()->second; + NifOgre::NodeTargetValue *nonaccumctrl = NULL; + for(size_t i = 0;i < iter->mControllers.size();i++) + { + NifOgre::NodeTargetValue *dstval; + dstval = dynamic_cast*>(iter->mControllers[i].getDestination().getPointer()); + if(dstval && dstval->getNode() == mNonAccumRoot) + { + nonaccumctrl = dstval; + break; + } + } + if(!foundanim) { - mCurrentAnim = anim; mCurrentKeys = keys; mCurrentGroup = groupname; mCurrentControllers = &iter->mControllers; - + mNonAccumCtrl = nonaccumctrl; mAnimVelocity = 0.0f; + foundanim = true; } if(!mNonAccumRoot) break; - mAnimVelocity = calcAnimVelocity(anim, mNonAccumRoot->getName(), groupname, keys); + mAnimVelocity = calcAnimVelocity(nonaccumctrl, groupname, keys); if(mAnimVelocity > 0.0f) break; } } @@ -469,7 +435,7 @@ Ogre::Vector3 Animation::runAnimation(float timepassed) Ogre::Vector3 movement(0.0f); timepassed *= mAnimSpeedMult; - while(mCurrentAnim && mPlaying) + while(!mCurrentGroup.empty() && mPlaying) { float targetTime = mCurrentTime + timepassed; if(mNextKey->first > targetTime) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index cd2523b63..92f636d0a 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -50,7 +50,6 @@ protected: Ogre::Bone *mNonAccumRoot; Ogre::Vector3 mAccumulate; Ogre::Vector3 mLastPosition; - Ogre::Animation *mCurrentAnim; std::string mCurrentGroup; std::vector > *mCurrentControllers; @@ -62,10 +61,11 @@ protected: bool mPlaying; bool mLooping; + NifOgre::NodeTargetValue *mNonAccumCtrl; float mAnimVelocity; float mAnimSpeedMult; - static float calcAnimVelocity(Ogre::Animation *anim, const std::string &bonename, + static float calcAnimVelocity(NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname, const NifOgre::TextKeyMap *keys); /* Updates a skeleton instance so that all bones matching the source skeleton (based on From bb64efc18e9252292b82f3540e487bb14b22901e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 22 Apr 2013 05:08:52 -0700 Subject: [PATCH 48/99] Improve looping behavior --- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwrender/animation.cpp | 78 ++++++++++++++------------- apps/openmw/mwrender/animation.hpp | 5 +- 3 files changed, 45 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 16d811528..4684ca6ea 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -123,7 +123,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim mAnimation->setAccumulation(Ogre::Vector3(0.0f)); } if(mAnimation->hasAnimation(mCurrentGroup)) - mAnimation->play(mCurrentGroup, "stop", "stop", loop); + mAnimation->play(mCurrentGroup, "start", "stop", loop); } CharacterController::CharacterController(const CharacterController &rhs) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 5700af3d6..d065195ad 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -293,36 +293,51 @@ Ogre::Vector3 Animation::updatePosition() void Animation::reset(const std::string &start, const std::string &stop) { + std::string tag = mCurrentGroup+": "+start; mStartKey = mCurrentKeys->begin(); - - while(mStartKey != mCurrentKeys->end() && mStartKey->second != start) + while(mStartKey != mCurrentKeys->end() && mStartKey->second != tag) mStartKey++; - if(mStartKey != mCurrentKeys->end()) - mCurrentTime = mStartKey->first; - else + if(mStartKey == mCurrentKeys->end() && tag == "loop start") { + tag = mCurrentGroup+": start"; mStartKey = mCurrentKeys->begin(); - mCurrentTime = mStartKey->first; + while(mStartKey != mCurrentKeys->end() && mStartKey->second != tag) + mStartKey++; } - mNextKey = mStartKey; + if(mStartKey == mCurrentKeys->end()) + mStartKey = mCurrentKeys->begin(); - if(stop.length() > 0) - { - mStopKey = mStartKey; - while(mStopKey != mCurrentKeys->end() && mStopKey->second != stop) - mStopKey++; - if(mStopKey == mCurrentKeys->end()) - mStopKey--; - } + tag = mCurrentGroup+": "+stop; + mStopKey = mStartKey; + while(mStopKey != mCurrentKeys->end() && mStopKey->second != tag) + mStopKey++; + if(mStopKey == mCurrentKeys->end()) + mStopKey--; + + mCurrentTime = mStartKey->first; + mLoopStartKey = mStartKey; + mNextKey = mStartKey; + ++mNextKey; if(mNonAccumCtrl) - mLastPosition = mNonAccumCtrl->getTranslation(mCurrentTime) * - mAccumulate; + mLastPosition = mNonAccumCtrl->getTranslation(mCurrentTime) * mAccumulate; +} + +void Animation::doLoop() +{ + mCurrentTime = mLoopStartKey->first; + mNextKey = mLoopStartKey; + ++mNextKey; + if(mNonAccumCtrl) + mLastPosition = mNonAccumCtrl->getTranslation(mCurrentTime) * mAccumulate; } -bool Animation::handleEvent(float time, const std::string &evt) +bool Animation::handleTextKey(const NifOgre::TextKeyMap::const_iterator &key) { + float time = key->first; + const std::string &evt = key->second; + if(evt.compare(0, 7, "sound: ") == 0) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); @@ -347,25 +362,15 @@ bool Animation::handleEvent(float time, const std::string &evt) if(evt.compare(off, len, "start") == 0 || evt.compare(off, len, "loop start") == 0) { - /* Do nothing */ + mLoopStartKey = key; return true; } - if(evt.compare(off, len, "loop stop") == 0) + if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0) { if(mLooping) { - reset(mCurrentGroup+": loop start"); - if(mCurrentTime >= time) - return false; - return true; - } - } - else if(evt.compare(off, len, "stop") == 0) - { - if(mLooping) - { - reset(mCurrentGroup+": loop start"); + doLoop(); if(mCurrentTime >= time) return false; return true; @@ -421,7 +426,7 @@ void Animation::play(const std::string &groupname, const std::string &start, con if(!foundanim) throw std::runtime_error("Failed to find animation "+groupname); - reset(mCurrentGroup+": "+start, mCurrentGroup+": "+stop); + reset(start, stop); setLooping(loop); mPlaying = true; } @@ -446,18 +451,15 @@ Ogre::Vector3 Animation::runAnimation(float timepassed) break; } - float time = mNextKey->first; - const std::string &evt = mNextKey->second; - mNextKey++; - - mCurrentTime = time; + NifOgre::TextKeyMap::const_iterator key(mNextKey++); + mCurrentTime = key->first; if(mNonAccumRoot) movement += updatePosition(); mPlaying = (mLooping || mStopKey->first > mCurrentTime); timepassed = targetTime - mCurrentTime; - if(!handleEvent(time, evt)) + if(!handleTextKey(key)) break; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 92f636d0a..0d81cf3b4 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -55,6 +55,7 @@ protected: std::vector > *mCurrentControllers; const NifOgre::TextKeyMap *mCurrentKeys; NifOgre::TextKeyMap::const_iterator mStartKey; + NifOgre::TextKeyMap::const_iterator mLoopStartKey; NifOgre::TextKeyMap::const_iterator mStopKey; NifOgre::TextKeyMap::const_iterator mNextKey; float mCurrentTime; @@ -82,7 +83,9 @@ protected: */ void reset(const std::string &start, const std::string &stop=std::string()); - bool handleEvent(float time, const std::string &evt); + void doLoop(); + + bool handleTextKey(const NifOgre::TextKeyMap::const_iterator &key); void addObjectList(Ogre::SceneNode *node, const std::string &model, bool baseonly); static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); From a2fc43c7df22786b4d022be550928c2478a16c24 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 22 Apr 2013 20:41:54 -0700 Subject: [PATCH 49/99] Use reset to check that the animation exists and has the right markers --- apps/openmw/mwrender/animation.cpp | 123 ++++++++++++++++------------- apps/openmw/mwrender/animation.hpp | 10 ++- 2 files changed, 72 insertions(+), 61 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index d065195ad..a17d225e9 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -209,14 +209,14 @@ void Animation::updatePtr(const MWWorld::Ptr &ptr) } -float Animation::calcAnimVelocity(NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname, const NifOgre::TextKeyMap *keys) +float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname) { const std::string loopstart = groupname+": loop start"; const std::string loopstop = groupname+": loop stop"; float loopstarttime = 0.0f; - float loopstoptime = std::numeric_limits::max(); - NifOgre::TextKeyMap::const_iterator keyiter = keys->begin(); - while(keyiter != keys->end()) + float loopstoptime = 0.0f; + NifOgre::TextKeyMap::const_iterator keyiter = keys.begin(); + while(keyiter != keys.end()) { if(keyiter->second == loopstart) loopstarttime = keyiter->first; @@ -291,36 +291,44 @@ Ogre::Vector3 Animation::updatePosition() return posdiff; } -void Animation::reset(const std::string &start, const std::string &stop) +bool Animation::reset(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname, const std::string &start, const std::string &stop) { - std::string tag = mCurrentGroup+": "+start; - mStartKey = mCurrentKeys->begin(); - while(mStartKey != mCurrentKeys->end() && mStartKey->second != tag) - mStartKey++; - if(mStartKey == mCurrentKeys->end() && tag == "loop start") + std::string tag = groupname+": "+start; + NifOgre::TextKeyMap::const_iterator startkey(keys.begin()); + while(startkey != keys.end() && startkey->second != tag) + startkey++; + if(startkey == keys.end() && tag == "loop start") { - tag = mCurrentGroup+": start"; - mStartKey = mCurrentKeys->begin(); - while(mStartKey != mCurrentKeys->end() && mStartKey->second != tag) - mStartKey++; + tag = groupname+": start"; + startkey = keys.begin(); + while(startkey != keys.end() && startkey->second != tag) + startkey++; } - if(mStartKey == mCurrentKeys->end()) - mStartKey = mCurrentKeys->begin(); + if(startkey == keys.end()) + return false; - tag = mCurrentGroup+": "+stop; - mStopKey = mStartKey; - while(mStopKey != mCurrentKeys->end() && mStopKey->second != tag) - mStopKey++; - if(mStopKey == mCurrentKeys->end()) - mStopKey--; + tag = groupname+": "+stop; + NifOgre::TextKeyMap::const_iterator stopkey(startkey); + while(stopkey != keys.end() && stopkey->second != tag) + stopkey++; + if(stopkey == keys.end()) + return false; - mCurrentTime = mStartKey->first; + if(startkey == stopkey) + return false; + + mStartKey = startkey; mLoopStartKey = mStartKey; + mStopKey = stopkey; mNextKey = mStartKey; ++mNextKey; - if(mNonAccumCtrl) - mLastPosition = mNonAccumCtrl->getTranslation(mCurrentTime) * mAccumulate; + mCurrentTime = mStartKey->first; + + if(nonaccumctrl) + mLastPosition = nonaccumctrl->getTranslation(mCurrentTime) * mAccumulate; + + return true; } void Animation::doLoop() @@ -390,45 +398,46 @@ void Animation::play(const std::string &groupname, const std::string &start, con /* Look in reverse; last-inserted source has priority. */ for(std::vector::reverse_iterator iter(mObjectLists.rbegin());iter != mObjectLists.rend();iter++) { - if(iter->mSkelBase && iter->mSkelBase->hasAnimationState(groupname)) + if(iter->mTextKeys.size() == 0) + continue; + + const NifOgre::TextKeyMap &keys = iter->mTextKeys.begin()->second; + NifOgre::NodeTargetValue *nonaccumctrl = NULL; + for(size_t i = 0;i < iter->mControllers.size();i++) { - const NifOgre::TextKeyMap *keys = &iter->mTextKeys.begin()->second; - NifOgre::NodeTargetValue *nonaccumctrl = NULL; - for(size_t i = 0;i < iter->mControllers.size();i++) + NifOgre::NodeTargetValue *dstval; + dstval = dynamic_cast*>(iter->mControllers[i].getDestination().getPointer()); + if(dstval && dstval->getNode() == mNonAccumRoot) { - NifOgre::NodeTargetValue *dstval; - dstval = dynamic_cast*>(iter->mControllers[i].getDestination().getPointer()); - if(dstval && dstval->getNode() == mNonAccumRoot) - { - nonaccumctrl = dstval; - break; - } - } - - if(!foundanim) - { - mCurrentKeys = keys; - mCurrentGroup = groupname; - mCurrentControllers = &iter->mControllers; - mNonAccumCtrl = nonaccumctrl; - mAnimVelocity = 0.0f; - - foundanim = true; - } - - if(!mNonAccumRoot) + nonaccumctrl = dstval; break; - - mAnimVelocity = calcAnimVelocity(nonaccumctrl, groupname, keys); - if(mAnimVelocity > 0.0f) break; + } } + + if(!foundanim) + { + if(!reset(keys, nonaccumctrl, groupname, start, stop)) + continue; + mCurrentKeys = &keys; + mCurrentGroup = groupname; + mCurrentControllers = &iter->mControllers; + mNonAccumCtrl = nonaccumctrl; + mAnimVelocity = 0.0f; + + setLooping(loop); + mPlaying = true; + + foundanim = true; + } + + if(!mNonAccumRoot) + break; + + mAnimVelocity = calcAnimVelocity(keys, nonaccumctrl, groupname); + if(mAnimVelocity > 0.0f) break; } if(!foundanim) throw std::runtime_error("Failed to find animation "+groupname); - - reset(start, stop); - setLooping(loop); - mPlaying = true; } catch(std::exception &e) { std::cerr<< e.what() < *nonaccumctrl, - const std::string &groupname, const NifOgre::TextKeyMap *keys); + static float calcAnimVelocity(const NifOgre::TextKeyMap &keys, + NifOgre::NodeTargetValue *nonaccumctrl, + const std::string &groupname); /* Updates a skeleton instance so that all bones matching the source skeleton (based on * bone names) are positioned identically. */ @@ -79,9 +80,10 @@ protected: /* Resets the animation to the time of the specified start marker, without * moving anything, and set the end time to the specified stop marker. If - * the marker is not found, it resets to the beginning or end respectively. + * the marker is not found, or if the markers are the same, it returns + * false. */ - void reset(const std::string &start, const std::string &stop=std::string()); + bool reset(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname, const std::string &start, const std::string &stop); void doLoop(); From 7279e015e9e76a0dec940f2f10fd2948c39500f5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 22 Apr 2013 20:59:55 -0700 Subject: [PATCH 50/99] Search the textkeys to check if an animation group exists --- apps/openmw/mwrender/animation.cpp | 19 ++++++++++++++++++- apps/openmw/mwrender/animation.hpp | 2 ++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index a17d225e9..b5f23b2c4 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -169,11 +169,28 @@ Ogre::Node *Animation::getNode(const std::string &name) } +NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname) +{ + NifOgre::TextKeyMap::const_iterator iter(keys.begin()); + for(;iter != keys.end();iter++) + { + if(iter->second.compare(0, groupname.size(), groupname) == 0 && + iter->second.compare(groupname.size(), 2, ": ") == 0) + break; + } + return iter; +} + + bool Animation::hasAnimation(const std::string &anim) { for(std::vector::const_iterator iter(mObjectLists.begin());iter != mObjectLists.end();iter++) { - if(iter->mSkelBase && iter->mSkelBase->hasAnimationState(anim)) + if(iter->mTextKeys.size() == 0) + continue; + + const NifOgre::TextKeyMap &keys = iter->mTextKeys.begin()->second; + if(findGroupStart(keys, anim) != keys.end()) return true; } return false; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 06c2b485b..6393ee7c9 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -78,6 +78,8 @@ protected: * returns the wanted movement vector from the previous update. */ Ogre::Vector3 updatePosition(); + static NifOgre::TextKeyMap::const_iterator findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname); + /* Resets the animation to the time of the specified start marker, without * moving anything, and set the end time to the specified stop marker. If * the marker is not found, or if the markers are the same, it returns From c3ce49798a315c9fd8065b356f7fae87557c69cb Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 22 Apr 2013 21:34:39 -0700 Subject: [PATCH 51/99] Fix animation velocity calculation --- apps/openmw/mwrender/animation.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index b5f23b2c4..45f2a6fcb 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -228,29 +228,31 @@ void Animation::updatePtr(const MWWorld::Ptr &ptr) float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname) { + const std::string start = groupname+": start"; const std::string loopstart = groupname+": loop start"; const std::string loopstop = groupname+": loop stop"; - float loopstarttime = 0.0f; - float loopstoptime = 0.0f; - NifOgre::TextKeyMap::const_iterator keyiter = keys.begin(); + const std::string stop = groupname+": stop"; + float starttime = std::numeric_limits::max(); + float stoptime = 0.0f; + NifOgre::TextKeyMap::const_iterator keyiter(keys.begin()); while(keyiter != keys.end()) { - if(keyiter->second == loopstart) - loopstarttime = keyiter->first; - else if(keyiter->second == loopstop) + if(keyiter->second == start || keyiter->second == loopstart) + starttime = keyiter->first; + else if(keyiter->second == loopstop || keyiter->second == stop) { - loopstoptime = keyiter->first; + stoptime = keyiter->first; break; } keyiter++; } - if(loopstoptime > loopstarttime) + if(stoptime > starttime) { - Ogre::Vector3 startpos = nonaccumctrl->getTranslation(loopstarttime); - Ogre::Vector3 endpos = nonaccumctrl->getTranslation(loopstarttime); + Ogre::Vector3 startpos = nonaccumctrl->getTranslation(starttime); + Ogre::Vector3 endpos = nonaccumctrl->getTranslation(stoptime); - return startpos.distance(endpos) / (loopstoptime-loopstarttime); + return startpos.distance(endpos) / (stoptime-starttime); } return 0.0f; From 8782ae6976aa316a5394dd4e33c6cf0cd290779a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 22 Apr 2013 23:35:50 -0700 Subject: [PATCH 52/99] Separate some Animation fields into a separate AnimLayer class --- apps/openmw/mwmechanics/character.cpp | 4 - apps/openmw/mwrender/animation.cpp | 179 +++++++++++++++----------- apps/openmw/mwrender/animation.hpp | 52 ++++---- 3 files changed, 130 insertions(+), 105 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 4684ca6ea..07f7b6940 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -304,11 +304,7 @@ void CharacterController::skipAnim() void CharacterController::setState(CharacterState state, bool loop) { if(mState == state) - { - if(mAnimation) - mAnimation->setLooping(loop); return; - } mState = state; if(!mAnimation) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 45f2a6fcb..4c5a86985 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -18,6 +18,32 @@ namespace MWRender { +Animation::AnimLayer::AnimLayer() + : mControllers(NULL) + , mTextKeys(NULL) + , mTime(0.0f) + , mPlaying(false) + , mLooping(false) +{ +} + + +Ogre::Real Animation::AnimationValue::getValue() const +{ + size_t idx = mIndex; + while(idx > 0 && mAnimation->mLayer[idx].mGroupName.empty()) + idx--; + if(!mAnimation->mLayer[idx].mGroupName.empty()) + return mAnimation->mLayer[idx].mTime; + return 0.0f; +} + +void Animation::AnimationValue::setValue(Ogre::Real value) +{ + mAnimation->mLayer[mIndex].mTime = value; +} + + void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects) { for(size_t i = 0;i < objects.mParticles.size();i++) @@ -32,7 +58,7 @@ void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectL } Animation::Animation(const MWWorld::Ptr &ptr) - : mAnimationBaseValuePtr(OGRE_NEW AnimationValue(this)) + : mAnimationBaseValuePtr(OGRE_NEW AnimationValue(this, 0)) , mPtr(ptr) , mController(NULL) , mInsert(NULL) @@ -41,11 +67,6 @@ Animation::Animation(const MWWorld::Ptr &ptr) , mNonAccumRoot(NULL) , mAccumulate(0.0f) , mLastPosition(0.0f) - , mCurrentControllers(NULL) - , mCurrentKeys(NULL) - , mCurrentTime(0.0f) - , mPlaying(false) - , mLooping(false) , mNonAccumCtrl(NULL) , mAnimVelocity(0.0f) , mAnimSpeedMult(1.0f) @@ -127,9 +148,6 @@ void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, b if(objlist.mControllers[i].getSource().isNull()) objlist.mControllers[i].setSource(mAnimationBaseValuePtr); } - - if(!mCurrentControllers || (*mCurrentControllers).size() == 0) - mCurrentControllers = &objlist.mControllers; } void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue) @@ -215,10 +233,6 @@ void Animation::setSpeed(float speed) mAnimSpeedMult = speed / mAnimVelocity; } -void Animation::setLooping(bool loop) -{ - mLooping = loop; -} void Animation::updatePtr(const MWWorld::Ptr &ptr) { @@ -301,7 +315,7 @@ Ogre::Vector3 Animation::updatePosition() Ogre::Vector3 posdiff; /* Get the non-accumulation root's difference from the last update. */ - posdiff = (mNonAccumCtrl->getTranslation(mCurrentTime) - mLastPosition) * mAccumulate; + posdiff = (mNonAccumCtrl->getTranslation(mLayer[0].mTime) - mLastPosition) * mAccumulate; /* Translate the accumulation root back to compensate for the move. */ mLastPosition += posdiff; @@ -310,7 +324,7 @@ Ogre::Vector3 Animation::updatePosition() return posdiff; } -bool Animation::reset(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname, const std::string &start, const std::string &stop) +bool Animation::reset(size_t layeridx, const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname, const std::string &start, const std::string &stop) { std::string tag = groupname+": "+start; NifOgre::TextKeyMap::const_iterator startkey(keys.begin()); @@ -336,31 +350,31 @@ bool Animation::reset(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue< if(startkey == stopkey) return false; - mStartKey = startkey; - mLoopStartKey = mStartKey; - mStopKey = stopkey; - mNextKey = mStartKey; - ++mNextKey; + mLayer[layeridx].mStartKey = startkey; + mLayer[layeridx].mLoopStartKey = startkey; + mLayer[layeridx].mStopKey = stopkey; + mLayer[layeridx].mNextKey = startkey; + mLayer[layeridx].mNextKey++; - mCurrentTime = mStartKey->first; + mLayer[layeridx].mTime = mLayer[layeridx].mStartKey->first; - if(nonaccumctrl) - mLastPosition = nonaccumctrl->getTranslation(mCurrentTime) * mAccumulate; + if(layeridx == 0 && nonaccumctrl) + mLastPosition = nonaccumctrl->getTranslation(mLayer[layeridx].mTime) * mAccumulate; return true; } -void Animation::doLoop() +void Animation::doLoop(size_t layeridx) { - mCurrentTime = mLoopStartKey->first; - mNextKey = mLoopStartKey; - ++mNextKey; - if(mNonAccumCtrl) - mLastPosition = mNonAccumCtrl->getTranslation(mCurrentTime) * mAccumulate; + mLayer[layeridx].mTime = mLayer[layeridx].mLoopStartKey->first; + mLayer[layeridx].mNextKey = mLayer[layeridx].mLoopStartKey; + mLayer[layeridx].mNextKey++; + if(layeridx == 0 && mNonAccumCtrl) + mLastPosition = mNonAccumCtrl->getTranslation(mLayer[layeridx].mTime) * mAccumulate; } -bool Animation::handleTextKey(const NifOgre::TextKeyMap::const_iterator &key) +bool Animation::handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key) { float time = key->first; const std::string &evt = key->second; @@ -378,27 +392,27 @@ bool Animation::handleTextKey(const NifOgre::TextKeyMap::const_iterator &key) return true; } - if(evt.compare(0, mCurrentGroup.size(), mCurrentGroup) != 0 || - evt.compare(mCurrentGroup.size(), 2, ": ") != 0) + if(evt.compare(0, mLayer[layeridx].mGroupName.size(), mLayer[layeridx].mGroupName) != 0 || + evt.compare(mLayer[layeridx].mGroupName.size(), 2, ": ") != 0) { - // Not ours + // Not ours, skip it return true; } - size_t off = mCurrentGroup.size()+2; + size_t off = mLayer[layeridx].mGroupName.size()+2; size_t len = evt.size() - off; if(evt.compare(off, len, "start") == 0 || evt.compare(off, len, "loop start") == 0) { - mLoopStartKey = key; + mLayer[layeridx].mLoopStartKey = key; return true; } if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0) { - if(mLooping) + if(mLayer[layeridx].mLooping) { - doLoop(); - if(mCurrentTime >= time) + doLoop(layeridx); + if(mLayer[layeridx].mTime >= time) return false; return true; } @@ -412,6 +426,9 @@ bool Animation::handleTextKey(const NifOgre::TextKeyMap::const_iterator &key) void Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop) { + // TODO: parameterize this + size_t layeridx = 0; + try { bool foundanim = false; /* Look in reverse; last-inserted source has priority. */ @@ -422,34 +439,40 @@ void Animation::play(const std::string &groupname, const std::string &start, con const NifOgre::TextKeyMap &keys = iter->mTextKeys.begin()->second; NifOgre::NodeTargetValue *nonaccumctrl = NULL; - for(size_t i = 0;i < iter->mControllers.size();i++) + if(layeridx == 0) { - NifOgre::NodeTargetValue *dstval; - dstval = dynamic_cast*>(iter->mControllers[i].getDestination().getPointer()); - if(dstval && dstval->getNode() == mNonAccumRoot) + for(size_t i = 0;i < iter->mControllers.size();i++) { - nonaccumctrl = dstval; - break; + NifOgre::NodeTargetValue *dstval; + dstval = dynamic_cast*>(iter->mControllers[i].getDestination().getPointer()); + if(dstval && dstval->getNode() == mNonAccumRoot) + { + nonaccumctrl = dstval; + break; + } } } if(!foundanim) { - if(!reset(keys, nonaccumctrl, groupname, start, stop)) + if(!reset(layeridx, keys, nonaccumctrl, groupname, start, stop)) continue; - mCurrentKeys = &keys; - mCurrentGroup = groupname; - mCurrentControllers = &iter->mControllers; - mNonAccumCtrl = nonaccumctrl; - mAnimVelocity = 0.0f; + mLayer[layeridx].mGroupName = groupname; + mLayer[layeridx].mTextKeys = &keys; + mLayer[layeridx].mControllers = &iter->mControllers; + mLayer[layeridx].mLooping = loop; + mLayer[layeridx].mPlaying = true; - setLooping(loop); - mPlaying = true; + if(layeridx == 0) + { + mNonAccumCtrl = nonaccumctrl; + mAnimVelocity = 0.0f; + } foundanim = true; } - if(!mNonAccumRoot) + if(!nonaccumctrl) break; mAnimVelocity = calcAnimVelocity(keys, nonaccumctrl, groupname); @@ -463,37 +486,43 @@ void Animation::play(const std::string &groupname, const std::string &start, con } } -Ogre::Vector3 Animation::runAnimation(float timepassed) +Ogre::Vector3 Animation::runAnimation(float duration) { Ogre::Vector3 movement(0.0f); - timepassed *= mAnimSpeedMult; - while(!mCurrentGroup.empty() && mPlaying) + duration *= mAnimSpeedMult; + for(size_t layeridx = 0;layeridx < sMaxLayers;layeridx++) { - float targetTime = mCurrentTime + timepassed; - if(mNextKey->first > targetTime) + if(mLayer[layeridx].mGroupName.empty()) + continue; + + float timepassed = duration; + while(mLayer[layeridx].mPlaying) { - mCurrentTime = targetTime; - if(mNonAccumRoot) + float targetTime = mLayer[layeridx].mTime + timepassed; + if(mLayer[layeridx].mNextKey->first > targetTime) + { + mLayer[layeridx].mTime = targetTime; + if(layeridx == 0 && mNonAccumRoot) + movement += updatePosition(); + break; + } + + NifOgre::TextKeyMap::const_iterator key(mLayer[layeridx].mNextKey++); + mLayer[layeridx].mTime = key->first; + if(layeridx == 0 && mNonAccumRoot) movement += updatePosition(); - break; + + mLayer[layeridx].mPlaying = (mLayer[layeridx].mLooping || mLayer[layeridx].mStopKey->first > mLayer[layeridx].mTime); + timepassed = targetTime - mLayer[layeridx].mTime; + + if(!handleTextKey(layeridx, key)) + break; } - - NifOgre::TextKeyMap::const_iterator key(mNextKey++); - mCurrentTime = key->first; - if(mNonAccumRoot) - movement += updatePosition(); - - mPlaying = (mLooping || mStopKey->first > mCurrentTime); - timepassed = targetTime - mCurrentTime; - - if(!handleTextKey(key)) - break; + for(size_t i = 0;i < (*(mLayer[layeridx].mControllers)).size();i++) + (*(mLayer[layeridx].mControllers))[i].update(); } - for(size_t i = 0;i < (*mCurrentControllers).size();i++) - (*mCurrentControllers)[i].update(); - if(mSkelBase) { const Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 6393ee7c9..96138cf74 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -23,20 +23,15 @@ protected: { private: Animation *mAnimation; + size_t mIndex; public: - AnimationValue(Animation *anim) : mAnimation(anim) + AnimationValue(Animation *anim, size_t layeridx) + : mAnimation(anim), mIndex(layeridx) { } - virtual Ogre::Real getValue() const - { - return mAnimation->mCurrentTime; - } - - virtual void setValue(Ogre::Real value) - { - mAnimation->mCurrentTime = value; - } + virtual Ogre::Real getValue() const; + virtual void setValue(Ogre::Real value); }; Ogre::SharedPtr > mAnimationBaseValuePtr; @@ -51,21 +46,28 @@ protected: Ogre::Vector3 mAccumulate; Ogre::Vector3 mLastPosition; - std::string mCurrentGroup; - std::vector > *mCurrentControllers; - const NifOgre::TextKeyMap *mCurrentKeys; - NifOgre::TextKeyMap::const_iterator mStartKey; - NifOgre::TextKeyMap::const_iterator mLoopStartKey; - NifOgre::TextKeyMap::const_iterator mStopKey; - NifOgre::TextKeyMap::const_iterator mNextKey; - float mCurrentTime; - bool mPlaying; - bool mLooping; - NifOgre::NodeTargetValue *mNonAccumCtrl; float mAnimVelocity; float mAnimSpeedMult; + static const size_t sMaxLayers = 1; + struct AnimLayer { + std::string mGroupName; + std::vector > *mControllers; + const NifOgre::TextKeyMap *mTextKeys; + NifOgre::TextKeyMap::const_iterator mStartKey; + NifOgre::TextKeyMap::const_iterator mLoopStartKey; + NifOgre::TextKeyMap::const_iterator mStopKey; + NifOgre::TextKeyMap::const_iterator mNextKey; + + float mTime; + + bool mPlaying; + bool mLooping; + + AnimLayer(); + } mLayer[sMaxLayers]; + static float calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname); @@ -85,11 +87,11 @@ protected: * the marker is not found, or if the markers are the same, it returns * false. */ - bool reset(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname, const std::string &start, const std::string &stop); + bool reset(size_t layeridx, const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname, const std::string &start, const std::string &stop); - void doLoop(); + void doLoop(size_t layeridx); - bool handleTextKey(const NifOgre::TextKeyMap::const_iterator &key); + bool handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key); void addObjectList(Ogre::SceneNode *node, const std::string &model, bool baseonly); static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); @@ -113,8 +115,6 @@ public: void setSpeed(float speed); - void setLooping(bool loop); - void play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop); virtual Ogre::Vector3 runAnimation(float timepassed); From e3781769371e8c736bcf2643537c60cf6fe106f2 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Apr 2013 00:15:31 -0700 Subject: [PATCH 53/99] Don't update controllers that have already been updated --- apps/openmw/mwrender/animation.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 4c5a86985..0166eb843 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -519,8 +519,23 @@ Ogre::Vector3 Animation::runAnimation(float duration) if(!handleTextKey(layeridx, key)) break; } - for(size_t i = 0;i < (*(mLayer[layeridx].mControllers)).size();i++) - (*(mLayer[layeridx].mControllers))[i].update(); + + bool updatectrls = true; + for(size_t i = layeridx-1;i < layeridx;i--) + { + if(mLayer[i].mGroupName.empty()) + continue; + if(mLayer[i].mControllers == mLayer[layeridx].mControllers) + { + updatectrls = false; + break; + } + } + if(updatectrls) + { + for(size_t i = 0;i < (*(mLayer[layeridx].mControllers)).size();i++) + (*(mLayer[layeridx].mControllers))[i].update(); + } } if(mSkelBase) From e4c5aac9664db105fde2b31b0973d5c7b483d80a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Apr 2013 02:48:11 -0700 Subject: [PATCH 54/99] Keep track of the animation layers a given object list is animating on This only tracks layers they explicitly animate on. They may animate on other layers as well, if nothing else is animating on them. --- apps/openmw/mwrender/activatoranimation.cpp | 2 +- apps/openmw/mwrender/animation.cpp | 63 +++++++++++++++------ apps/openmw/mwrender/animation.hpp | 11 +++- apps/openmw/mwrender/creatureanimation.cpp | 2 +- 4 files changed, 58 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwrender/activatoranimation.cpp b/apps/openmw/mwrender/activatoranimation.cpp index c3a3045c2..4eb844607 100644 --- a/apps/openmw/mwrender/activatoranimation.cpp +++ b/apps/openmw/mwrender/activatoranimation.cpp @@ -22,7 +22,7 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) const std::string name = "meshes\\"+ref->mBase->mModel; addObjectList(mPtr.getRefData().getBaseNode(), name, false); - setRenderProperties(mObjectLists.back(), RV_Misc, RQG_Main, RQG_Alpha); + setRenderProperties(mObjects.back().mObjectList, RV_Misc, RQG_Main, RQG_Alpha); } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 0166eb843..98e2f9104 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -78,9 +78,9 @@ Animation::~Animation() if(mInsert) { Ogre::SceneManager *sceneMgr = mInsert->getCreator(); - for(size_t i = 0;i < mObjectLists.size();i++) - destroyObjectList(sceneMgr, mObjectLists[i]); - mObjectLists.clear(); + for(size_t i = 0;i < mObjects.size();i++) + destroyObjectList(sceneMgr, mObjects[i].mObjectList); + mObjects.clear(); } } @@ -93,9 +93,13 @@ void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, b assert(mInsert); } - mObjectLists.push_back(!baseonly ? NifOgre::Loader::createObjects(mInsert, model) : - NifOgre::Loader::createObjectBase(mInsert, model)); - NifOgre::ObjectList &objlist = mObjectLists.back(); + mObjects.push_back(ObjectInfo()); + ObjectInfo &obj = mObjects.back(); + obj.mActiveLayers = 0; + obj.mObjectList = (!baseonly ? NifOgre::Loader::createObjects(mInsert, model) : + NifOgre::Loader::createObjectBase(mInsert, model)); + + NifOgre::ObjectList &objlist = obj.mObjectList; if(objlist.mSkelBase) { if(!mSkelBase) @@ -202,12 +206,12 @@ NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::Tex bool Animation::hasAnimation(const std::string &anim) { - for(std::vector::const_iterator iter(mObjectLists.begin());iter != mObjectLists.end();iter++) + for(std::vector::const_iterator iter(mObjects.begin());iter != mObjects.end();iter++) { - if(iter->mTextKeys.size() == 0) + if(iter->mObjectList.mTextKeys.size() == 0) continue; - const NifOgre::TextKeyMap &keys = iter->mTextKeys.begin()->second; + const NifOgre::TextKeyMap &keys = iter->mObjectList.mTextKeys.begin()->second; if(findGroupStart(keys, anim) != keys.end()) return true; } @@ -430,21 +434,45 @@ void Animation::play(const std::string &groupname, const std::string &start, con size_t layeridx = 0; try { + for(std::vector::iterator iter(mObjects.begin());iter != mObjects.end();iter++) + iter->mActiveLayers &= ~(1<::reverse_iterator iter(mObjectLists.rbegin());iter != mObjectLists.rend();iter++) + for(std::vector::reverse_iterator iter(mObjects.rbegin());iter != mObjects.rend();iter++) { - if(iter->mTextKeys.size() == 0) + NifOgre::ObjectList &objlist = iter->mObjectList; + if(objlist.mTextKeys.size() == 0) continue; - const NifOgre::TextKeyMap &keys = iter->mTextKeys.begin()->second; + const NifOgre::TextKeyMap &keys = objlist.mTextKeys.begin()->second; NifOgre::NodeTargetValue *nonaccumctrl = NULL; if(layeridx == 0) { - for(size_t i = 0;i < iter->mControllers.size();i++) + for(size_t i = 0;i < objlist.mControllers.size();i++) { NifOgre::NodeTargetValue *dstval; - dstval = dynamic_cast*>(iter->mControllers[i].getDestination().getPointer()); + dstval = dynamic_cast*>(objlist.mControllers[i].getDestination().getPointer()); if(dstval && dstval->getNode() == mNonAccumRoot) { nonaccumctrl = dstval; @@ -459,7 +487,7 @@ void Animation::play(const std::string &groupname, const std::string &start, con continue; mLayer[layeridx].mGroupName = groupname; mLayer[layeridx].mTextKeys = &keys; - mLayer[layeridx].mControllers = &iter->mControllers; + mLayer[layeridx].mControllers = &objlist.mControllers; mLayer[layeridx].mLooping = loop; mLayer[layeridx].mPlaying = true; @@ -469,6 +497,7 @@ void Animation::play(const std::string &groupname, const std::string &start, con mAnimVelocity = 0.0f; } + iter->mActiveLayers |= (1<getSkeleton(); - for(std::vector::iterator iter(mObjectLists.begin());iter != mObjectLists.end();iter++) + for(std::vector::iterator iter(mObjects.begin());iter != mObjects.end();iter++) { - Ogre::Entity *ent = iter->mSkelBase; + Ogre::Entity *ent = iter->mObjectList.mSkelBase; if(!ent) continue; Ogre::SkeletonInstance *inst = ent->getSkeleton(); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 96138cf74..840c22ae4 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -38,9 +38,18 @@ protected: MWWorld::Ptr mPtr; MWMechanics::CharacterController *mController; + struct ObjectInfo { + NifOgre::ObjectList mObjectList; + /* Bit-field specifying which animation layers this object list is + * explicitly animating on (1 = layer 0, 2 = layer 1, 4 = layer 2. + * etc). + */ + int mActiveLayers; + }; + Ogre::SceneNode *mInsert; Ogre::Entity *mSkelBase; - std::vector mObjectLists; + std::vector mObjects; Ogre::Node *mAccumRoot; Ogre::Bone *mNonAccumRoot; Ogre::Vector3 mAccumulate; diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 51632b2ee..97c28c0ae 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -25,7 +25,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) addObjectList(mPtr.getRefData().getBaseNode(), "meshes\\base_anim.nif", true); addObjectList(mPtr.getRefData().getBaseNode(), model, false); - setRenderProperties(mObjectLists.back(), RV_Actors, RQG_Main, RQG_Alpha); + setRenderProperties(mObjects.back().mObjectList, RV_Actors, RQG_Main, RQG_Alpha); } } From cfccf0981dee740e725eb819d1dbb2cf00c66b2d Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 23 Apr 2013 13:24:06 +0400 Subject: [PATCH 55/99] update IndexedStore::setUp() for multiple datafile support --- apps/openmw/mwworld/store.hpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index fb3f8c482..959b944e1 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -872,7 +872,28 @@ namespace MWWorld } void setUp() { - std::sort(mStatic.begin(), mStatic.end(), Compare()); + /// \note This method sorts indexed values for further + /// searches. Every loaded item is present in storage, but + /// latest loaded shadows any previous while searching. + /// If memory cost will be too high, it is possible to remove + /// unused values. + + Compare cmp; + + std::stable_sort(mStatic.begin(), mStatic.end(), cmp); + + typename std::vector::iterator first, next; + next = first = mStatic.begin(); + + while (first != mStatic.end() && ++next != mStatic.end()) { + while (next != mStatic.end() && !cmp(*first, *next)) { + ++next; + } + if (first != --next) { + std::swap(*first, *next); + } + first = ++next; + } } const T *search(int index) const { From c4806f01f850bbf18052659e45b222553b9f747c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Apr 2013 03:42:00 -0700 Subject: [PATCH 56/99] Use a separate list of controllers to update This should make it easier to figure out what controllers need to be updated for various animation sources and layers. --- apps/openmw/mwrender/animation.cpp | 220 ++++++++++++++++++----------- apps/openmw/mwrender/animation.hpp | 3 + 2 files changed, 139 insertions(+), 84 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 98e2f9104..fbf5725c4 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -71,6 +71,9 @@ Animation::Animation(const MWWorld::Ptr &ptr) , mAnimVelocity(0.0f) , mAnimSpeedMult(1.0f) { + /* As long as we remain under 128 active controllers, we can avoid + * reallocations. */ + mActiveCtrls.reserve(128); } Animation::~Animation() @@ -179,6 +182,79 @@ void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::ui } +void Animation::updateActiveControllers() +{ + mActiveCtrls.clear(); + + /* First, get all controllers that don't target a node, or that target + * nodes that don't belong to any particular layer. + */ + std::vector::iterator obj(mObjects.begin()); + for(;obj != mObjects.end();obj++) + { + std::vector >::const_iterator ctrl(obj->mObjectList.mControllers.begin()); + for(;ctrl != obj->mObjectList.mControllers.end();ctrl++) + { + NifOgre::NodeTargetValue *dstval; + dstval = dynamic_cast*>(ctrl->getDestination().getPointer()); + if(dstval) + { + /*if(getLayerByName(dstval->getNode()->getName()) >= 0)*/ + continue; + } + mActiveCtrls.insert(mActiveCtrls.end(), *ctrl); + } + } + + std::vector > *ctrls = NULL; + size_t layer = 0; + while(layer < sMaxLayers) + { + /* Now get controllers that target nodes that belong to this layer from + * whatever objectlist is active on this layer. + */ + std::vector::iterator obj(mObjects.begin()); + for(;obj != mObjects.end();obj++) + { + if((obj->mActiveLayers&(1<mObjectList.mControllers; + break; + } + } + + /* Check if any objectlists are active on subsequent layers. Include + * those layers if not. + */ + size_t nextlayer = layer+1; + for(;nextlayer < sMaxLayers;nextlayer++) + { + for(obj = mObjects.begin();obj != mObjects.end();obj++) + { + if((obj->mActiveLayers&(1< >::const_iterator ctrl(ctrls->begin()); + for(;ctrl != ctrls->end();ctrl++) + { + NifOgre::NodeTargetValue *dstval; + dstval = dynamic_cast*>(ctrl->getDestination().getPointer()); + if(dstval) + { + /*ssize_t idx = getLayerByName(dstval->getNode()->getName()); + if(idx >= (ssize_t)layer && idx < (ssize_t)nextlayer)*/ + mActiveCtrls.insert(mActiveCtrls.end(), *ctrl); + } + } + + layer = nextlayer; + } +} + + Ogre::Node *Animation::getNode(const std::string &name) { if(mSkelBase) @@ -433,86 +509,76 @@ void Animation::play(const std::string &groupname, const std::string &start, con // TODO: parameterize this size_t layeridx = 0; - try { - for(std::vector::iterator iter(mObjects.begin());iter != mObjects.end();iter++) - iter->mActiveLayers &= ~(1<::iterator iter(mObjects.begin());iter != mObjects.end();iter++) + iter->mActiveLayers &= ~(1<::reverse_iterator iter(mObjects.rbegin());iter != mObjects.rend();iter++) + { + NifOgre::ObjectList &objlist = iter->mObjectList; + if(objlist.mTextKeys.size() == 0) + continue; + + const NifOgre::TextKeyMap &keys = objlist.mTextKeys.begin()->second; + NifOgre::NodeTargetValue *nonaccumctrl = NULL; + if(layeridx == 0) { - // Do not allow layer 0 to be disabled - assert(layeridx != 0); + for(size_t i = 0;i < objlist.mControllers.size();i++) + { + NifOgre::NodeTargetValue *dstval; + dstval = dynamic_cast*>(objlist.mControllers[i].getDestination().getPointer()); + if(dstval && dstval->getNode() == mNonAccumRoot) + { + nonaccumctrl = dstval; + break; + } + } + } - mLayer[layeridx].mGroupName.clear(); - mLayer[layeridx].mTextKeys = NULL; - mLayer[layeridx].mControllers = NULL; - mLayer[layeridx].mLooping = false; - mLayer[layeridx].mPlaying = false; + if(!foundanim) + { + if(!reset(layeridx, keys, nonaccumctrl, groupname, start, stop)) + continue; + mLayer[layeridx].mGroupName = groupname; + mLayer[layeridx].mTextKeys = &keys; + mLayer[layeridx].mControllers = &objlist.mControllers; + mLayer[layeridx].mLooping = loop; + mLayer[layeridx].mPlaying = true; if(layeridx == 0) { - mNonAccumCtrl = NULL; + mNonAccumCtrl = nonaccumctrl; mAnimVelocity = 0.0f; } - return; + iter->mActiveLayers |= (1<::reverse_iterator iter(mObjects.rbegin());iter != mObjects.rend();iter++) - { - NifOgre::ObjectList &objlist = iter->mObjectList; - if(objlist.mTextKeys.size() == 0) - continue; + if(!nonaccumctrl) + break; - const NifOgre::TextKeyMap &keys = objlist.mTextKeys.begin()->second; - NifOgre::NodeTargetValue *nonaccumctrl = NULL; - if(layeridx == 0) - { - for(size_t i = 0;i < objlist.mControllers.size();i++) - { - NifOgre::NodeTargetValue *dstval; - dstval = dynamic_cast*>(objlist.mControllers[i].getDestination().getPointer()); - if(dstval && dstval->getNode() == mNonAccumRoot) - { - nonaccumctrl = dstval; - break; - } - } - } - - if(!foundanim) - { - if(!reset(layeridx, keys, nonaccumctrl, groupname, start, stop)) - continue; - mLayer[layeridx].mGroupName = groupname; - mLayer[layeridx].mTextKeys = &keys; - mLayer[layeridx].mControllers = &objlist.mControllers; - mLayer[layeridx].mLooping = loop; - mLayer[layeridx].mPlaying = true; - - if(layeridx == 0) - { - mNonAccumCtrl = nonaccumctrl; - mAnimVelocity = 0.0f; - } - - iter->mActiveLayers |= (1< 0.0f) break; - } - if(!foundanim) - throw std::runtime_error("Failed to find animation "+groupname); - } - catch(std::exception &e) { - std::cerr<< e.what() < 0.0f) break; } + if(!foundanim) + std::cerr<< "Failed to find animation "<getSkeleton(); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 840c22ae4..4860f44f5 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -58,6 +58,7 @@ protected: NifOgre::NodeTargetValue *mNonAccumCtrl; float mAnimVelocity; float mAnimSpeedMult; + std::vector > mActiveCtrls; static const size_t sMaxLayers = 1; struct AnimLayer { @@ -107,6 +108,8 @@ protected: static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue); + void updateActiveControllers(); + public: Animation(const MWWorld::Ptr &ptr); virtual ~Animation(); From 8c9e2e0ee9e24b444efa67f811ed8be1cd87fbcb Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Apr 2013 05:34:08 -0700 Subject: [PATCH 57/99] Improve handling of reaching the stop animation text key --- apps/openmw/mwrender/animation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index fbf5725c4..d98a32828 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -449,6 +449,7 @@ void Animation::doLoop(size_t layeridx) mLayer[layeridx].mTime = mLayer[layeridx].mLoopStartKey->first; mLayer[layeridx].mNextKey = mLayer[layeridx].mLoopStartKey; mLayer[layeridx].mNextKey++; + mLayer[layeridx].mPlaying = true; if(layeridx == 0 && mNonAccumCtrl) mLastPosition = mNonAccumCtrl->getTranslation(mLayer[layeridx].mTime) * mAccumulate; } @@ -608,7 +609,7 @@ Ogre::Vector3 Animation::runAnimation(float duration) if(layeridx == 0 && mNonAccumRoot) movement += updatePosition(); - mLayer[layeridx].mPlaying = (mLayer[layeridx].mLooping || mLayer[layeridx].mStopKey->first > mLayer[layeridx].mTime); + mLayer[layeridx].mPlaying = (key != mLayer[layeridx].mStopKey); timepassed = targetTime - mLayer[layeridx].mTime; if(!handleTextKey(layeridx, key)) From 44ac0a7c1856bf463f18ed435d554574745d4c50 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 23 Apr 2013 22:47:21 +0200 Subject: [PATCH 58/99] Use one vertex buffer for all UV coordinates --- components/nifogre/mesh.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp index c0a7af5c3..6d940deed 100644 --- a/components/nifogre/mesh.cpp +++ b/components/nifogre/mesh.cpp @@ -260,13 +260,24 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape // Texture UV coordinates size_t numUVs = data->uvlist.size(); - for(size_t i = 0;i < numUVs;i++) + if (numUVs) { - vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2), - srcVerts.size(), Ogre::HardwareBuffer::HBU_STATIC); - vbuf->writeData(0, vbuf->getSizeInBytes(), &data->uvlist[i][0], true); + size_t elemSize = Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2); + + for(size_t i = 0; i < numUVs; i++) + decl->addElement(nextBuf, elemSize*i, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, i); + + vbuf = hwBufMgr->createVertexBuffer(decl->getVertexSize(nextBuf), srcVerts.size(), + Ogre::HardwareBuffer::HBU_STATIC); + + std::vector allUVs; + allUVs.reserve(srcVerts.size()*numUVs); + for (size_t vert = 0; vertuvlist[i][vert]); + + vbuf->writeData(0, elemSize*srcVerts.size()*numUVs, &allUVs[0], true); - decl->addElement(nextBuf, 0, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, i); bind->setBinding(nextBuf++, vbuf); } From 634b0fd067a7c4ac65123be69dd29c0c4739be2f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Apr 2013 17:57:40 -0700 Subject: [PATCH 59/99] Fix for controller delta time going below start time --- components/nifogre/ogrenifloader.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 451993cf4..95ba439d1 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -65,11 +65,7 @@ public: , mStopTime(ctrl->timeStop) { if(mDeltaInput) - { mDeltaCount = mPhase; - while(mDeltaCount < mStartTime) - mDeltaCount += (mStopTime-mStartTime); - } } virtual Ogre::Real calculate(Ogre::Real value) @@ -77,6 +73,9 @@ public: if(mDeltaInput) { mDeltaCount += value*mFrequency; + if(mDeltaCount < mStartTime) + mDeltaCount = mStopTime - std::fmod(mStartTime - mDeltaCount, + mStopTime - mStartTime); mDeltaCount = std::fmod(mDeltaCount - mStartTime, mStopTime - mStartTime) + mStartTime; return mDeltaCount; From 7e2995bc2f7b21f2335424e0d49e7d5231a673d7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Apr 2013 19:43:26 -0700 Subject: [PATCH 60/99] Fix setting up active controllers --- apps/openmw/mwrender/animation.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index d98a32828..cb54a0366 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -155,6 +155,8 @@ void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, b if(objlist.mControllers[i].getSource().isNull()) objlist.mControllers[i].setSource(mAnimationBaseValuePtr); } + + mActiveCtrls.insert(mActiveCtrls.end(), objlist.mControllers.begin(), objlist.mControllers.end()); } void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue) @@ -231,7 +233,7 @@ void Animation::updateActiveControllers() { for(obj = mObjects.begin();obj != mObjects.end();obj++) { - if((obj->mActiveLayers&(1<mActiveLayers&(1< Date: Tue, 23 Apr 2013 20:42:54 -0700 Subject: [PATCH 61/99] Don't create Ogre animations for skeletons --- components/nifogre/ogrenifloader.cpp | 28 +++- components/nifogre/skeleton.cpp | 220 ++------------------------- components/nifogre/skeleton.hpp | 4 +- 3 files changed, 37 insertions(+), 215 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 95ba439d1..e451326fe 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -676,6 +676,32 @@ class NIFObjectLoader } + static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk) + { + TextKeyMap textkeys; + for(size_t i = 0;i < tk->list.size();i++) + { + const std::string &str = tk->list[i].text; + std::string::size_type pos = 0; + while(pos < str.length()) + { + if(::isspace(str[pos])) + { + pos++; + continue; + } + + std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos)); + std::string result = str.substr(pos, nextpos-pos); + textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result))); + + pos = nextpos; + } + } + return textkeys; + } + + static void createObjects(const std::string &name, const std::string &group, Ogre::SceneManager *sceneMgr, const Nif::Node *node, ObjectList &objectlist, int flags, int animflags, int partflags) @@ -704,7 +730,7 @@ class NIFObjectLoader const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); - objectlist.mTextKeys[trgtid] = NIFSkeletonLoader::extractTextKeys(tk); + objectlist.mTextKeys[trgtid] = extractTextKeys(tk); } else if(e->recType == Nif::RC_NiStringExtraData) { diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index 1306d037c..3aec920fc 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -11,142 +11,7 @@ namespace NifOgre { -void NIFSkeletonLoader::buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector &ctrls, const std::vector &targets, float startTime, float stopTime) -{ - Ogre::Animation *anim = skel->createAnimation(name, stopTime); - - for(size_t i = 0;i < ctrls.size();i++) - { - const Nif::NiKeyframeController *kfc = ctrls[i]; - if(kfc->data.empty()) - continue; - const Nif::NiKeyframeData *kf = kfc->data.getPtr(); - - /* Get the keyframes and make sure they're sorted first to last */ - const Nif::QuaternionKeyList &quatkeys = kf->mRotations; - const Nif::Vector3KeyList &trankeys = kf->mTranslations; - const Nif::FloatKeyList &scalekeys = kf->mScales; - - Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin(); - Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin(); - Nif::FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin(); - - Ogre::Bone *bone = skel->getBone(targets[i]); - // NOTE: For some reason, Ogre doesn't like the node track ID being different from - // the bone ID - Ogre::NodeAnimationTrack *nodetrack = anim->hasNodeTrack(bone->getHandle()) ? - anim->getNodeTrack(bone->getHandle()) : - anim->createNodeTrack(bone->getHandle(), bone); - - Ogre::Quaternion lastquat, curquat; - Ogre::Vector3 lasttrans(0.0f), curtrans(0.0f); - Ogre::Vector3 lastscale(1.0f), curscale(1.0f); - if(quatiter != quatkeys.mKeys.end()) - lastquat = curquat = quatiter->mValue; - if(traniter != trankeys.mKeys.end()) - lasttrans = curtrans = traniter->mValue; - if(scaleiter != scalekeys.mKeys.end()) - lastscale = curscale = Ogre::Vector3(scaleiter->mValue); - - bool didlast = false; - while(!didlast) - { - float curtime = std::numeric_limits::max(); - - //Get latest time - if(quatiter != quatkeys.mKeys.end()) - curtime = std::min(curtime, quatiter->mTime); - if(traniter != trankeys.mKeys.end()) - curtime = std::min(curtime, traniter->mTime); - if(scaleiter != scalekeys.mKeys.end()) - curtime = std::min(curtime, scaleiter->mTime); - - curtime = std::max(curtime, startTime); - if(curtime >= stopTime) - { - didlast = true; - curtime = stopTime; - } - - // Get the latest quaternions, translations, and scales for the - // current time - while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime) - { - lastquat = curquat; - if(++quatiter != quatkeys.mKeys.end()) - curquat = quatiter->mValue; - } - while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime) - { - lasttrans = curtrans; - if(++traniter != trankeys.mKeys.end()) - curtrans = traniter->mValue; - } - while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime) - { - lastscale = curscale; - if(++scaleiter != scalekeys.mKeys.end()) - curscale = Ogre::Vector3(scaleiter->mValue); - } - - Ogre::TransformKeyFrame *kframe; - kframe = nodetrack->createNodeKeyFrame(curtime); - if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin()) - kframe->setRotation(curquat); - else - { - Nif::QuaternionKeyList::VecType::const_iterator last = quatiter-1; - float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime); - kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat)); - } - if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin()) - kframe->setTranslate(curtrans); - else - { - Nif::Vector3KeyList::VecType::const_iterator last = traniter-1; - float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime); - kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff)); - } - if(scaleiter == scalekeys.mKeys.end() || scaleiter == scalekeys.mKeys.begin()) - kframe->setScale(curscale); - else - { - Nif::FloatKeyList::VecType::const_iterator last = scaleiter-1; - float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime); - kframe->setScale(lastscale + ((curscale-lastscale)*diff)); - } - } - } - anim->optimise(); -} - - -TextKeyMap NIFSkeletonLoader::extractTextKeys(const Nif::NiTextKeyExtraData *tk) -{ - TextKeyMap textkeys; - for(size_t i = 0;i < tk->list.size();i++) - { - const std::string &str = tk->list[i].text; - std::string::size_type pos = 0; - while(pos < str.length()) - { - if(::isspace(str[pos])) - { - pos++; - continue; - } - - std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos)); - std::string result = str.substr(pos, nextpos-pos); - textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result))); - - pos = nextpos; - } - } - return textkeys; -} - -void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector &ctrls, Ogre::Bone *parent) +void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent) { Ogre::Bone *bone; if(!skel->hasBone(node->name)) @@ -175,29 +40,16 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Nif::ControllerPtr ctrl = node->controller; while(!ctrl.empty()) { - if(ctrl->recType == Nif::RC_NiKeyframeController) - ctrls.push_back(static_cast(ctrl.getPtr())); - else if(!(ctrl->recType == Nif::RC_NiParticleSystemController || - ctrl->recType == Nif::RC_NiVisController || - ctrl->recType == Nif::RC_NiUVController || - ctrl->recType == Nif::RC_NiGeomMorpherController - )) + if(!(ctrl->recType == Nif::RC_NiParticleSystemController || + ctrl->recType == Nif::RC_NiVisController || + ctrl->recType == Nif::RC_NiUVController || + ctrl->recType == Nif::RC_NiKeyframeController || + ctrl->recType == Nif::RC_NiGeomMorpherController + )) warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName()); ctrl = ctrl->next; } - Nif::ExtraPtr e = node->extra; - while(!e.empty()) - { - if(e->recType == Nif::RC_NiTextKeyExtraData && !animroot) - { - const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); - textkeys = extractTextKeys(tk); - animroot = bone; - } - e = e->extra; - } - const Nif::NiNode *ninode = dynamic_cast(node); if(ninode) { @@ -205,7 +57,7 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, for(size_t i = 0;i < children.length();i++) { if(!children[i].empty()) - buildBones(skel, children[i].getPtr(), animroot, textkeys, ctrls, bone); + buildBones(skel, children[i].getPtr(), bone); } } } @@ -218,68 +70,14 @@ void NIFSkeletonLoader::loadResource(Ogre::Resource *resource) Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName())); const Nif::Node *node = static_cast(nif->getRoot(0)); - std::vector ctrls; - Ogre::Bone *animroot = NULL; - TextKeyMap textkeys; try { - buildBones(skel, node, animroot, textkeys, ctrls); + buildBones(skel, node); } catch(std::exception &e) { std::cerr<< "Exception while loading "<getName() < targets; - // TODO: If ctrls.size() == 0, check for a .kf file sharing the name of the .nif file - if(ctrls.size() == 0) // No animations? Then we're done. - return; - - float maxtime = 0.0f; - for(size_t i = 0;i < ctrls.size();i++) - { - const Nif::NiKeyframeController *ctrl = ctrls[i]; - maxtime = std::max(maxtime, ctrl->timeStop); - Nif::Named *target = dynamic_cast(ctrl->target.getPtr()); - if(target != NULL) - targets.push_back(target->name); - } - - if(targets.size() != ctrls.size()) - { - warn("Target size mismatch ("+Ogre::StringConverter::toString(targets.size())+" targets, "+ - Ogre::StringConverter::toString(ctrls.size())+" controllers)"); - return; - } - - std::string currentgroup; - TextKeyMap::const_iterator keyiter = textkeys.begin(); - for(keyiter = textkeys.begin();keyiter != textkeys.end();keyiter++) - { - std::string::size_type sep = keyiter->second.find(':'); - if((sep == currentgroup.length() && keyiter->second.compare(0, sep, currentgroup) == 0) || - (sep == sizeof("soundgen")-1 && keyiter->second.compare(0, sep, "soundgen") == 0) || - (sep == sizeof("sound")-1 && keyiter->second.compare(0, sep, "sound") == 0)) - continue; - currentgroup = keyiter->second.substr(0, sep); - - if(skel->hasAnimation(currentgroup)) - continue; - - TextKeyMap::const_iterator lastkeyiter = textkeys.end(); - while((--lastkeyiter)->first > keyiter->first) - { - if(lastkeyiter->second.find(':') == currentgroup.length() && - lastkeyiter->second.compare(0, currentgroup.length(), currentgroup) == 0) - break; - } - - buildAnimation(skel, currentgroup, ctrls, targets, keyiter->first, lastkeyiter->first); - } } diff --git a/components/nifogre/skeleton.hpp b/components/nifogre/skeleton.hpp index e71dcfb15..38cfe14e3 100644 --- a/components/nifogre/skeleton.hpp +++ b/components/nifogre/skeleton.hpp @@ -36,8 +36,7 @@ class NIFSkeletonLoader : public Ogre::ManualResourceLoader abort(); } - static void buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector &ctrls, const std::vector &targets, float startTime, float stopTime); - void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector &ctrls, Ogre::Bone *parent=NULL); + void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent=NULL); // Lookup to retrieve an Ogre bone handle for a given Nif record index std::map mNifToOgreHandleMap; @@ -46,7 +45,6 @@ class NIFSkeletonLoader : public Ogre::ManualResourceLoader static LoaderMap sLoaders; public: - static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk); void loadResource(Ogre::Resource *resource); static Ogre::SkeletonPtr createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node); From 6f0b9a5f2cb98747715dbfa6c88b86f178c6f94c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Apr 2013 21:42:59 -0700 Subject: [PATCH 62/99] Avoid unnecessary multimap copies --- components/nifogre/ogrenifloader.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index e451326fe..57aa79403 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -676,9 +676,8 @@ class NIFObjectLoader } - static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk) + static void extractTextKeys(const Nif::NiTextKeyExtraData *tk, TextKeyMap &textkeys) { - TextKeyMap textkeys; for(size_t i = 0;i < tk->list.size();i++) { const std::string &str = tk->list[i].text; @@ -698,7 +697,6 @@ class NIFObjectLoader pos = nextpos; } } - return textkeys; } @@ -730,7 +728,7 @@ class NIFObjectLoader const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); - objectlist.mTextKeys[trgtid] = extractTextKeys(tk); + extractTextKeys(tk, objectlist.mTextKeys[trgtid]); } else if(e->recType == Nif::RC_NiStringExtraData) { From 544011e0962b28b9cb978d90b9f1fe32455fb1ed Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Apr 2013 00:40:58 -0700 Subject: [PATCH 63/99] Fix 'start' text key fallback lookup --- apps/openmw/mwrender/animation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index cb54a0366..dc1312837 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -412,7 +412,7 @@ bool Animation::reset(size_t layeridx, const NifOgre::TextKeyMap &keys, NifOgre: NifOgre::TextKeyMap::const_iterator startkey(keys.begin()); while(startkey != keys.end() && startkey->second != tag) startkey++; - if(startkey == keys.end() && tag == "loop start") + if(startkey == keys.end() && start == "loop start") { tag = groupname+": start"; startkey = keys.begin(); From 26cc86ffdcd152b9acb6b953fff7de7acf2c48a0 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Apr 2013 00:42:45 -0700 Subject: [PATCH 64/99] Use the Nif node's transform for skinned fixups --- components/nifogre/mesh.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp index 5637cd828..ca92f62d4 100644 --- a/components/nifogre/mesh.cpp +++ b/components/nifogre/mesh.cpp @@ -109,7 +109,6 @@ NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape) { - Ogre::SkeletonPtr skel; const Nif::NiTriShapeData *data = shape->data.getPtr(); const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr()); std::vector srcVerts = data->vertices; @@ -139,10 +138,6 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape // explicitly attached later. mesh->setSkeletonName(mName); - // Get the skeleton resource, so vertices can be transformed into the bones' initial state. - Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); - skel = skelMgr->getByName(mName); - // Convert vertices and normals to bone space from bind position. It would be // better to transform the bones into bind position, but there doesn't seem to // be a reliable way to do that. @@ -153,11 +148,10 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape const Nif::NodeList &bones = skin->bones; for(size_t b = 0;b < bones.length();b++) { - Ogre::Bone *bone = skel->getBone(bones[b]->name); Ogre::Matrix4 mat; mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale), Ogre::Quaternion(data->bones[b].trafo.rotation)); - mat = bone->_getFullTransform() * mat; + mat = bones[b]->getWorldTransform() * mat; const std::vector &weights = data->bones[b].weights; for(size_t i = 0;i < weights.size();i++) @@ -310,6 +304,8 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape // Assign bone weights for this TriShape if(skin != NULL) { + Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(mName); + const Nif::NiSkinData *data = skin->data.getPtr(); const Nif::NodeList &bones = skin->bones; for(size_t i = 0;i < bones.length();i++) From 9e05ee53da793846d0ddebd0b960aba621a621a6 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Apr 2013 01:18:08 -0700 Subject: [PATCH 65/99] Avoid looking for the controller if there is no NonAccum node Also, make sure there is a controller before updating the position. --- apps/openmw/mwrender/animation.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index dc1312837..a2b429840 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -538,7 +538,7 @@ void Animation::play(const std::string &groupname, const std::string &start, con const NifOgre::TextKeyMap &keys = objlist.mTextKeys.begin()->second; NifOgre::NodeTargetValue *nonaccumctrl = NULL; - if(layeridx == 0) + if(layeridx == 0 && mNonAccumRoot) { for(size_t i = 0;i < objlist.mControllers.size();i++) { @@ -601,14 +601,14 @@ Ogre::Vector3 Animation::runAnimation(float duration) if(mLayer[layeridx].mNextKey->first > targetTime) { mLayer[layeridx].mTime = targetTime; - if(layeridx == 0 && mNonAccumRoot) + if(layeridx == 0 && mNonAccumCtrl) movement += updatePosition(); break; } NifOgre::TextKeyMap::const_iterator key(mLayer[layeridx].mNextKey++); mLayer[layeridx].mTime = key->first; - if(layeridx == 0 && mNonAccumRoot) + if(layeridx == 0 && mNonAccumCtrl) movement += updatePosition(); mLayer[layeridx].mPlaying = (key != mLayer[layeridx].mStopKey); From 102b3bdef99d0d7ea1ee601565d3463a7cb5212d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Apr 2013 01:57:51 -0700 Subject: [PATCH 66/99] Update position by reference --- apps/openmw/mwrender/animation.cpp | 13 +++++++------ apps/openmw/mwrender/animation.hpp | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index a2b429840..a8b2a1897 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -392,18 +392,19 @@ void Animation::updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Og } -Ogre::Vector3 Animation::updatePosition() +void Animation::updatePosition(Ogre::Vector3 &position) { Ogre::Vector3 posdiff; - /* Get the non-accumulation root's difference from the last update. */ + /* Get the non-accumulation root's difference from the last update, and move the position + * accordingly. + */ posdiff = (mNonAccumCtrl->getTranslation(mLayer[0].mTime) - mLastPosition) * mAccumulate; + position += posdiff; /* Translate the accumulation root back to compensate for the move. */ mLastPosition += posdiff; mAccumRoot->setPosition(-mLastPosition); - - return posdiff; } bool Animation::reset(size_t layeridx, const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname, const std::string &start, const std::string &stop) @@ -602,14 +603,14 @@ Ogre::Vector3 Animation::runAnimation(float duration) { mLayer[layeridx].mTime = targetTime; if(layeridx == 0 && mNonAccumCtrl) - movement += updatePosition(); + updatePosition(movement); break; } NifOgre::TextKeyMap::const_iterator key(mLayer[layeridx].mNextKey++); mLayer[layeridx].mTime = key->first; if(layeridx == 0 && mNonAccumCtrl) - movement += updatePosition(); + updatePosition(movement); mLayer[layeridx].mPlaying = (key != mLayer[layeridx].mStopKey); timepassed = targetTime - mLayer[layeridx].mTime; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 4860f44f5..a874cb1a8 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -88,7 +88,7 @@ protected: /* Updates the position of the accum root node for the current time, and * returns the wanted movement vector from the previous update. */ - Ogre::Vector3 updatePosition(); + void updatePosition(Ogre::Vector3 &position); static NifOgre::TextKeyMap::const_iterator findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname); From 68a9a4de5f77153f16e41fea47654b722fa6119d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Apr 2013 04:12:27 -0700 Subject: [PATCH 67/99] Fix setting and getting KeyframeController transforms --- components/nifogre/ogrenifloader.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 57aa79403..d36199f86 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -173,8 +173,6 @@ public: { if(time <= keys.front().mTime) return keys.front().mValue; - if(time >= keys.back().mTime) - return keys.back().mValue; Nif::FloatKeyList::VecType::const_iterator iter(keys.begin()+1); for(;iter != keys.end();iter++) @@ -193,8 +191,6 @@ public: { if(time <= keys.front().mTime) return keys.front().mValue; - if(time >= keys.back().mTime) - return keys.back().mValue; Nif::Vector3KeyList::VecType::const_iterator iter(keys.begin()+1); for(;iter != keys.end();iter++) @@ -213,8 +209,6 @@ public: { if(time <= keys.front().mTime) return keys.front().mValue; - if(time >= keys.back().mTime) - return keys.back().mValue; Nif::QuaternionKeyList::VecType::const_iterator iter(keys.begin()+1); for(;iter != keys.end();iter++) @@ -241,21 +235,21 @@ public: { if(mRotations.mKeys.size() > 0) return interpKey(mRotations.mKeys, time); - return Ogre::Quaternion(); + return mNode->getOrientation(); } virtual Ogre::Vector3 getTranslation(float time) const { if(mTranslations.mKeys.size() > 0) return interpKey(mTranslations.mKeys, time); - return Ogre::Vector3(0.0f); + return mNode->getPosition(); } virtual Ogre::Vector3 getScale(float time) const { if(mScales.mKeys.size() > 0) return Ogre::Vector3(interpKey(mScales.mKeys, time)); - return Ogre::Vector3(1.0f); + return mNode->getScale(); } virtual Ogre::Real getValue() const @@ -266,9 +260,12 @@ public: virtual void setValue(Ogre::Real time) { - mNode->setOrientation(Value::getRotation(time)); - mNode->setPosition(Value::getTranslation(time)); - mNode->setScale(Value::getScale(time)); + if(mRotations.mKeys.size() > 0) + mNode->setOrientation(interpKey(mRotations.mKeys, time)); + if(mTranslations.mKeys.size() > 0) + mNode->setPosition(interpKey(mTranslations.mKeys, time)); + if(mScales.mKeys.size() > 0) + mNode->setScale(Ogre::Vector3(interpKey(mScales.mKeys, time))); } }; From 7241267d5c036f8c052598c18f04bdb8fef0b70e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Apr 2013 04:41:52 -0700 Subject: [PATCH 68/99] Make an AnimationValue for each animation layer --- apps/openmw/mwrender/animation.cpp | 8 +++--- apps/openmw/mwrender/animation.hpp | 40 ++++++++++++++++-------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index a8b2a1897..3d938cc25 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -58,8 +58,7 @@ void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectL } Animation::Animation(const MWWorld::Ptr &ptr) - : mAnimationBaseValuePtr(OGRE_NEW AnimationValue(this, 0)) - , mPtr(ptr) + : mPtr(ptr) , mController(NULL) , mInsert(NULL) , mSkelBase(NULL) @@ -71,6 +70,9 @@ Animation::Animation(const MWWorld::Ptr &ptr) , mAnimVelocity(0.0f) , mAnimSpeedMult(1.0f) { + for(size_t i = 0;i < sMaxLayers;i++) + mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this, i)); + /* As long as we remain under 128 active controllers, we can avoid * reallocations. */ mActiveCtrls.reserve(128); @@ -153,7 +155,7 @@ void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, b for(size_t i = 0;i < objlist.mControllers.size();i++) { if(objlist.mControllers[i].getSource().isNull()) - objlist.mControllers[i].setSource(mAnimationBaseValuePtr); + objlist.mControllers[i].setSource(mAnimationValuePtr[0]); } mActiveCtrls.insert(mActiveCtrls.end(), objlist.mControllers.begin(), objlist.mControllers.end()); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index a874cb1a8..282cd3eca 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -33,10 +33,6 @@ protected: virtual Ogre::Real getValue() const; virtual void setValue(Ogre::Real value); }; - Ogre::SharedPtr > mAnimationBaseValuePtr; - - MWWorld::Ptr mPtr; - MWMechanics::CharacterController *mController; struct ObjectInfo { NifOgre::ObjectList mObjectList; @@ -47,20 +43,6 @@ protected: int mActiveLayers; }; - Ogre::SceneNode *mInsert; - Ogre::Entity *mSkelBase; - std::vector mObjects; - Ogre::Node *mAccumRoot; - Ogre::Bone *mNonAccumRoot; - Ogre::Vector3 mAccumulate; - Ogre::Vector3 mLastPosition; - - NifOgre::NodeTargetValue *mNonAccumCtrl; - float mAnimVelocity; - float mAnimSpeedMult; - std::vector > mActiveCtrls; - - static const size_t sMaxLayers = 1; struct AnimLayer { std::string mGroupName; std::vector > *mControllers; @@ -76,7 +58,27 @@ protected: bool mLooping; AnimLayer(); - } mLayer[sMaxLayers]; + }; + + MWWorld::Ptr mPtr; + MWMechanics::CharacterController *mController; + + Ogre::SceneNode *mInsert; + Ogre::Entity *mSkelBase; + std::vector mObjects; + Ogre::Node *mAccumRoot; + Ogre::Bone *mNonAccumRoot; + Ogre::Vector3 mAccumulate; + Ogre::Vector3 mLastPosition; + + NifOgre::NodeTargetValue *mNonAccumCtrl; + float mAnimVelocity; + float mAnimSpeedMult; + std::vector > mActiveCtrls; + + static const size_t sMaxLayers = 1; + AnimLayer mLayer[sMaxLayers]; + Ogre::SharedPtr > mAnimationValuePtr[sMaxLayers]; static float calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, From c58dfbe921cda569f950648f3307145a9c2cfe37 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Apr 2013 04:56:39 -0700 Subject: [PATCH 69/99] Specify the loop count to Animation::play --- apps/openmw/mwmechanics/character.cpp | 12 ++++++------ apps/openmw/mwrender/animation.cpp | 19 ++++++++++++------- apps/openmw/mwrender/animation.hpp | 6 +++--- apps/openmw/mwrender/characterpreview.cpp | 4 ++-- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 07f7b6940..664ed7c03 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -123,7 +123,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim mAnimation->setAccumulation(Ogre::Vector3(0.0f)); } if(mAnimation->hasAnimation(mCurrentGroup)) - mAnimation->play(mCurrentGroup, "start", "stop", loop); + mAnimation->play(mCurrentGroup, "start", "stop", loop ? (~(size_t)0) : 0); } CharacterController::CharacterController(const CharacterController &rhs) @@ -155,7 +155,7 @@ void CharacterController::markerEvent(float time, const std::string &evt) if(mAnimQueue.size() >= 2 && mAnimQueue[0] == mAnimQueue[1]) { mAnimQueue.pop_front(); - mAnimation->play(mCurrentGroup, "loop start", "stop", false); + mAnimation->play(mCurrentGroup, "loop start", "stop", 0); } return; } @@ -164,7 +164,7 @@ void CharacterController::markerEvent(float time, const std::string &evt) if(mAnimQueue.size() >= 2 && mAnimQueue[0] == mAnimQueue[1]) { mAnimQueue.pop_front(); - mAnimation->play(mCurrentGroup, "loop start", "stop", false); + mAnimation->play(mCurrentGroup, "loop start", "stop", 0); } else if(mAnimQueue.size() > 0) { @@ -172,7 +172,7 @@ void CharacterController::markerEvent(float time, const std::string &evt) if(mAnimQueue.size() > 0) { mCurrentGroup = mAnimQueue.front(); - mAnimation->play(mCurrentGroup, "start", "stop", false); + mAnimation->play(mCurrentGroup, "start", "stop", 0); } } return; @@ -284,7 +284,7 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int mAnimQueue.push_back(groupname); mCurrentGroup = groupname; mState = CharState_SpecialIdle; - mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", false); + mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", 0); } else if(mode == 0) { @@ -316,7 +316,7 @@ void CharacterController::setState(CharacterState state, bool loop) if(mAnimation->hasAnimation(anim)) { mCurrentGroup = anim; - mAnimation->play(mCurrentGroup, "start", "stop", loop); + mAnimation->play(mCurrentGroup, "start", "stop", loop ? (~(size_t)0) : 0); } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 3d938cc25..4e82f0b8c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -23,7 +23,7 @@ Animation::AnimLayer::AnimLayer() , mTextKeys(NULL) , mTime(0.0f) , mPlaying(false) - , mLooping(false) + , mLoopCount(0) { } @@ -449,14 +449,20 @@ bool Animation::reset(size_t layeridx, const NifOgre::TextKeyMap &keys, NifOgre: return true; } -void Animation::doLoop(size_t layeridx) +bool Animation::doLoop(size_t layeridx) { + if(mLayer[layeridx].mLoopCount == 0) + return false; + mLayer[layeridx].mLoopCount--; + mLayer[layeridx].mTime = mLayer[layeridx].mLoopStartKey->first; mLayer[layeridx].mNextKey = mLayer[layeridx].mLoopStartKey; mLayer[layeridx].mNextKey++; mLayer[layeridx].mPlaying = true; if(layeridx == 0 && mNonAccumCtrl) mLastPosition = mNonAccumCtrl->getTranslation(mLayer[layeridx].mTime) * mAccumulate; + + return true; } @@ -495,9 +501,8 @@ bool Animation::handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_ if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0) { - if(mLayer[layeridx].mLooping) + if(doLoop(layeridx)) { - doLoop(layeridx); if(mLayer[layeridx].mTime >= time) return false; return true; @@ -510,7 +515,7 @@ bool Animation::handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_ } -void Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop) +void Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, size_t loops) { // TODO: parameterize this size_t layeridx = 0; @@ -527,7 +532,7 @@ void Animation::play(const std::string &groupname, const std::string &start, con mLayer[layeridx].mGroupName.clear(); mLayer[layeridx].mTextKeys = NULL; mLayer[layeridx].mControllers = NULL; - mLayer[layeridx].mLooping = false; + mLayer[layeridx].mLoopCount = 0; mLayer[layeridx].mPlaying = false; foundanim = true; @@ -562,7 +567,7 @@ void Animation::play(const std::string &groupname, const std::string &start, con mLayer[layeridx].mGroupName = groupname; mLayer[layeridx].mTextKeys = &keys; mLayer[layeridx].mControllers = &objlist.mControllers; - mLayer[layeridx].mLooping = loop; + mLayer[layeridx].mLoopCount = loops; mLayer[layeridx].mPlaying = true; if(layeridx == 0) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 282cd3eca..1b300887f 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -55,7 +55,7 @@ protected: float mTime; bool mPlaying; - bool mLooping; + size_t mLoopCount; AnimLayer(); }; @@ -101,7 +101,7 @@ protected: */ bool reset(size_t layeridx, const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname, const std::string &start, const std::string &stop); - void doLoop(size_t layeridx); + bool doLoop(size_t layeridx); bool handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key); @@ -129,7 +129,7 @@ public: void setSpeed(float speed); - void play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop); + void play(const std::string &groupname, const std::string &start, const std::string &stop, size_t loops); virtual Ogre::Vector3 runAnimation(float timepassed); Ogre::Node *getNode(const std::string &name); diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 41770d489..c22d8a4da 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -155,7 +155,7 @@ namespace MWRender if (!mSelectionBuffer) mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, 0); - mAnimation->play("inventoryhandtohand", "start", "stop", false); + mAnimation->play("inventoryhandtohand", "start", "stop", 0); } // -------------------------------------------------------------------------------------------------- @@ -189,7 +189,7 @@ namespace MWRender void RaceSelectionPreview::onSetup () { - mAnimation->play("idle", "start", "stop", false); + mAnimation->play("idle", "start", "stop", 0); updateCamera(); } From b482d5be097b16a8df8ecc04c8b25252e81e746c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Apr 2013 05:23:45 -0700 Subject: [PATCH 70/99] Add a method to check if a given layer is playing --- apps/openmw/mwrender/animation.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 1b300887f..87c57ebf7 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -132,6 +132,9 @@ public: void play(const std::string &groupname, const std::string &start, const std::string &stop, size_t loops); virtual Ogre::Vector3 runAnimation(float timepassed); + bool isPlaying(size_t layeridx) const + { return mLayer[layeridx].mPlaying; } + Ogre::Node *getNode(const std::string &name); }; From e8cabc94349d95bbb9e23a3604e8d1afe85ed8b1 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Apr 2013 06:32:11 -0700 Subject: [PATCH 71/99] Pass the loop count from loopgroup to the play method Instead of queueing each iteration separately. --- apps/openmw/mwmechanics/character.cpp | 34 +++++++-------------------- apps/openmw/mwmechanics/character.hpp | 2 +- apps/openmw/mwrender/animation.cpp | 11 ++++++++- 3 files changed, 20 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 664ed7c03..086a2fc25 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -150,29 +150,16 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) void CharacterController::markerEvent(float time, const std::string &evt) { - if(evt == "loop stop") - { - if(mAnimQueue.size() >= 2 && mAnimQueue[0] == mAnimQueue[1]) - { - mAnimQueue.pop_front(); - mAnimation->play(mCurrentGroup, "loop start", "stop", 0); - } - return; - } if(evt == "stop") { - if(mAnimQueue.size() >= 2 && mAnimQueue[0] == mAnimQueue[1]) - { - mAnimQueue.pop_front(); - mAnimation->play(mCurrentGroup, "loop start", "stop", 0); - } - else if(mAnimQueue.size() > 0) + if(mAnimQueue.size() > 0) { mAnimQueue.pop_front(); if(mAnimQueue.size() > 0) { - mCurrentGroup = mAnimQueue.front(); - mAnimation->play(mCurrentGroup, "start", "stop", 0); + mCurrentGroup = mAnimQueue.front().first; + size_t count = mAnimQueue.front().second; + mAnimation->play(mCurrentGroup, "start", "stop", count); } } return; @@ -223,7 +210,6 @@ void CharacterController::update(float duration, Movement &movement) if(vec.x > 0.0f) setState(inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight) : (sneak ? CharState_SneakRight : (isrunning ? CharState_RunRight : CharState_WalkRight)), true); - else if(vec.x < 0.0f) setState(inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft) : (sneak ? CharState_SneakLeft : (isrunning ? CharState_RunLeft : CharState_WalkLeft)), true); @@ -236,10 +222,10 @@ void CharacterController::update(float duration, Movement &movement) if(vec.y > 0.0f) setState(inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward) : (sneak ? CharState_SneakForward : (isrunning ? CharState_RunForward : CharState_WalkForward)), true); - else if(vec.y < 0.0f) setState(inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack) : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack)), true); + // Apply any sideways movement manually movement.mPosition[0] += vec.x * (speed*duration); } @@ -250,7 +236,7 @@ void CharacterController::update(float duration, Movement &movement) else if(rot.z < 0.0f) setState(CharState_TurnLeft, true); } - else if(mAnimQueue.size() == 0) + else if(getState() != CharState_SpecialIdle || !mAnimation->isPlaying(0)) setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)), true); movement.mRotation[0] += rot.x * duration; @@ -280,17 +266,15 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int if(mode != 0 || mAnimQueue.size() == 0) { mAnimQueue.clear(); - while(count-- > 0) - mAnimQueue.push_back(groupname); + mAnimQueue.push_back(std::make_pair(groupname, count-1)); mCurrentGroup = groupname; mState = CharState_SpecialIdle; - mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", 0); + mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", count-1); } else if(mode == 0) { mAnimQueue.resize(1); - while(count-- > 0) - mAnimQueue.push_back(groupname); + mAnimQueue.push_back(std::make_pair(groupname, count-1)); } } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 5b5a65f79..4453b078d 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -72,7 +72,7 @@ class CharacterController MWWorld::Ptr mPtr; MWRender::Animation *mAnimation; - typedef std::deque AnimationQueue; + typedef std::deque > AnimationQueue; AnimationQueue mAnimQueue; std::string mCurrentGroup; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 4e82f0b8c..b205d9519 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -499,7 +499,16 @@ bool Animation::handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_ return true; } - if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0) + if(evt.compare(off, len, "loop stop") == 0) + { + if(doLoop(layeridx)) + { + if(mLayer[layeridx].mTime >= time) + return false; + } + return true; + } + if(evt.compare(off, len, "stop") == 0) { if(doLoop(layeridx)) { From b80891099e0171af7a58dd9733b4abfd901fc3aa Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Apr 2013 06:48:34 -0700 Subject: [PATCH 72/99] Don't use the character controller to handle text keys The Animation class has the Ptr and can call to whatever subsystem is needed. --- apps/openmw/mwmechanics/character.cpp | 38 +++++++++------------------ apps/openmw/mwmechanics/character.hpp | 6 ----- apps/openmw/mwrender/animation.cpp | 16 +++-------- 3 files changed, 16 insertions(+), 44 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 086a2fc25..bbf679cda 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -148,27 +148,6 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) } -void CharacterController::markerEvent(float time, const std::string &evt) -{ - if(evt == "stop") - { - if(mAnimQueue.size() > 0) - { - mAnimQueue.pop_front(); - if(mAnimQueue.size() > 0) - { - mCurrentGroup = mAnimQueue.front().first; - size_t count = mAnimQueue.front().second; - mAnimation->play(mCurrentGroup, "start", "stop", count); - } - } - return; - } - - std::cerr<< "Unhandled animation event: "<isPlaying(0)) - setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)), true); + { + if(mAnimQueue.size() > 0) + { + mCurrentGroup = mAnimQueue.front().first; + size_t count = mAnimQueue.front().second; + mAnimQueue.pop_front(); + mAnimation->play(mCurrentGroup, "start", "stop", count); + } + else + setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)), true); + } movement.mRotation[0] += rot.x * duration; movement.mRotation[1] += rot.y * duration; @@ -263,17 +252,16 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int else { count = std::max(count, 1); - if(mode != 0 || mAnimQueue.size() == 0) + if(mode != 0 || getState() != CharState_SpecialIdle) { mAnimQueue.clear(); - mAnimQueue.push_back(std::make_pair(groupname, count-1)); mCurrentGroup = groupname; mState = CharState_SpecialIdle; mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", count-1); } else if(mode == 0) { - mAnimQueue.resize(1); + mAnimQueue.clear(); mAnimQueue.push_back(std::make_pair(groupname, count-1)); } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 4453b078d..4bbea1551 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -79,12 +79,6 @@ class CharacterController CharacterState mState; bool mSkipAnim; -protected: - /* Called by the animation whenever a new text key is reached. */ - void markerEvent(float time, const std::string &evt); - - friend class MWRender::Animation; - public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop); CharacterController(const CharacterController &rhs); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index b205d9519..c0fee0d3b 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -499,7 +499,7 @@ bool Animation::handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_ return true; } - if(evt.compare(off, len, "loop stop") == 0) + if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0) { if(doLoop(layeridx)) { @@ -508,18 +508,8 @@ bool Animation::handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_ } return true; } - if(evt.compare(off, len, "stop") == 0) - { - if(doLoop(layeridx)) - { - if(mLayer[layeridx].mTime >= time) - return false; - return true; - } - // fall-through - } - if(mController) - mController->markerEvent(time, evt.substr(off)); + + std::cerr<< "Unhandled animation textkey: "< Date: Wed, 24 Apr 2013 07:10:41 -0700 Subject: [PATCH 73/99] Remove the character controller from the Animation class --- apps/openmw/mwmechanics/character.cpp | 13 ------------- apps/openmw/mwmechanics/character.hpp | 1 - apps/openmw/mwrender/animation.cpp | 9 +-------- apps/openmw/mwrender/animation.hpp | 12 +++--------- 4 files changed, 4 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index bbf679cda..2a7ab8b09 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -108,8 +108,6 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim if(!mAnimation) return; - mAnimation->setController(this); - getStateInfo(mState, &mCurrentGroup); if(MWWorld::Class::get(mPtr).isActor()) { @@ -126,17 +124,6 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim mAnimation->play(mCurrentGroup, "start", "stop", loop ? (~(size_t)0) : 0); } -CharacterController::CharacterController(const CharacterController &rhs) - : mPtr(rhs.mPtr), mAnimation(rhs.mAnimation), mAnimQueue(rhs.mAnimQueue) - , mCurrentGroup(rhs.mCurrentGroup), mState(rhs.mState) - , mSkipAnim(rhs.mSkipAnim) -{ - if(!mAnimation) - return; - /* We've been copied. Update the animation with the new controller. */ - mAnimation->setController(this); -} - CharacterController::~CharacterController() { } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 4bbea1551..ffd220ff2 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -81,7 +81,6 @@ class CharacterController public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop); - CharacterController(const CharacterController &rhs); virtual ~CharacterController(); void updatePtr(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index c0fee0d3b..c2f453516 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -59,14 +59,13 @@ void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectL Animation::Animation(const MWWorld::Ptr &ptr) : mPtr(ptr) - , mController(NULL) , mInsert(NULL) , mSkelBase(NULL) , mAccumRoot(NULL) , mNonAccumRoot(NULL) + , mNonAccumCtrl(NULL) , mAccumulate(0.0f) , mLastPosition(0.0f) - , mNonAccumCtrl(NULL) , mAnimVelocity(0.0f) , mAnimSpeedMult(1.0f) { @@ -299,12 +298,6 @@ bool Animation::hasAnimation(const std::string &anim) } -void Animation::setController(MWMechanics::CharacterController *controller) -{ - mController = controller; -} - - void Animation::setAccumulation(const Ogre::Vector3 &accum) { mAccumulate = accum; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 87c57ebf7..526bca69a 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -8,10 +8,6 @@ #include "../mwworld/ptr.hpp" -namespace MWMechanics -{ - class CharacterController; -} namespace MWRender { @@ -61,20 +57,20 @@ protected: }; MWWorld::Ptr mPtr; - MWMechanics::CharacterController *mController; Ogre::SceneNode *mInsert; Ogre::Entity *mSkelBase; std::vector mObjects; Ogre::Node *mAccumRoot; Ogre::Bone *mNonAccumRoot; + NifOgre::NodeTargetValue *mNonAccumCtrl; Ogre::Vector3 mAccumulate; Ogre::Vector3 mLastPosition; - NifOgre::NodeTargetValue *mNonAccumCtrl; + std::vector > mActiveCtrls; + float mAnimVelocity; float mAnimSpeedMult; - std::vector > mActiveCtrls; static const size_t sMaxLayers = 1; AnimLayer mLayer[sMaxLayers]; @@ -116,8 +112,6 @@ public: Animation(const MWWorld::Ptr &ptr); virtual ~Animation(); - void setController(MWMechanics::CharacterController *controller); - void updatePtr(const MWWorld::Ptr &ptr); bool hasAnimation(const std::string &anim); From c0102954f1d435b21aa2e6a7f1b6d61207ac925b Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sat, 30 Mar 2013 13:28:17 +0100 Subject: [PATCH 74/99] Add non-breaking space to font code range --- files/mygui/openmw_font.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/files/mygui/openmw_font.xml b/files/mygui/openmw_font.xml index b1446fae1..f1be51e16 100644 --- a/files/mygui/openmw_font.xml +++ b/files/mygui/openmw_font.xml @@ -9,6 +9,7 @@ + From cd57e3f896260e480db9fab1aa0d0b9e03f2aa09 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Apr 2013 23:51:43 +0200 Subject: [PATCH 75/99] Auto calculate attributes if there are not specified in the NPC record --- apps/openmw/mwclass/npc.cpp | 66 +++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 7cda87bb1..ea49ae4a4 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -49,6 +49,67 @@ namespace { return new CustomData (*this); } + + void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats) + { + // race bonus + const ESM::Race *race = + MWBase::Environment::get().getWorld()->getStore().get().find(npc->mRace); + + bool male = (npc->mFlags & ESM::NPC::Female) == 0; + + int level = creatureStats.getLevel(); + + for (int i=0; imData.mAttributeValues[i]; + creatureStats.getAttribute(i).setBase (male ? attribute.mMale : attribute.mFemale); + } + + // class bonus + const ESM::Class *class_ = + MWBase::Environment::get().getWorld()->getStore().get().find(npc->mClass); + + for (int i=0; i<2; ++i) + { + int attribute = class_->mData.mAttribute[i]; + if (attribute>=0 && attribute<8) + { + creatureStats.getAttribute(attribute).setBase ( + creatureStats.getAttribute(attribute).getBase() + 10); + } + } + + // skill bonus + for (int attribute=0; attributegetStore().get().find(j); + + if (skill->mData.mAttribute != attribute) + continue; + + // is this a minor or major skill? + float add=0.2; + for (int k=0; k<5; ++k) + { + if (class_->mData.mSkills[k][0] == j) + add=0.5; + } + for (int k=0; k<5; ++k) + { + if (class_->mData.mSkills[k][1] == j) + add=1.0; + } + modifierSum += add; + } + creatureStats.getAttribute(attribute).setBase ( std::min(creatureStats.getAttribute(attribute).getBase() + + static_cast((level-1) * modifierSum+0.5), 100) ); + } + } } namespace MWClass @@ -126,15 +187,14 @@ namespace MWClass } else { - for (int i=0; i<8; ++i) - data->mCreatureStats.getAttribute (i).set (10); - for (int i=0; i<3; ++i) data->mCreatureStats.setDynamic (i, 10); data->mCreatureStats.setLevel(ref->mBase->mNpdt12.mLevel); data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt12.mDisposition); data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation); + + autoCalculateAttributes(ref->mBase, data->mCreatureStats); } data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello); From 2c8b0a154175f3320fe4e082a5208d538f707875 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 19 Apr 2013 00:26:36 +0200 Subject: [PATCH 76/99] last minute change to changelog --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index 5f3845776..d5a5dcc18 100644 --- a/readme.txt +++ b/readme.txt @@ -136,6 +136,7 @@ Bug #700: "On the rocks" mod does not load its UV coordinates correctly. Bug #702: Some race mods don't work Bug #711: Crash during character creation Bug #715: Growing Tauryon +Bug #725: Auto calculate stats Feature #55/657: Item Repairing Feature #62/87: Enchanting Feature #99: Pathfinding From 21bdcc9f20d14e7d5aabf906860cee573e0e31a4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 19 Apr 2013 01:33:04 +0200 Subject: [PATCH 77/99] Only use the shader based mygui manager if the fixed pipeline is unavailable --- libs/openengine/gui/manager.cpp | 30 ++++++++++++++++++++++++++---- libs/openengine/gui/manager.hpp | 5 +++-- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index f9117586f..a0a4ab0ae 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -559,6 +559,8 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool assert(mgr); mSceneMgr = mgr; + mShaderRenderManager = NULL; + mRenderManager = NULL; using namespace MyGUI; @@ -574,7 +576,10 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool // Set up OGRE platform (bypassing OgrePlatform). We might make this more generic later. mLogManager = new LogManager(); - mRenderManager = new MyGUI::ShaderBasedRenderManager(); + if (!Ogre::Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(Ogre::RSC_FIXED_FUNCTION)) + mShaderRenderManager = new MyGUI::ShaderBasedRenderManager(); + else + mRenderManager = new MyGUI::OgreRenderManager(); mDataManager = new MyGUI::FixedOgreDataManager(); LogManager::getInstance().setSTDOutputEnabled(logging); @@ -582,7 +587,10 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool if (!theLogFile.empty()) LogManager::getInstance().createDefaultSource(theLogFile); - mRenderManager->initialise(wnd, mgr); + if (mShaderRenderManager) + mShaderRenderManager->initialise(wnd, mgr); + else + mRenderManager->initialise(wnd, mgr); mDataManager->initialise("General"); // Create GUI @@ -592,8 +600,16 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool void MyGUIManager::updateWindow (Ogre::RenderWindow *wnd) { - mRenderManager->setRenderWindow (wnd); - mRenderManager->setActiveViewport(0); + if (mShaderRenderManager) + { + mShaderRenderManager->setRenderWindow (wnd); + mShaderRenderManager->setActiveViewport(0); + } + else + { + mRenderManager->setRenderWindow (wnd); + mRenderManager->setActiveViewport(0); + } } void MyGUIManager::shutdown() @@ -606,6 +622,12 @@ void MyGUIManager::shutdown() delete mRenderManager; mRenderManager = NULL; } + if(mShaderRenderManager) + { + mShaderRenderManager->shutdown(); + delete mShaderRenderManager; + mShaderRenderManager = NULL; + } if(mDataManager) { mDataManager->shutdown(); diff --git a/libs/openengine/gui/manager.hpp b/libs/openengine/gui/manager.hpp index eec867ff8..9535f2a24 100644 --- a/libs/openengine/gui/manager.hpp +++ b/libs/openengine/gui/manager.hpp @@ -8,6 +8,7 @@ namespace MyGUI class Gui; class LogManager; class OgreDataManager; + class OgreRenderManager; class ShaderBasedRenderManager; } @@ -25,12 +26,12 @@ namespace GUI MyGUI::Gui *mGui; MyGUI::LogManager* mLogManager; MyGUI::OgreDataManager* mDataManager; - MyGUI::ShaderBasedRenderManager* mRenderManager; + MyGUI::OgreRenderManager* mRenderManager; + MyGUI::ShaderBasedRenderManager* mShaderRenderManager; Ogre::SceneManager* mSceneMgr; public: - MyGUIManager() : mLogManager(NULL), mDataManager(NULL), mRenderManager(NULL), mGui(NULL) {} MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")) { setup(wnd,mgr,logging, logDir); From 87ce2110dcc912394a6d49599a7bb7558bac68a0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 19 Apr 2013 14:41:26 +0200 Subject: [PATCH 78/99] hotfix for CharacterPreview destructor --- apps/openmw/mwrender/characterpreview.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 8396acaea..41770d489 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -21,13 +21,15 @@ namespace MWRender CharacterPreview::CharacterPreview(MWWorld::Ptr character, int sizeX, int sizeY, const std::string& name, Ogre::Vector3 position, Ogre::Vector3 lookAt) - : mSizeX(sizeX) - , mSizeY(sizeY) - , mName(name) + + : mSceneMgr (0) , mPosition(position) , mLookAt(lookAt) , mCharacter(character) , mAnimation(NULL) + , mName(name) + , mSizeX(sizeX) + , mSizeY(sizeY) { } @@ -88,16 +90,20 @@ namespace MWRender CharacterPreview::~CharacterPreview () { - //Ogre::TextureManager::getSingleton().remove(mName); - mSceneMgr->destroyCamera (mName); - delete mAnimation; - Ogre::Root::getSingleton().destroySceneManager(mSceneMgr); + if (mSceneMgr) + { + //Ogre::TextureManager::getSingleton().remove(mName); + mSceneMgr->destroyAllCameras(); + delete mAnimation; + Ogre::Root::getSingleton().destroySceneManager(mSceneMgr); + } } void CharacterPreview::rebuild() { assert(mAnimation); delete mAnimation; + mAnimation = 0; mAnimation = new NpcAnimation(mCharacter, mNode, MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter), 0, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal)); From 51580ead4f9c20f8b683384c618c5f1b25c2afd7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 19 Apr 2013 17:57:22 +0200 Subject: [PATCH 79/99] Fix consecutive dialogue choices --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 9380ab76c..220adf566 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -426,6 +426,7 @@ namespace MWDialogue void DialogueManager::questionAnswered (const std::string& answer) { + if (mChoiceMap.find(answer) != mChoiceMap.end()) { mChoice = mChoiceMap[answer]; @@ -442,6 +443,10 @@ namespace MWDialogue std::string text = info->mResponse; parseText (text); + mChoiceMap.clear(); + mChoice = -1; + mIsInChoice = false; + MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (Interpreter::fixDefinesDialog(text, interpreterContext)); MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId); @@ -449,9 +454,6 @@ namespace MWDialogue mLastDialogue = *info; } } - mChoiceMap.clear(); - mChoice = -1; - mIsInChoice = false; } updateTopics(); From 2dc01fe56b923c382ebfe7da5c1829d8580bb450 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sun, 21 Apr 2013 11:41:09 -0700 Subject: [PATCH 80/99] fixed the type of iterator uses in MWWorld::Store The containers type used to declare some iterators was not an exact match for the type of the container the iterator was being initialized from. This was causing build failure on windows. --- apps/openmw/mwworld/store.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 959b944e1..7949cbe00 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -475,7 +475,7 @@ namespace MWWorld cell.mData.mX = x, cell.mData.mY = y; std::pair key(x, y); - std::map, ESM::Cell>::const_iterator it = mExt.find(key); + DynamicExt::const_iterator it = mExt.find(key); if (it != mExt.end()) { return &(it->second); } @@ -493,7 +493,7 @@ namespace MWWorld cell.mData.mX = x, cell.mData.mY = y; std::pair key(x, y); - std::map, ESM::Cell>::const_iterator it = mExt.find(key); + DynamicExt::const_iterator it = mExt.find(key); if (it != mExt.end()) { return &(it->second); } @@ -534,7 +534,7 @@ namespace MWWorld void setUp() { //typedef std::vector::iterator Iterator; - typedef std::map, ESM::Cell>::iterator ExtIterator; + typedef DynamicExt::iterator ExtIterator; typedef std::map::iterator IntIterator; //std::sort(mInt.begin(), mInt.end(), RecordCmp()); From a7092ef2d709a575848451ade18e45e360f80f09 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 21 Apr 2013 21:24:48 +0200 Subject: [PATCH 81/99] Fix activation not working sometimes The current player cell was only being updated when the reference was not empty, causing it to incorrectly detect a cell change the first time something was activated in a newly visited cell, immediately closing the opened dialogue again. --- apps/openmw/mwgui/referenceinterface.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/referenceinterface.cpp b/apps/openmw/mwgui/referenceinterface.cpp index 66e036d92..86a85be18 100644 --- a/apps/openmw/mwgui/referenceinterface.cpp +++ b/apps/openmw/mwgui/referenceinterface.cpp @@ -18,17 +18,17 @@ namespace MWGui void ReferenceInterface::checkReferenceAvailable() { - if (mPtr.isEmpty()) - return; - MWWorld::Ptr::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); // check if player has changed cell, or count of the reference has become 0 if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL) - || mPtr.getRefData().getCount() == 0) + || (!mPtr.isEmpty() && mPtr.getRefData().getCount() == 0)) { - mPtr = MWWorld::Ptr(); - onReferenceUnavailable(); + if (!mPtr.isEmpty()) + { + mPtr = MWWorld::Ptr(); + onReferenceUnavailable(); + } } mCurrentPlayerCell = playerCell; From baa7a9160c35b4ce13dde8666a2039eb998354e1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 22 Apr 2013 07:57:53 +0200 Subject: [PATCH 82/99] Rank condition should always fail if NPC is not in a faction --- apps/openmw/mwdialogue/filter.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 78969ffd0..52c7bd4f3 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -73,6 +73,11 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const if (iter->second < info.mData.mRank) return false; } + else if (info.mData.mRank != -1) + { + // if there is a rank condition, but the NPC is not in a faction, always fail + return false; + } // Gender if (!isCreature) From 8d0e5fc0c4b7272508309e4325ab6c5590b2aa8b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 22 Apr 2013 09:56:11 +0200 Subject: [PATCH 83/99] updated changelog once more --- readme.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.txt b/readme.txt index d5a5dcc18..c033f9547 100644 --- a/readme.txt +++ b/readme.txt @@ -137,6 +137,8 @@ Bug #702: Some race mods don't work Bug #711: Crash during character creation Bug #715: Growing Tauryon Bug #725: Auto calculate stats +Bug #728: Failure to open container and talk dialogue +Bug #731: Crash with Mush-Mere's "background" topic Feature #55/657: Item Repairing Feature #62/87: Enchanting Feature #99: Pathfinding From ad05f238deaec2679336e7d0c35366b53cdfa4c1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 23 Apr 2013 22:47:21 +0200 Subject: [PATCH 84/99] Use one vertex buffer for all UV coordinates --- components/nifogre/mesh.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp index c0a7af5c3..6d940deed 100644 --- a/components/nifogre/mesh.cpp +++ b/components/nifogre/mesh.cpp @@ -260,13 +260,24 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape // Texture UV coordinates size_t numUVs = data->uvlist.size(); - for(size_t i = 0;i < numUVs;i++) + if (numUVs) { - vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2), - srcVerts.size(), Ogre::HardwareBuffer::HBU_STATIC); - vbuf->writeData(0, vbuf->getSizeInBytes(), &data->uvlist[i][0], true); + size_t elemSize = Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2); + + for(size_t i = 0; i < numUVs; i++) + decl->addElement(nextBuf, elemSize*i, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, i); + + vbuf = hwBufMgr->createVertexBuffer(decl->getVertexSize(nextBuf), srcVerts.size(), + Ogre::HardwareBuffer::HBU_STATIC); + + std::vector allUVs; + allUVs.reserve(srcVerts.size()*numUVs); + for (size_t vert = 0; vertuvlist[i][vert]); + + vbuf->writeData(0, elemSize*srcVerts.size()*numUVs, &allUVs[0], true); - decl->addElement(nextBuf, 0, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, i); bind->setBinding(nextBuf++, vbuf); } From 933f89414842eaa566803bb6ed506e85380fd2e4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 25 Apr 2013 02:31:51 +0200 Subject: [PATCH 85/99] Added Blind and Night Eye effects --- apps/openmw/mwrender/renderingmanager.cpp | 27 ++++++++++++++++------- libs/openengine/ogre/fader.cpp | 12 +++++----- libs/openengine/ogre/fader.hpp | 4 ++++ 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 029cf394b..da51f10c1 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -30,6 +30,8 @@ #include "../mwbase/inputmanager.hpp" // FIXME #include "../mwbase/windowmanager.hpp" // FIXME +#include "../mwmechanics/creaturestats.hpp" + #include "../mwworld/ptr.hpp" #include "../mwworld/player.hpp" @@ -315,13 +317,15 @@ void RenderingManager::update (float duration, bool paused) { MWBase::World *world = MWBase::Environment::get().getWorld(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + + int blind = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude; + mRendering.getFader()->setFactor(1.f-(blind / 100.f)); + + setAmbientMode(); + // player position - MWWorld::RefData &data = - MWBase::Environment::get() - .getWorld() - ->getPlayer() - .getPlayer() - .getRefData(); + MWWorld::RefData &data = player.getRefData(); float *_playerPos = data.getPosition().pos; Ogre::Vector3 playerPos(_playerPos[0], _playerPos[1], _playerPos[2]); @@ -597,8 +601,15 @@ void RenderingManager::setSunColour(const Ogre::ColourValue& colour) void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour) { - mRendering.getScene()->setAmbientLight(colour); - mTerrainManager->setAmbient(colour); + mAmbientColor = colour; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + int nightEye = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::NightEye)).mMagnitude; + Ogre::ColourValue final = colour; + final += Ogre::ColourValue(0.7,0.7,0.7,0) * std::min(1.f, (nightEye/100.f)); + + mRendering.getScene()->setAmbientLight(final); + mTerrainManager->setAmbient(final); } void RenderingManager::sunEnable(bool real) diff --git a/libs/openengine/ogre/fader.cpp b/libs/openengine/ogre/fader.cpp index 9390d0664..923b0b7e3 100644 --- a/libs/openengine/ogre/fader.cpp +++ b/libs/openengine/ogre/fader.cpp @@ -19,6 +19,7 @@ Fader::Fader(Ogre::SceneManager* sceneMgr) , mTargetAlpha(0.f) , mCurrentAlpha(0.f) , mStartAlpha(0.f) + , mFactor(1.f) { // Create the fading material MaterialPtr material = MaterialManager::getSingleton().create("FadeInOutMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ); @@ -62,19 +63,20 @@ void Fader::update(float dt) mCurrentAlpha += dt/mTargetTime * (mTargetAlpha-mStartAlpha); if (mCurrentAlpha > mTargetAlpha) mCurrentAlpha = mTargetAlpha; } - - applyAlpha(); - + mRemainingTime -= dt; } - if (mCurrentAlpha == 0.f) mRectangle->setVisible(false); + if (1.f-((1.f-mCurrentAlpha) * mFactor) == 0.f) + mRectangle->setVisible(false); + else + applyAlpha(); } void Fader::applyAlpha() { mRectangle->setVisible(true); - mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, mCurrentAlpha); + mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, 1.f-((1.f-mCurrentAlpha) * mFactor)); } void Fader::fadeIn(float time) diff --git a/libs/openengine/ogre/fader.hpp b/libs/openengine/ogre/fader.hpp index bddf5dc91..53124e2f6 100644 --- a/libs/openengine/ogre/fader.hpp +++ b/libs/openengine/ogre/fader.hpp @@ -29,6 +29,8 @@ namespace Render void fadeOut(const float time); void fadeTo(const int percent, const float time); + void setFactor (float factor) { mFactor = factor; } + private: enum FadingMode { @@ -49,6 +51,8 @@ namespace Render float mCurrentAlpha; float mStartAlpha; + float mFactor; + Ogre::SceneManager* mSceneMgr; }; }} From 0817d59f23d4b2f36711109696c87cf6c22ad672 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Apr 2013 19:09:36 -0700 Subject: [PATCH 86/99] Allow specifying where to start in an animation --- apps/openmw/mwmechanics/character.cpp | 14 +++++++------- apps/openmw/mwrender/animation.cpp | 18 +++++++++++++----- apps/openmw/mwrender/animation.hpp | 18 ++++++++++++++++-- apps/openmw/mwrender/characterpreview.cpp | 4 ++-- 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 2a7ab8b09..9397b2e35 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -121,7 +121,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim mAnimation->setAccumulation(Ogre::Vector3(0.0f)); } if(mAnimation->hasAnimation(mCurrentGroup)) - mAnimation->play(mCurrentGroup, "start", "stop", loop ? (~(size_t)0) : 0); + mAnimation->play(mCurrentGroup, "start", "stop", 1.0f, loop ? (~(size_t)0) : 0); } CharacterController::~CharacterController() @@ -204,15 +204,15 @@ void CharacterController::update(float duration, Movement &movement) } else if(getState() != CharState_SpecialIdle || !mAnimation->isPlaying(0)) { - if(mAnimQueue.size() > 0) + if(mAnimQueue.size() == 0) + setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)), true); + else { mCurrentGroup = mAnimQueue.front().first; size_t count = mAnimQueue.front().second; mAnimQueue.pop_front(); - mAnimation->play(mCurrentGroup, "start", "stop", count); + mAnimation->play(mCurrentGroup, "start", "stop", 0.0f, count); } - else - setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)), true); } movement.mRotation[0] += rot.x * duration; @@ -244,7 +244,7 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int mAnimQueue.clear(); mCurrentGroup = groupname; mState = CharState_SpecialIdle; - mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", count-1); + mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1); } else if(mode == 0) { @@ -275,7 +275,7 @@ void CharacterController::setState(CharacterState state, bool loop) if(mAnimation->hasAnimation(anim)) { mCurrentGroup = anim; - mAnimation->play(mCurrentGroup, "start", "stop", loop ? (~(size_t)0) : 0); + mAnimation->play(mCurrentGroup, "start", "stop", 0.0f, loop ? (~(size_t)0) : 0); } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index c2f453516..0f509d276 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -402,7 +402,7 @@ void Animation::updatePosition(Ogre::Vector3 &position) mAccumRoot->setPosition(-mLastPosition); } -bool Animation::reset(size_t layeridx, const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname, const std::string &start, const std::string &stop) +bool Animation::reset(size_t layeridx, const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint) { std::string tag = groupname+": "+start; NifOgre::TextKeyMap::const_iterator startkey(keys.begin()); @@ -432,9 +432,17 @@ bool Animation::reset(size_t layeridx, const NifOgre::TextKeyMap &keys, NifOgre: mLayer[layeridx].mLoopStartKey = startkey; mLayer[layeridx].mStopKey = stopkey; mLayer[layeridx].mNextKey = startkey; - mLayer[layeridx].mNextKey++; - mLayer[layeridx].mTime = mLayer[layeridx].mStartKey->first; + mLayer[layeridx].mTime = mLayer[layeridx].mStartKey->first + ((mLayer[layeridx].mStopKey->first- + mLayer[layeridx].mStartKey->first) * startpoint); + + tag = groupname+": loop start"; + while(mLayer[layeridx].mNextKey->first <= mLayer[layeridx].mTime && mLayer[layeridx].mNextKey != mLayer[layeridx].mStopKey) + { + if(mLayer[layeridx].mNextKey->second == tag) + mLayer[layeridx].mLoopStartKey = mLayer[layeridx].mNextKey; + mLayer[layeridx].mNextKey++; + } if(layeridx == 0 && nonaccumctrl) mLastPosition = nonaccumctrl->getTranslation(mLayer[layeridx].mTime) * mAccumulate; @@ -507,7 +515,7 @@ bool Animation::handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_ } -void Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, size_t loops) +void Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, size_t loops) { // TODO: parameterize this size_t layeridx = 0; @@ -554,7 +562,7 @@ void Animation::play(const std::string &groupname, const std::string &start, con if(!foundanim) { - if(!reset(layeridx, keys, nonaccumctrl, groupname, start, stop)) + if(!reset(layeridx, keys, nonaccumctrl, groupname, start, stop, startpoint)) continue; mLayer[layeridx].mGroupName = groupname; mLayer[layeridx].mTextKeys = &keys; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 526bca69a..d7f38545f 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -95,7 +95,10 @@ protected: * the marker is not found, or if the markers are the same, it returns * false. */ - bool reset(size_t layeridx, const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname, const std::string &start, const std::string &stop); + bool reset(size_t layeridx, const NifOgre::TextKeyMap &keys, + NifOgre::NodeTargetValue *nonaccumctrl, + const std::string &groupname, const std::string &start, const std::string &stop, + float startpoint); bool doLoop(size_t layeridx); @@ -123,7 +126,18 @@ public: void setSpeed(float speed); - void play(const std::string &groupname, const std::string &start, const std::string &stop, size_t loops); + /** Plays an animation. + * \param groupname Name of the animation group to play. + * \param start Key marker from which to start. + * \param stop Key marker to stop at. + * \param startpoint How far in between the two markers to start. 0 starts + * at the start marker, 1 starts at the stop marker. + * \param loops How many times to loop the animation. This will use the + * "loop start" and "loop stop" markers if they exist, + * otherwise it will use "start" and "stop". + */ + void play(const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, size_t loops); + virtual Ogre::Vector3 runAnimation(float timepassed); bool isPlaying(size_t layeridx) const diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index c22d8a4da..24b089db3 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -155,7 +155,7 @@ namespace MWRender if (!mSelectionBuffer) mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, 0); - mAnimation->play("inventoryhandtohand", "start", "stop", 0); + mAnimation->play("inventoryhandtohand", "start", "stop", 0.0f, 0); } // -------------------------------------------------------------------------------------------------- @@ -189,7 +189,7 @@ namespace MWRender void RaceSelectionPreview::onSetup () { - mAnimation->play("idle", "start", "stop", 0); + mAnimation->play("idle", "start", "stop", 0.0f, 0); updateCamera(); } From 9e3917881d5d4f0b1c23c31d879369a06a9bc4bd Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 25 Apr 2013 04:44:30 +0200 Subject: [PATCH 87/99] Don't fail on nonexistent items in a levelled list --- apps/openmw/mwworld/containerstore.cpp | 85 ++++++++++++++------------ 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 05026a98b..5cf534239 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -193,59 +193,68 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: { count = std::abs(count); /// \todo implement item restocking (indicated by negative count) - ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id); - - if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) + try { - const ESM::ItemLevList* levItem = ref.getPtr().get()->mBase; - const std::vector& items = levItem->mList; + ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - int playerLevel = MWWorld::Class::get(player).getCreatureStats(player).getLevel(); - - failChance += levItem->mChanceNone; - - if (topLevel && count > 1 && levItem->mFlags & ESM::ItemLevList::Each) + if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) { - for (int i=0; i()->mBase; + const std::vector& items = levItem->mList; - float random = static_cast (std::rand()) / RAND_MAX; - if (random >= failChance/100.f) - { - std::vector candidates; - int highestLevel = 0; - for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + int playerLevel = MWWorld::Class::get(player).getCreatureStats(player).getLevel(); + + failChance += levItem->mChanceNone; + + if (topLevel && count > 1 && levItem->mFlags & ESM::ItemLevList::Each) { - if (it->mLevel > highestLevel) - highestLevel = it->mLevel; + for (int i=0; i highest = std::make_pair(-1, ""); - for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + float random = static_cast (std::rand()) / RAND_MAX; + if (random >= failChance/100.f) { - if (playerLevel >= it->mLevel - && (levItem->mFlags & ESM::ItemLevList::AllLevels || it->mLevel == highestLevel)) + std::vector candidates; + int highestLevel = 0; + for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) { - candidates.push_back(it->mId); - if (it->mLevel >= highest.first) - highest = std::make_pair(it->mLevel, it->mId); + if (it->mLevel > highestLevel) + highestLevel = it->mLevel; } + std::pair highest = std::make_pair(-1, ""); + for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + { + if (playerLevel >= it->mLevel + && (levItem->mFlags & ESM::ItemLevList::AllLevels || it->mLevel == highestLevel)) + { + candidates.push_back(it->mId); + if (it->mLevel >= highest.first) + highest = std::make_pair(it->mLevel, it->mId); + } + + } + if (!candidates.size()) + return; + std::string item = candidates[std::rand()%candidates.size()]; + addInitialItem(item, owner, count, failChance, false); } - if (!candidates.size()) - return; - std::string item = candidates[std::rand()%candidates.size()]; - addInitialItem(item, owner, count, failChance, false); + } + else + { + ref.getPtr().getRefData().setCount (count); + ref.getPtr().getCellRef().mOwner = owner; + addImp (ref.getPtr()); } } - else + catch (std::logic_error& e) { - ref.getPtr().getRefData().setCount (count); - ref.getPtr().getCellRef().mOwner = owner; - addImp (ref.getPtr()); + // Vanilla doesn't fail on nonexistent items in levelled lists + std::cerr << "Warning: ignoring nonexistent item '" << id << "'" << std::endl; + return; } } From 24f1eba9025f3e7dca4ff20fcd9fb9fba57eb74a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Apr 2013 22:45:43 -0700 Subject: [PATCH 88/99] Keep track of whether an animation supplies movement Also handle it when it doesn't. --- apps/openmw/mwmechanics/character.cpp | 18 ++++++++++++------ apps/openmw/mwmechanics/character.hpp | 2 ++ apps/openmw/mwrender/animation.cpp | 22 ++++++++++++++++------ apps/openmw/mwrender/animation.hpp | 5 ++++- apps/openmw/mwrender/npcanimation.cpp | 6 +++--- 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 9397b2e35..0085f8de6 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -103,7 +103,7 @@ static void getStateInfo(CharacterState state, std::string *group) CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop) - : mPtr(ptr), mAnimation(anim), mState(state), mSkipAnim(false) + : mPtr(ptr), mAnimation(anim), mState(state), mSkipAnim(false), mMovingAnim(false) { if(!mAnimation) return; @@ -121,7 +121,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim mAnimation->setAccumulation(Ogre::Vector3(0.0f)); } if(mAnimation->hasAnimation(mCurrentGroup)) - mAnimation->play(mCurrentGroup, "start", "stop", 1.0f, loop ? (~(size_t)0) : 0); + mMovingAnim = mAnimation->play(mCurrentGroup, "start", "stop", 1.0f, loop ? (~(size_t)0) : 0); } CharacterController::~CharacterController() @@ -180,6 +180,9 @@ void CharacterController::update(float duration, Movement &movement) setState(inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft) : (sneak ? CharState_SneakLeft : (isrunning ? CharState_RunLeft : CharState_WalkLeft)), true); + // If this animation isn't moving us sideways, do it manually + if(!mMovingAnim) + movement.mPosition[0] += vec.x * (speed*duration); // Apply any forward/backward movement manually movement.mPosition[1] += vec.y * (speed*duration); } @@ -194,6 +197,9 @@ void CharacterController::update(float duration, Movement &movement) // Apply any sideways movement manually movement.mPosition[0] += vec.x * (speed*duration); + // If this animation isn't moving us forward/backward, do it manually + if(!mMovingAnim) + movement.mPosition[1] += vec.y * (speed*duration); } else if(rot.z != 0.0f && !inwater && !sneak) { @@ -211,7 +217,7 @@ void CharacterController::update(float duration, Movement &movement) mCurrentGroup = mAnimQueue.front().first; size_t count = mAnimQueue.front().second; mAnimQueue.pop_front(); - mAnimation->play(mCurrentGroup, "start", "stop", 0.0f, count); + mMovingAnim = mAnimation->play(mCurrentGroup, "start", "stop", 0.0f, count); } } @@ -244,7 +250,7 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int mAnimQueue.clear(); mCurrentGroup = groupname; mState = CharState_SpecialIdle; - mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1); + mMovingAnim = mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1); } else if(mode == 0) { @@ -272,10 +278,10 @@ void CharacterController::setState(CharacterState state, bool loop) std::string anim; getStateInfo(mState, &anim); - if(mAnimation->hasAnimation(anim)) + if((mMovingAnim=mAnimation->hasAnimation(anim)) != false) { mCurrentGroup = anim; - mAnimation->play(mCurrentGroup, "start", "stop", 0.0f, loop ? (~(size_t)0) : 0); + mMovingAnim = mAnimation->play(mCurrentGroup, "start", "stop", 0.0f, loop ? (~(size_t)0) : 0); } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index ffd220ff2..8e4bd2f09 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -79,6 +79,8 @@ class CharacterController CharacterState mState; bool mSkipAnim; + bool mMovingAnim; + public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop); virtual ~CharacterController(); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 0f509d276..5838b1ff3 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -317,7 +317,7 @@ void Animation::updatePtr(const MWWorld::Ptr &ptr) } -float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const std::string &groupname) +float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const Ogre::Vector3 &accum, const std::string &groupname) { const std::string start = groupname+": start"; const std::string loopstart = groupname+": loop start"; @@ -340,8 +340,8 @@ float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::Node if(stoptime > starttime) { - Ogre::Vector3 startpos = nonaccumctrl->getTranslation(starttime); - Ogre::Vector3 endpos = nonaccumctrl->getTranslation(stoptime); + Ogre::Vector3 startpos = nonaccumctrl->getTranslation(starttime) * accum; + Ogre::Vector3 endpos = nonaccumctrl->getTranslation(stoptime) * accum; return startpos.distance(endpos) / (stoptime-starttime); } @@ -515,7 +515,7 @@ bool Animation::handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_ } -void Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, size_t loops) +bool Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, size_t loops) { // TODO: parameterize this size_t layeridx = 0; @@ -523,6 +523,7 @@ void Animation::play(const std::string &groupname, const std::string &start, con for(std::vector::iterator iter(mObjects.begin());iter != mObjects.end();iter++) iter->mActiveLayers &= ~(1<mActiveLayers |= (1< 0.0f) break; + mAnimVelocity = calcAnimVelocity(keys, nonaccumctrl, mAccumulate, groupname); + if(mAnimVelocity > 1.0f) + { + movinganim = (nonaccumctrl==mNonAccumCtrl); + break; + } } if(!foundanim) std::cerr<< "Failed to find animation "< *nonaccumctrl, + const Ogre::Vector3 &accum, const std::string &groupname); /* Updates a skeleton instance so that all bones matching the source skeleton (based on @@ -135,8 +136,10 @@ public: * \param loops How many times to loop the animation. This will use the * "loop start" and "loop stop" markers if they exist, * otherwise it will use "start" and "stop". + * \return Boolean specifying whether the animation will return movement + * for the character at all. */ - void play(const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, size_t loops); + bool play(const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, size_t loops); virtual Ogre::Vector3 runAnimation(float timepassed); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 0b05b4ba6..c0f2e7e29 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -97,10 +97,10 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); addObjectList(node, smodel, true); - if(!mNpc->isMale() && !isBeast) - addObjectList(node, "meshes\\base_anim_female.nif", true); - else if(mBodyPrefix.find("argonian") != std::string::npos) + if(mBodyPrefix.find("argonian") != std::string::npos) addObjectList(node, "meshes\\argonian_swimkna.nif", true); + else if(!mNpc->isMale() && !isBeast) + addObjectList(node, "meshes\\base_anim_female.nif", true); if(mNpc->mModel.length() > 0) addObjectList(node, "meshes\\"+mNpc->mModel, true); From 09fc50948bff04e319dedc646c17e92be0f16ae5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 25 Apr 2013 00:03:27 -0700 Subject: [PATCH 89/99] Add methods to disable and get info for a layer --- apps/openmw/mwrender/animation.cpp | 54 ++++++++++++++++++++++-------- apps/openmw/mwrender/animation.hpp | 16 ++++++++- 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 5838b1ff3..36fa1197d 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -225,6 +225,8 @@ void Animation::updateActiveControllers() break; } } + if(ctrls == NULL) + continue; /* Check if any objectlists are active on subsequent layers. Include * those layers if not. @@ -239,7 +241,6 @@ void Animation::updateActiveControllers() } } - assert(ctrls != NULL); std::vector >::const_iterator ctrl(ctrls->begin()); for(;ctrl != ctrls->end();ctrl++) { @@ -525,21 +526,9 @@ bool Animation::play(const std::string &groupname, const std::string &start, con bool movinganim = false; bool foundanim = false; - if(groupname.empty()) - { - // Do not allow layer 0 to be disabled - assert(layeridx != 0); - mLayer[layeridx].mGroupName.clear(); - mLayer[layeridx].mTextKeys = NULL; - mLayer[layeridx].mControllers = NULL; - mLayer[layeridx].mLoopCount = 0; - mLayer[layeridx].mPlaying = false; - - foundanim = true; - } /* Look in reverse; last-inserted source has priority. */ - else for(std::vector::reverse_iterator iter(mObjects.rbegin());iter != mObjects.rend();iter++) + for(std::vector::reverse_iterator iter(mObjects.rbegin());iter != mObjects.rend();iter++) { NifOgre::ObjectList &objlist = iter->mObjectList; if(objlist.mTextKeys.size() == 0) @@ -602,6 +591,43 @@ bool Animation::play(const std::string &groupname, const std::string &start, con return movinganim; } +void Animation::disable(size_t layeridx) +{ + if(mLayer[layeridx].mGroupName.empty()) + return; + + for(std::vector::iterator iter(mObjects.begin());iter != mObjects.end();iter++) + iter->mActiveLayers &= ~(1<first) / + (mLayer[layeridx].mStopKey->first - mLayer[layeridx].mStartKey->first); + if(groupname) *groupname = mLayer[layeridx].mGroupName; + if(start) *start = mLayer[layeridx].mStartKey->second.substr(mLayer[layeridx].mGroupName.size()+2); + if(stop) *stop = mLayer[layeridx].mStopKey->second.substr(mLayer[layeridx].mGroupName.size()+2); + return true; +} + + Ogre::Vector3 Animation::runAnimation(float duration) { Ogre::Vector3 movement(0.0f); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index f349dd71f..4d342e0b1 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -141,8 +141,22 @@ public: */ bool play(const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, size_t loops); - virtual Ogre::Vector3 runAnimation(float timepassed); + /** Stops and removes the animation from the given layer. */ + void disable(size_t layeridx); + /** Gets info about the given animation layer. + * \param layeridx Layer index to get info about. + * \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc. + * \param groupname Stores animation group being played. + * \param start Stores the start key + * \param stop Stores the stop key + * \return True if an animation is active on the layer, false otherwise. + */ + bool getInfo(size_t layeridx, float *complete=NULL, std::string *groupname=NULL, std::string *start=NULL, std::string *stop=NULL) const; + + virtual Ogre::Vector3 runAnimation(float duration); + + /* Returns if there's an animation playing on the given layer. */ bool isPlaying(size_t layeridx) const { return mLayer[layeridx].mPlaying; } From 7eac37be231eb7ef883ca65b448383b81f7e7c6e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 25 Apr 2013 00:18:02 -0700 Subject: [PATCH 90/99] Make sure the old layer is cleared before starting the next --- apps/openmw/mwrender/animation.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 36fa1197d..8d6508aee 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -524,6 +524,12 @@ bool Animation::play(const std::string &groupname, const std::string &start, con for(std::vector::iterator iter(mObjects.begin());iter != mObjects.end();iter++) iter->mActiveLayers &= ~(1< Date: Thu, 25 Apr 2013 00:52:26 -0700 Subject: [PATCH 91/99] Only set mSkelBase from the first objectlist --- apps/openmw/mwrender/animation.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8d6508aee..7383734ee 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -106,7 +106,7 @@ void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, b NifOgre::ObjectList &objlist = obj.mObjectList; if(objlist.mSkelBase) { - if(!mSkelBase) + if(mObjects.size() == 1) mSkelBase = objlist.mSkelBase; Ogre::AnimationStateSet *aset = objlist.mSkelBase->getAllAnimationStates(); @@ -125,7 +125,9 @@ void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, b Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); while(boneiter.hasMoreElements()) boneiter.getNext()->setManuallyControlled(true); - + } + if(objlist.mSkelBase && mSkelBase) + { Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); if(mSkelBase == objlist.mSkelBase) { @@ -286,6 +288,9 @@ NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::Tex bool Animation::hasAnimation(const std::string &anim) { + if(!mSkelBase) + return false; + for(std::vector::const_iterator iter(mObjects.begin());iter != mObjects.end();iter++) { if(iter->mObjectList.mTextKeys.size() == 0) @@ -521,6 +526,9 @@ bool Animation::play(const std::string &groupname, const std::string &start, con // TODO: parameterize this size_t layeridx = 0; + if(!mSkelBase) + return false; + for(std::vector::iterator iter(mObjects.begin());iter != mObjects.end();iter++) iter->mActiveLayers &= ~(1< Date: Thu, 25 Apr 2013 10:27:43 +0200 Subject: [PATCH 92/99] fixed a typo in a doxygen comment --- apps/openmw/mwdialogue/journalentry.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwdialogue/journalentry.hpp b/apps/openmw/mwdialogue/journalentry.hpp index 19a9f42b5..9d009b48b 100644 --- a/apps/openmw/mwdialogue/journalentry.hpp +++ b/apps/openmw/mwdialogue/journalentry.hpp @@ -3,7 +3,7 @@ #include -namespace MWWorld +namespace MWWorld { struct ESMStore; } @@ -27,7 +27,7 @@ namespace MWDialogue static std::string idFromIndex (const std::string& topic, int index); }; - /// \biref A quest entry with a timestamp. + /// \brief A quest entry with a timestamp. struct StampedJournalEntry : public JournalEntry { int mDay; From d5d14c514758b9137850b521a12f58f0cc48edbf Mon Sep 17 00:00:00 2001 From: pvdk Date: Thu, 25 Apr 2013 11:04:12 +0200 Subject: [PATCH 93/99] Fixes issue with multiple spaces in plugin filename not being read correctly --- apps/launcher/settings/gamesettings.cpp | 6 +++--- apps/launcher/settings/settingsbase.hpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/launcher/settings/gamesettings.cpp b/apps/launcher/settings/gamesettings.cpp index 56c08582f..9a9b8df41 100644 --- a/apps/launcher/settings/gamesettings.cpp +++ b/apps/launcher/settings/gamesettings.cpp @@ -96,15 +96,15 @@ bool GameSettings::readFile(QTextStream &stream) QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); while (!stream.atEnd()) { - QString line = stream.readLine().simplified(); + QString line = stream.readLine(); if (line.isEmpty() || line.startsWith("#")) continue; if (keyRe.indexIn(line) != -1) { - QString key = keyRe.cap(1).simplified(); - QString value = keyRe.cap(2).simplified(); + QString key = keyRe.cap(1); + QString value = keyRe.cap(2); // Don't remove existing data entries if (key != QLatin1String("data")) diff --git a/apps/launcher/settings/settingsbase.hpp b/apps/launcher/settings/settingsbase.hpp index bbfad1fbb..155f5f9a9 100644 --- a/apps/launcher/settings/settingsbase.hpp +++ b/apps/launcher/settings/settingsbase.hpp @@ -52,7 +52,7 @@ public: QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); while (!stream.atEnd()) { - QString line = stream.readLine().simplified(); + QString line = stream.readLine(); if (line.isEmpty() || line.startsWith("#")) continue; @@ -65,8 +65,8 @@ public: if (keyRe.indexIn(line) != -1) { - QString key = keyRe.cap(1).simplified(); - QString value = keyRe.cap(2).simplified(); + QString key = keyRe.cap(1); + QString value = keyRe.cap(2); if (!sectionPrefix.isEmpty()) key.prepend(sectionPrefix); From 9be20b90f1bfa0fea800f12db2f176371a4bd33d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 25 Apr 2013 03:35:40 -0700 Subject: [PATCH 94/99] Fix potential infinite loop when layer 0 is empty --- apps/openmw/mwrender/animation.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 7383734ee..81701c65e 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -228,7 +228,10 @@ void Animation::updateActiveControllers() } } if(ctrls == NULL) + { + layer++; continue; + } /* Check if any objectlists are active on subsequent layers. Include * those layers if not. From 9a2a32f2a5c5a826cbac4f441927fc1610c0730d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 25 Apr 2013 03:44:15 -0700 Subject: [PATCH 95/99] Add a method to clear all Animation sources except the first The first needs to remain, since that's where any mSkelBase entity comes from, and where any retrieved nodes are from. --- apps/openmw/mwrender/animation.cpp | 33 ++++++++++++++++++++++++++++++ apps/openmw/mwrender/animation.hpp | 5 +++++ 2 files changed, 38 insertions(+) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 81701c65e..b4a219e38 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -187,6 +187,36 @@ void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::ui } +void Animation::clearExtraSources() +{ + for(size_t layer = 0;layer < sMaxLayers;layer++) + { + mLayer[layer].mGroupName.clear(); + mLayer[layer].mTextKeys = NULL; + mLayer[layer].mControllers = NULL; + mLayer[layer].mTime = 0.0f; + mLayer[layer].mLoopCount = 0; + mLayer[layer].mPlaying = false; + } + mNonAccumCtrl = NULL; + mAnimVelocity = 0.0f; + + mLastPosition = Ogre::Vector3(0.0f); + if(mAccumRoot) + mAccumRoot->setPosition(mLastPosition); + + if(mObjects.size() > 1) + { + mObjects.resize(1); + mObjects[0].mActiveLayers = 0; + + NifOgre::ObjectList &objlist = mObjects[0].mObjectList; + mActiveCtrls.clear(); + mActiveCtrls.insert(mActiveCtrls.end(), objlist.mControllers.begin(), objlist.mControllers.end()); + } +} + + void Animation::updateActiveControllers() { mActiveCtrls.clear(); @@ -538,6 +568,7 @@ bool Animation::play(const std::string &groupname, const std::string &start, con mLayer[layeridx].mGroupName.clear(); mLayer[layeridx].mTextKeys = NULL; mLayer[layeridx].mControllers = NULL; + mLayer[layeridx].mTime = 0.0f; mLayer[layeridx].mLoopCount = 0; mLayer[layeridx].mPlaying = false; @@ -571,6 +602,7 @@ bool Animation::play(const std::string &groupname, const std::string &start, con { if(!reset(layeridx, keys, nonaccumctrl, groupname, start, stop, startpoint)) continue; + mLayer[layeridx].mGroupName = groupname; mLayer[layeridx].mTextKeys = &keys; mLayer[layeridx].mControllers = &objlist.mControllers; @@ -619,6 +651,7 @@ void Animation::disable(size_t layeridx) mLayer[layeridx].mGroupName.clear(); mLayer[layeridx].mTextKeys = NULL; mLayer[layeridx].mControllers = NULL; + mLayer[layeridx].mTime = 0.0f; mLayer[layeridx].mLoopCount = 0; mLayer[layeridx].mPlaying = false; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 4d342e0b1..3b6cc4abf 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -116,6 +116,11 @@ public: Animation(const MWWorld::Ptr &ptr); virtual ~Animation(); + /** Clears all ObjectLists except the first one. As a consequence, any + * playing animations are stopped. + */ + void clearExtraSources(); + void updatePtr(const MWWorld::Ptr &ptr); bool hasAnimation(const std::string &anim); From 2769adcee09fd51ede8063c4705436b485c1a167 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 25 Apr 2013 05:23:05 -0700 Subject: [PATCH 96/99] Implement switching to and from first-person animations Note that the current animation gets interrupted, so switching to first person may break movement until you stop and try to move again. This can be fixed by alerting the character controller in the mechanics manager to force a state update when switching POV. --- apps/openmw/mwrender/npcanimation.cpp | 42 +++++++++++++-------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index c0f2e7e29..563bca4c7 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -97,14 +97,10 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); addObjectList(node, smodel, true); - if(mBodyPrefix.find("argonian") != std::string::npos) - addObjectList(node, "meshes\\argonian_swimkna.nif", true); - else if(!mNpc->isMale() && !isBeast) - addObjectList(node, "meshes\\base_anim_female.nif", true); - if(mNpc->mModel.length() > 0) - addObjectList(node, "meshes\\"+mNpc->mModel, true); - - forceUpdate(); + if(viewMode != VM_HeadOnly) + setViewMode(viewMode); + else + forceUpdate(); } void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) @@ -112,28 +108,30 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) assert(viewMode != VM_HeadOnly); mViewMode = viewMode; - /* FIXME: Enable this once first-person animations work. */ -#if 0 + Ogre::SceneNode *node = mInsert->getParentSceneNode(); + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.get().find(mNpc->mRace); - bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; - std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - std::vector skelnames(1, smodel); - if(!mNpc->isMale() && !isBeast) - skelnames.push_back("meshes\\base_anim_female.nif"); - else if(mBodyPrefix.find("argonian") != std::string::npos) - skelnames.push_back("meshes\\argonian_swimkna.nif"); + clearExtraSources(); + if(mBodyPrefix.find("argonian") != std::string::npos) + addObjectList(node, "meshes\\argonian_swimkna.nif", true); + else if(!mNpc->isMale() && !isBeast) + addObjectList(node, "meshes\\base_anim_female.nif", true); if(mNpc->mModel.length() > 0) - skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel)); + addObjectList(node, "meshes\\"+mNpc->mModel, true); if(mViewMode == VM_FirstPerson) { - smodel = (!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif"); - skelnames.push_back(smodel); + /* A bit counter-intuitive, but unlike third-person anims, it seems + * beast races get both base_anim.1st.nif and base_animkna.1st.nif. + */ + addObjectList(node, "meshes\\base_anim.1st.nif", true); + if(isBeast) + addObjectList(node, "meshes\\base_animkna.1st.nif", true); + if(!mNpc->isMale() && !isBeast) + addObjectList(node, "meshes\\base_anim_female.1st.nif", true); } - setAnimationSources(skelnames); -#endif for(size_t i = 0;i < sPartListSize;i++) removeIndividualPart(i); From 9da2c9153e7b035cb3bb46cc4437586c6fcd21c1 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 25 Apr 2013 06:12:37 -0700 Subject: [PATCH 97/99] Don't store the animation group string in the character controller --- apps/openmw/mwmechanics/character.cpp | 21 +++++++++------------ apps/openmw/mwmechanics/character.hpp | 1 - 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0085f8de6..cf323b0f2 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -108,7 +108,8 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim if(!mAnimation) return; - getStateInfo(mState, &mCurrentGroup); + std::string group; + getStateInfo(mState, &group); if(MWWorld::Class::get(mPtr).isActor()) { /* Accumulate along X/Y only for now, until we can figure out how we should @@ -120,8 +121,8 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim /* Don't accumulate with non-actors. */ mAnimation->setAccumulation(Ogre::Vector3(0.0f)); } - if(mAnimation->hasAnimation(mCurrentGroup)) - mMovingAnim = mAnimation->play(mCurrentGroup, "start", "stop", 1.0f, loop ? (~(size_t)0) : 0); + if(mAnimation->hasAnimation(group)) + mMovingAnim = mAnimation->play(group, "start", "stop", 1.0f, loop ? (~(size_t)0) : 0); } CharacterController::~CharacterController() @@ -214,10 +215,10 @@ void CharacterController::update(float duration, Movement &movement) setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)), true); else { - mCurrentGroup = mAnimQueue.front().first; - size_t count = mAnimQueue.front().second; + mMovingAnim = mAnimation->play(mAnimQueue.front().first, + "start", "stop", 0.0f, + mAnimQueue.front().second); mAnimQueue.pop_front(); - mMovingAnim = mAnimation->play(mCurrentGroup, "start", "stop", 0.0f, count); } } @@ -248,9 +249,8 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int if(mode != 0 || getState() != CharState_SpecialIdle) { mAnimQueue.clear(); - mCurrentGroup = groupname; mState = CharState_SpecialIdle; - mMovingAnim = mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1); + mMovingAnim = mAnimation->play(groupname, ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1); } else if(mode == 0) { @@ -279,10 +279,7 @@ void CharacterController::setState(CharacterState state, bool loop) std::string anim; getStateInfo(mState, &anim); if((mMovingAnim=mAnimation->hasAnimation(anim)) != false) - { - mCurrentGroup = anim; - mMovingAnim = mAnimation->play(mCurrentGroup, "start", "stop", 0.0f, loop ? (~(size_t)0) : 0); - } + mMovingAnim = mAnimation->play(anim, "start", "stop", 0.0f, loop ? (~(size_t)0) : 0); } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 8e4bd2f09..d102e6961 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -75,7 +75,6 @@ class CharacterController typedef std::deque > AnimationQueue; AnimationQueue mAnimQueue; - std::string mCurrentGroup; CharacterState mState; bool mSkipAnim; From e4eb4b7e307d47451d310b2f17f7ac56b369bcdb Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 25 Apr 2013 06:29:17 -0700 Subject: [PATCH 98/99] Keep track of the character looping state --- apps/openmw/mwmechanics/character.cpp | 16 +++++++++------- apps/openmw/mwmechanics/character.hpp | 5 +++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index cf323b0f2..682ad834c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -103,13 +103,13 @@ static void getStateInfo(CharacterState state, std::string *group) CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop) - : mPtr(ptr), mAnimation(anim), mState(state), mSkipAnim(false), mMovingAnim(false) + : mPtr(ptr), mAnimation(anim), mCharState(state), mSkipAnim(false), mMovingAnim(false) { if(!mAnimation) return; std::string group; - getStateInfo(mState, &group); + getStateInfo(mCharState, &group); if(MWWorld::Class::get(mPtr).isActor()) { /* Accumulate along X/Y only for now, until we can figure out how we should @@ -249,7 +249,8 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int if(mode != 0 || getState() != CharState_SpecialIdle) { mAnimQueue.clear(); - mState = CharState_SpecialIdle; + mCharState = CharState_SpecialIdle; + mLooping = false; mMovingAnim = mAnimation->play(groupname, ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1); } else if(mode == 0) @@ -268,18 +269,19 @@ void CharacterController::skipAnim() void CharacterController::setState(CharacterState state, bool loop) { - if(mState == state) + if(mCharState == state) return; - mState = state; + mCharState = state; + mLooping = loop; if(!mAnimation) return; mAnimQueue.clear(); std::string anim; - getStateInfo(mState, &anim); + getStateInfo(mCharState, &anim); if((mMovingAnim=mAnimation->hasAnimation(anim)) != false) - mMovingAnim = mAnimation->play(anim, "start", "stop", 0.0f, loop ? (~(size_t)0) : 0); + mMovingAnim = mAnimation->play(anim, "start", "stop", 0.0f, mLooping ? (~(size_t)0) : 0); } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index d102e6961..bfaf33be5 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -75,7 +75,8 @@ class CharacterController typedef std::deque > AnimationQueue; AnimationQueue mAnimQueue; - CharacterState mState; + CharacterState mCharState; + bool mLooping; bool mSkipAnim; bool mMovingAnim; @@ -93,7 +94,7 @@ public: void setState(CharacterState state, bool loop); CharacterState getState() const - { return mState; } + { return mCharState; } }; } From 274f3c7b778be536b81e2bfd67ded22c8508e339 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 25 Apr 2013 07:08:11 -0700 Subject: [PATCH 99/99] Force character state to update after switching view modes --- apps/openmw/mwbase/mechanicsmanager.hpp | 3 +++ apps/openmw/mwmechanics/actors.cpp | 7 ++++++ apps/openmw/mwmechanics/actors.hpp | 2 ++ apps/openmw/mwmechanics/character.cpp | 5 ++++ apps/openmw/mwmechanics/character.hpp | 2 ++ .../mwmechanics/mechanicsmanagerimp.cpp | 6 +++++ .../mwmechanics/mechanicsmanagerimp.hpp | 2 ++ apps/openmw/mwrender/npcanimation.cpp | 25 ++++++++++++++++--- 8 files changed, 48 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 020647744..38794269b 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -99,6 +99,9 @@ namespace MWBase float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0; ///< Perform a persuasion action on NPC + virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0; + ///< Forces an object to refresh its animation state. + virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0; ///< Run animation for a MW-reference. Calls to this function for references that are currently not /// in the scene should be ignored. diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 82e46ea46..a20bf6d1f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -290,6 +290,13 @@ namespace MWMechanics return 0; } + void Actors::forceStateUpdate(const MWWorld::Ptr & ptr) + { + PtrControllerMap::iterator iter = mActors.find(ptr); + if(iter != mActors.end()) + iter->second.forceStateUpdate(); + } + void Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) { PtrControllerMap::iterator iter = mActors.find(ptr); diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index aabd86dc7..c01d63093 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -78,6 +78,8 @@ namespace MWMechanics int countDeaths (const std::string& id) const; ///< Return the number of deaths for actors with the given ID. + void forceStateUpdate(const MWWorld::Ptr &ptr); + void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); void skipAnimation(const MWWorld::Ptr& ptr); }; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 682ad834c..86a266033 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -274,6 +274,11 @@ void CharacterController::setState(CharacterState state, bool loop) mCharState = state; mLooping = loop; + forceStateUpdate(); +} + +void CharacterController::forceStateUpdate() +{ if(!mAnimation) return; mAnimQueue.clear(); diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index bfaf33be5..7b26d6b34 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -95,6 +95,8 @@ public: void setState(CharacterState state, bool loop); CharacterState getState() const { return mCharState; } + + void forceStateUpdate(); }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 66d20e662..969a233bc 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -654,6 +654,12 @@ namespace MWMechanics } } + void MechanicsManager::forceStateUpdate(const MWWorld::Ptr &ptr) + { + if(MWWorld::Class::get(ptr).isActor()) + mActors.forceStateUpdate(ptr); + } + void MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) { if(MWWorld::Class::get(ptr).isActor()) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 4b8d42cd3..f3a38bf36 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -96,6 +96,8 @@ namespace MWMechanics void toLower(std::string npcFaction); ///< Perform a persuasion action on NPC + virtual void forceStateUpdate(const MWWorld::Ptr &ptr); + virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); virtual void skipAnimation(const MWWorld::Ptr& ptr); }; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 563bca4c7..9403c7dd3 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -11,6 +11,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "renderconst.hpp" @@ -97,10 +98,25 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); addObjectList(node, smodel, true); - if(viewMode != VM_HeadOnly) - setViewMode(viewMode); - else - forceUpdate(); + if(mBodyPrefix.find("argonian") != std::string::npos) + addObjectList(node, "meshes\\argonian_swimkna.nif", true); + else if(!mNpc->isMale() && !isBeast) + addObjectList(node, "meshes\\base_anim_female.nif", true); + if(mNpc->mModel.length() > 0) + addObjectList(node, "meshes\\"+mNpc->mModel, true); + if(mViewMode == VM_FirstPerson) + { + /* A bit counter-intuitive, but unlike third-person anims, it seems + * beast races get both base_anim.1st.nif and base_animkna.1st.nif. + */ + addObjectList(node, "meshes\\base_anim.1st.nif", true); + if(isBeast) + addObjectList(node, "meshes\\base_animkna.1st.nif", true); + if(!mNpc->isMale() && !isBeast) + addObjectList(node, "meshes\\base_anim_female.1st.nif", true); + } + + forceUpdate(); } void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) @@ -132,6 +148,7 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) if(!mNpc->isMale() && !isBeast) addObjectList(node, "meshes\\base_anim_female.1st.nif", true); } + MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr); for(size_t i = 0;i < sPartListSize;i++) removeIndividualPart(i);