diff --git a/source/glest_game/game/game.cpp b/source/glest_game/game/game.cpp index a0fc693c..39acbd64 100644 --- a/source/glest_game/game/game.cpp +++ b/source/glest_game/game/game.cpp @@ -5251,6 +5251,9 @@ void Game::render3d(){ if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] renderFps = %d took msecs: %lld [renderUnits]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,renderFps,chrono.getMillis()); if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) chrono.start(); + //renderOnTopBars (aka Healthbars) + renderer.renderOnTopBars(); + //particles renderer.renderParticleManager(rsGame); if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] renderFps = %d took msecs: %lld [renderParticleManager]\n",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,renderFps,chrono.getMillis()); diff --git a/source/glest_game/graphics/renderer.cpp b/source/glest_game/graphics/renderer.cpp index 8638650e..fea57fc7 100644 --- a/source/glest_game/graphics/renderer.cpp +++ b/source/glest_game/graphics/renderer.cpp @@ -5610,6 +5610,77 @@ void Renderer::renderSelectionEffects() { glPopAttrib(); } +void Renderer::renderOnTopBars(){ + if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) { + return; + } + + Config &config= Config::getInstance(); + if(config.getBool("RecordMode","false") == true) { + return; + } + + glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT); + glDisable(GL_LIGHTING); + glDisable(GL_TEXTURE_2D); + glDepthFunc(GL_ALWAYS); + glDisable(GL_STENCIL_TEST); + glDisable(GL_CULL_FACE); + glEnable(GL_BLEND); + glLineWidth(2.f); + + VisibleQuadContainerCache &qCache = getQuadCache(); + if(qCache.visibleQuadUnitList.empty() == false) { + for(int visibleUnitIndex = 0; + visibleUnitIndex < (int)qCache.visibleQuadUnitList.size(); ++visibleUnitIndex) { + Unit *unit = qCache.visibleQuadUnitList[visibleUnitIndex]; + + float healthbarheight; + float healthbarthickness; + int healthbarVisible; + + //get settings of the faction + healthbarheight=unit->getFaction()->getType()->getHealthbarHeight(); + healthbarthickness=unit->getFaction()->getType()->getHealthbarThickness(); + healthbarVisible=unit->getFaction()->getType()->getHealthbarVisible(); + + //replace them by the ones from the unit if existent + if(unit->getType()->getHealthbarVisible()!=hbvOff && unit->getType()->getHealthbarVisible()!=hbvUndefined) { + if(unit->getType()->getHealthbarHeight()!=-100.0f) { + healthbarheight=unit->getType()->getHealthbarHeight(); + } + if(unit->getType()->getHealthbarThickness()!=-1.0f) { + healthbarthickness=unit->getType()->getHealthbarThickness(); + } + healthbarVisible=unit->getType()->getHealthbarVisible(); + } + + if(unit->isAlive() && !(healthbarVisible==hbvUndefined || (healthbarVisible&hbvOff)) + && ((healthbarVisible&hbvAlways) + || ((healthbarVisible&hbvDamaged) && unit->getHp()!=unit->getType()->getMaxHp()) + || ((healthbarVisible&hbvSelected) && game->getGui()->isSelected(unit)))) { + Vec3f currVec= unit->getCurrVectorFlat(); + if(healthbarheight==-100.0f) { + currVec.y+=unit->getType()->getHeight()+1; + } else { + currVec.y+=healthbarheight; + } + + if(unit->getType()->getMaxEp() > 0) { + healthbarthickness=healthbarthickness*2; + } + + if(unit->getType()->getMaxEp() > 0) { + renderSelectionHpBar(currVec,unit->getType()->getSize(),unit->getHpRatio(),healthbarthickness,unit->getEpRatio()); + } else { + renderSelectionHpBar(currVec,unit->getType()->getSize(),unit->getHpRatio(),healthbarthickness); + } + } + } + } + glPopAttrib(); +} + void Renderer::renderWaterEffects(){ if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) { return; @@ -8215,6 +8286,72 @@ void Renderer::enableProjectiveTexturing() { } // ==================== private aux drawing ==================== +void Renderer::renderSelectionHpBar(Vec3f v, int size, float hp, float height, float ep) { + Vec3f rightVector; + Vec3f upVector; + v.y+=1; + float modelview[16]; + float width=(float)size/6+0.25f; + float red; + float green; + float brightness=0.7f; + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + // get the current modelview state + glGetFloatv(GL_MODELVIEW_MATRIX , modelview); + rightVector= Vec3f(modelview[0], modelview[4], modelview[8]); + upVector= Vec3f(modelview[1], modelview[5], modelview[9]); + + hp=hp*2-1; + ep=ep*2-1; + + //from green to yellow to red + if(hp >= 0.0f) { + green=brightness; + red=brightness-hp*brightness; + } else { + red=brightness; + green=brightness+hp*brightness; + } + + glColor4f(red,green,0.0f,0.4f); + glBegin(GL_QUADS); + if(ep < -2.0f) { + //hpbar + glVertex3fv((v - (rightVector*width - upVector*height)).ptr()); + glVertex3fv((v - (rightVector*width + upVector*height)).ptr()); + glVertex3fv((v + (rightVector*hp*width - upVector*height)).ptr()); + glVertex3fv((v + (rightVector*hp*width + upVector*height)).ptr()); + + } else { + //hpbar + glVertex3fv((v - (rightVector*width - upVector*height)).ptr()); + glVertex3fv((v - (rightVector*width + upVector*height*0.0f)).ptr()); + glVertex3fv((v + (rightVector*hp*width - upVector*height*0.0f)).ptr()); + glVertex3fv((v + (rightVector*hp*width + upVector*height)).ptr()); + //epbar + glColor4f(brightness,0,brightness,0.4f); + glVertex3fv((v - (rightVector*width + upVector*height*0.0f)).ptr()); + glVertex3fv((v - (rightVector*width + upVector*height)).ptr()); + glVertex3fv((v + (rightVector*ep*width - upVector*height)).ptr()); + glVertex3fv((v + (rightVector*ep*width - upVector*height*0.0f)).ptr()); + } + glEnd(); + + //border + glColor4f(red+0.1f,green+0.1f,0.1f,0.5f); + glBegin(GL_LINE_LOOP); + glVertex3fv((v - (rightVector*width - upVector*height)).ptr()); + glVertex3fv((v - (rightVector*width + upVector*height)).ptr()); + glVertex3fv((v + (rightVector*width - upVector*height)).ptr()); + glVertex3fv((v + (rightVector*width + upVector*height)).ptr()); + glEnd(); + + + glPopMatrix(); +} void Renderer::renderSelectionCircle(Vec3f v, int size, float radius, float thickness) { if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) { diff --git a/source/glest_game/graphics/renderer.h b/source/glest_game/graphics/renderer.h index ad7223b7..fa15818b 100644 --- a/source/glest_game/graphics/renderer.h +++ b/source/glest_game/graphics/renderer.h @@ -549,6 +549,7 @@ public: void renderUnitsToBuild(const int renderFps); void renderSelectionEffects(); + void renderOnTopBars(); void renderWaterEffects(); void renderHud(); void renderMinimap(); @@ -680,6 +681,7 @@ private: //private aux drawing void renderSelectionCircle(Vec3f v, int size, float radius, float thickness=0.2f); + void renderSelectionHpBar(Vec3f v, int size, float hp, float height, float ep=-1.0f); void renderTeamColorEffect(Vec3f &v, int heigth, int size, Vec3f color, const Texture2D *texture); void renderArrow(const Vec3f &pos1, const Vec3f &pos2, const Vec3f &color, float width); void renderTile(const Vec2i &pos); diff --git a/source/glest_game/types/faction_type.cpp b/source/glest_game/types/faction_type.cpp index bd8ae23f..1c335ef7 100644 --- a/source/glest_game/types/faction_type.cpp +++ b/source/glest_game/types/faction_type.cpp @@ -34,6 +34,9 @@ FactionType::FactionType() { music = NULL; personalityType = fpt_Normal; isLinked = false; + healthbarheight= -100.0f; + healthbarthickness= 0.05f; + healthbarVisible=hbvUndefined; } //load a faction, given a directory @@ -272,6 +275,35 @@ void FactionType::load(const string &factionName, const TechTree *techTree, Chec loadedFileList[musicNode->getAttribute("path")->getRestrictedValue(currentPath)].push_back(make_pair(path,musicNode->getAttribute("path")->getRestrictedValue())); } + //healthbar + if(factionNode->hasChild("healthbar")) { + const XmlNode *HealthbarNode= factionNode->getChild("healthbar"); + if(HealthbarNode->hasChild("height")) { + healthbarheight= HealthbarNode->getChild("height")->getAttribute("value")->getFloatValue(); + } + if(HealthbarNode->hasChild("thickness")) { + healthbarthickness= HealthbarNode->getChild("thickness")->getAttribute("value")->getFloatValue(0.f, 1.f); + } + if(HealthbarNode->hasChild("visible")) { + string healthbarVisibleString=HealthbarNode->getChild("visible")->getAttribute("value")->getValue(); + vector v=split(healthbarVisibleString,"|"); + for (int i = 0; i < (int)v.size(); ++i) { + string current=trim(v[i]); + if(current=="always") { + healthbarVisible=healthbarVisible|hbvAlways; + } else if(current=="selected") { + healthbarVisible=healthbarVisible|hbvSelected; + } else if(current=="damaged") { + healthbarVisible=healthbarVisible|hbvDamaged; + } else if(current=="off") { + healthbarVisible=healthbarVisible|hbvOff; + } else { + throw megaglest_runtime_error("Unknown Healthbar Visible Option: " + current, validationMode); + } + } + } + } + //read ai behavior if(factionNode->hasChild("ai-behavior") == true) { const XmlNode *aiNode= factionNode->getChild("ai-behavior"); diff --git a/source/glest_game/types/faction_type.h b/source/glest_game/types/faction_type.h index 47641598..909e9571 100644 --- a/source/glest_game/types/faction_type.h +++ b/source/glest_game/types/faction_type.h @@ -95,6 +95,10 @@ private: std::map mapAIBehaviorStaticOverrideValues; bool isLinked; + float healthbarheight; + float healthbarthickness; + int healthbarVisible; + public: //init @@ -120,6 +124,10 @@ public: int getStartingUnitCount() const {return (int)startingUnits.size();} const UnitType *getStartingUnit(int i) const {return startingUnits[i].first;} int getStartingUnitAmount(int i) const {return startingUnits[i].second;} + inline float getHealthbarHeight() const {return healthbarheight;} + inline float getHealthbarThickness() const {return healthbarthickness;} + inline int getHealthbarVisible() const {return healthbarVisible;} + const UnitType *getUnitType(const string &name) const; const UnitType *getUnitTypeById(int id) const; diff --git a/source/glest_game/types/unit_type.cpp b/source/glest_game/types/unit_type.cpp index 82129a6a..74c3cab6 100644 --- a/source/glest_game/types/unit_type.cpp +++ b/source/glest_game/types/unit_type.cpp @@ -88,6 +88,9 @@ UnitType::UnitType() : ProducibleType() { meetingPointImage = NULL; lightColor= Vec3f(0.f); light= false; + healthbarheight= -100.0f; + healthbarthickness=-1.0f; + healthbarVisible=hbvUndefined; multiSelect= false; commandable= true; armorType= NULL; @@ -446,6 +449,35 @@ void UnitType::loaddd(int id,const string &dir, const TechTree *techTree, } } + //healthbar + if(parametersNode->hasChild("healthbar")) { + const XmlNode *HealthbarNode= parametersNode->getChild("healthbar"); + if(HealthbarNode->hasChild("height")) { + healthbarheight= HealthbarNode->getChild("height")->getAttribute("value")->getFloatValue(); + } + if(HealthbarNode->hasChild("thickness")) { + healthbarthickness= HealthbarNode->getChild("thickness")->getAttribute("value")->getFloatValue(0.f, 1.f); + } + if(HealthbarNode->hasChild("visible")) { + string healthbarVisibleString=HealthbarNode->getChild("visible")->getAttribute("value")->getValue(); + vector v=split(healthbarVisibleString,"|"); + for (int i = 0; i < (int)v.size(); ++i) { + string current=trim(v[i]); + if(current=="always") { + healthbarVisible=healthbarVisible|hbvAlways; + } else if(current=="selected") { + healthbarVisible=healthbarVisible|hbvSelected; + } else if(current=="damaged") { + healthbarVisible=healthbarVisible|hbvDamaged; + } else if(current=="off") { + healthbarVisible=healthbarVisible|hbvOff; + } else { + throw megaglest_runtime_error("Unknown Healthbar Visible Option: " + current, validationMode); + } + } + } + } + //light const XmlNode *lightNode= parametersNode->getChild("light"); light= lightNode->getAttribute("enabled")->getBoolValue(); diff --git a/source/glest_game/types/unit_type.h b/source/glest_game/types/unit_type.h index 2bc37fb0..27e5aa33 100644 --- a/source/glest_game/types/unit_type.h +++ b/source/glest_game/types/unit_type.h @@ -116,6 +116,14 @@ enum UnitClass { typedef vector DamageParticleSystemTypes; +enum HealthbarVisible { + hbvUndefined=0, + hbvOff=1, + hbvAlways=2, + hbvDamaged=4, + hbvSelected=8 +}; + enum UnitCountsInVictoryConditions { ucvcNotSet, ucvcTrue, @@ -170,6 +178,9 @@ private: const ArmorType *armorType; bool light; Vec3f lightColor; + float healthbarheight; + float healthbarthickness; + int healthbarVisible; bool multiSelect; bool commandable; int sight; @@ -256,6 +267,9 @@ public: inline bool getLight() const {return light;} inline bool getRotationAllowed() const {return rotationAllowed;} inline Vec3f getLightColor() const {return lightColor;} + inline float getHealthbarHeight() const {return healthbarheight;} + inline float getHealthbarThickness() const {return healthbarthickness;} + inline int getHealthbarVisible() const {return healthbarVisible;} inline bool getMultiSelect() const {return multiSelect;} inline bool isCommandable() const {return commandable;} inline int getSight() const {return sight;} diff --git a/source/shared_lib/include/util/util.h b/source/shared_lib/include/util/util.h index 1ba171b4..b6306bc2 100644 --- a/source/shared_lib/include/util/util.h +++ b/source/shared_lib/include/util/util.h @@ -13,6 +13,7 @@ #define _SHARED_UTIL_UTIL_H_ #include +#include #include #include #include "thread.h" @@ -226,6 +227,7 @@ string cutLastFile(const string &s); string cutLastExt(const string &s); string ext(const string &s); string replaceBy(const string &s, char c1, char c2); +vector split(string s,string d); string toLower(const string &s); void copyStringToBuffer(char *buffer, int bufferSize, const string& s); diff --git a/source/shared_lib/sources/util/util.cpp b/source/shared_lib/sources/util/util.cpp index e749937f..5142acea 100644 --- a/source/shared_lib/sources/util/util.cpp +++ b/source/shared_lib/sources/util/util.cpp @@ -709,6 +709,22 @@ string replaceBy(const string &s, char c1, char c2){ return rs; } +vector split(string s,string d) { + vector results; + size_t lastOffset = 0; + + while(true) + { + size_t offset = s.find_first_of(d, lastOffset); + results.push_back(s.substr(lastOffset, offset - lastOffset)); + if (offset == string::npos) + break; + else + lastOffset = offset + d.size(); //skip the delimiter + } + return results; +} + string toLower(const string &s){ string rs= s;