commit 0ce9b5fcacf5d72c237b0cf8f161279b9a416055 Author: Titus Tscharntke Date: Fri Jan 22 01:45:58 2010 +0000 initial version ( megaglest 3.2.3-beta3 ) diff --git a/source/configurator/configuration.cpp b/source/configurator/configuration.cpp new file mode 100644 index 00000000..3afd7ae0 --- /dev/null +++ b/source/configurator/configuration.cpp @@ -0,0 +1,391 @@ +#include "configuration.h" + +#include + +#include "xml/xml_parser.h" +#include "util/util.h" +#include "util/properties.h" +#include "util/conversion.h" + +using namespace std; +using namespace Shared::Xml; +using namespace Shared::Util; + +namespace Configurator{ + +// =============================================== +// class Configuration +// =============================================== + +Configuration::~Configuration(){ + for(int i= 0; igetChild("title")->getAttribute("value")->getValue(); + + //fileName + fileName= configurationNode->getChild("file-name")->getAttribute("value")->getValue(); + + //icon + XmlNode *iconNode= configurationNode->getChild("icon"); + icon= iconNode->getAttribute("value")->getBoolValue(); + if(icon){ + iconPath= iconNode->getAttribute("path")->getValue(); + } + + const XmlNode *fieldGroupsNode= configurationNode->getChild("field-groups"); + + fieldGroups.resize(fieldGroupsNode->getChildCount()); + + for(int i=0; igetChild("field-group", i); + FieldGroup *fieldGroup= new FieldGroup(); + fieldGroup->load(fieldGroupNode); + fieldGroups[i]= fieldGroup; + } +} + +void Configuration::loadValues(const string &path){ + Properties properties; + + properties.load(path); + + for(int i=0; igetFieldCount(); ++j){ + Field *f= fg->getField(j); + f->setValue(properties.getString(f->getVariableName())); + } + } +} + +void Configuration::save(){ + Properties properties; + + properties.load(fileName); + + for(int i=0; igetFieldCount(); ++j){ + Field *f= fg->getField(j); + f->updateValue(); + if(!f->isValueValid(f->getValue())){ + f->setValue(f->getDefaultValue()); + f->updateControl(); + } + properties.setString(f->getVariableName(), f->getValue()); + } + } + + properties.save(fileName); +} + +string Field::getInfo() const{ + return name+" (default: " + defaultValue + ")"; +} + +// =============================================== +// class FieldGroup +// =============================================== + +FieldGroup::~FieldGroup(){ + for(int i= 0; igetAttribute("name")->getValue(); + + fields.resize(groupNode->getChildCount()); + for(int i=0; igetChild("field", i); + + Field *f= newField(fieldNode->getAttribute("type")->getValue()); + + //name + const XmlNode *nameNode= fieldNode->getChild("name"); + f->setName(nameNode->getAttribute("value")->getValue()); + + //variableName + const XmlNode *variableNameNode= fieldNode->getChild("variable-name"); + f->setVariableName(variableNameNode->getAttribute("value")->getValue()); + + //description + const XmlNode *descriptionNode= fieldNode->getChild("description"); + f->setDescription(descriptionNode->getAttribute("value")->getValue()); + + //default + const XmlNode *defaultNode= fieldNode->getChild("default"); + f->setDefaultValue(defaultNode->getAttribute("value")->getValue()); + + f->loadSpecific(fieldNode); + + if(!f->isValueValid(f->getDefaultValue())){ + throw runtime_error("Default value not valid in field: " + f->getName()); + } + + fields[i]= f; + } +} + +Field *FieldGroup::newField(const string &type){ + if(type=="Bool"){ + return new BoolField(); + } + else if(type=="Int"){ + return new IntField(); + } + else if(type=="Float"){ + return new FloatField(); + } + else if(type=="String"){ + return new StringField(); + } + else if(type=="Enum"){ + return new EnumField(); + } + else if(type=="IntRange"){ + return new IntRangeField(); + } + else if(type=="FloatRange"){ + return new FloatRangeField(); + } + else{ + throw runtime_error("Unknown field type: " + type); + } +} + +// =============================================== +// class BoolField +// =============================================== + +void BoolField::createControl(wxWindow *parent, wxSizer *sizer){ + checkBox= new wxCheckBox(parent, -1, ""); + checkBox->SetValue(strToBool(value)); + sizer->Add(checkBox); +} + +void BoolField::updateValue(){ + value= boolToStr(checkBox->GetValue()); +} + +void BoolField::updateControl(){ + checkBox->SetValue(strToBool(value)); +} + +bool BoolField::isValueValid(const string &value){ + try{ + strToBool(value); + } + catch(const exception &){ + return false; + } + return true; +} + +// =============================================== +// class IntField +// =============================================== + +void IntField::createControl(wxWindow *parent, wxSizer *sizer){ + textCtrl= new wxTextCtrl(parent, -1, value.c_str()); + sizer->Add(textCtrl); +} + +void IntField::updateValue(){ + value= textCtrl->GetValue(); +} + +void IntField::updateControl(){ + textCtrl->SetValue(value.c_str()); +} + +bool IntField::isValueValid(const string &value){ + try{ + strToInt(value); + } + catch(const exception &){ + return false; + } + return true; +} + +// =============================================== +// class FloatField +// =============================================== + +void FloatField::createControl(wxWindow *parent, wxSizer *sizer){ + textCtrl= new wxTextCtrl(parent, -1, value.c_str()); + sizer->Add(textCtrl); +} + +void FloatField::updateValue(){ + value= textCtrl->GetValue(); +} + +void FloatField::updateControl(){ + textCtrl->SetValue(value.c_str()); +} + +bool FloatField::isValueValid(const string &value){ + try{ + strToFloat(value); + } + catch(const exception &){ + return false; + } + return true; +} + +// =============================================== +// class StringField +// =============================================== + +void StringField::createControl(wxWindow *parent, wxSizer *sizer){ + textCtrl= new wxTextCtrl(parent, -1, value.c_str()); + textCtrl->SetSize(wxSize(3*textCtrl->GetSize().x/2, textCtrl->GetSize().y)); + sizer->Add(textCtrl); +} + +void StringField::updateValue(){ + value= textCtrl->GetValue(); +} + +void StringField::updateControl(){ + textCtrl->SetValue(value.c_str()); +} + +bool StringField::isValueValid(const string &value){ + return true; +} + +// =============================================== +// class EnumField +// =============================================== + +void EnumField::createControl(wxWindow *parent, wxSizer *sizer){ + comboBox= new wxComboBox(parent, -1, "", wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + for(int i=0; iAppend(enumerants[i].c_str()); + } + comboBox->SetValue(value.c_str()); + sizer->Add(comboBox); +} + +void EnumField::updateValue(){ + value= comboBox->GetValue(); +} + +void EnumField::updateControl(){ + comboBox->SetValue(value.c_str()); +} + +bool EnumField::isValueValid(const string &value){ + return true; +} + +void EnumField::loadSpecific(const XmlNode *fieldNode){ + const XmlNode *enumsNode= fieldNode->getChild("enums"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *enumNode= enumsNode->getChild("enum", i); + enumerants.push_back(enumNode->getAttribute("value")->getValue()); + } +}; + +// =============================================== +// class IntRange +// =============================================== + +void IntRangeField::createControl(wxWindow *parent, wxSizer *sizer){ + slider= new wxSlider(parent, -1, strToInt(value), min, max, wxDefaultPosition, wxDefaultSize, wxSL_LABELS); + sizer->Add(slider); +} + +void IntRangeField::updateValue(){ + value= intToStr(slider->GetValue()); +} + +void IntRangeField::updateControl(){ + slider->SetValue(strToInt(value)); +} + +bool IntRangeField::isValueValid(const string &value){ + try{ + strToInt(value); + } + catch(const exception &){ + return false; + } + return true; +} + +void IntRangeField::loadSpecific(const XmlNode *fieldNode){ + const XmlNode *minNode= fieldNode->getChild("min"); + min= strToInt(minNode->getAttribute("value")->getValue()); + + const XmlNode *maxNode= fieldNode->getChild("max"); + max= strToInt(maxNode->getAttribute("value")->getValue()); +} + +string IntRangeField::getInfo() const{ + return name + " (min: " + intToStr(min)+ ", max: " + intToStr(max) + ", default: " + defaultValue + ")"; +} + +// =============================================== +// class FloatRangeField +// =============================================== + +void FloatRangeField::createControl(wxWindow *parent, wxSizer *sizer){ + textCtrl= new wxTextCtrl(parent, -1, value.c_str()); + sizer->Add(textCtrl); +} + +void FloatRangeField::updateValue(){ + value= textCtrl->GetValue(); +} + +void FloatRangeField::updateControl(){ + textCtrl->SetValue(value.c_str()); +} + +bool FloatRangeField::isValueValid(const string &value){ + try{ + float f= strToFloat(value); + return f>=min && f<=max; + } + catch(const exception &){ + return false; + } + return true; +} + +void FloatRangeField::loadSpecific(const XmlNode *fieldNode){ + const XmlNode *minNode= fieldNode->getChild("min"); + min= strToFloat(minNode->getAttribute("value")->getValue()); + + const XmlNode *maxNode= fieldNode->getChild("max"); + max= strToFloat(maxNode->getAttribute("value")->getValue()); +}; + +string FloatRangeField::getInfo() const{ + return name + " (min: " + floatToStr(min)+ ", max: " + floatToStr(max) + ", default: " + defaultValue + ")"; +} + +}//end namespace \ No newline at end of file diff --git a/source/configurator/configuration.h b/source/configurator/configuration.h new file mode 100644 index 00000000..5442ce99 --- /dev/null +++ b/source/configurator/configuration.h @@ -0,0 +1,237 @@ +#ifndef _CONFIGURATOR_CONFIGURATION_H_ +#define _CONFIGURATOR_CONFIGURATION_H_ + +#include +#include + +#include + +#include "xml/xml_parser.h" + +using std::string; +using std::vector; + +using Shared::Xml::XmlNode; + +namespace Configurator{ + +class Field; +class FieldGroup; + +// =============================== +// class Configuration +// =============================== + +class Configuration{ +public: + typedef vector FieldGroups; + +private: + string title; + string fileName; + bool icon; + string iconPath; + FieldGroups fieldGroups; + +public: + ~Configuration(); + + void load(const string &path); + + void loadStructure(const string &path); + void loadValues(const string &path); + + void save(); + + const string &getTitle() const {return title;} + const string &getFileName() const {return fileName;} + bool getIcon() const {return icon;} + const string &getIconPath() const {return iconPath;} + + int getFieldGroupCount() const {return fieldGroups.size();} + FieldGroup *getFieldGroup(int i) const {return fieldGroups[i];} +}; + +// =============================== +// class FieldGroup +// =============================== + +class FieldGroup{ +public: + typedef vector Fields; + +private: + Fields fields; + string name; + +public: + ~FieldGroup(); + + int getFieldCount() const {return fields.size();} + Field *getField(int i) const {return fields[i];} + const string &getName() const {return name;} + + void load(const XmlNode *groupNode); + +private: + Field *newField(const string &type); +}; + +// =============================== +// class Field +// =============================== + +class Field{ +protected: + string name; + string variableName; + string description; + string value; + string defaultValue; + +public: + virtual ~Field()= 0{} + + const string &getName() const {return name;} + const string &getVariableName() const {return variableName;} + const string &getDescription() const {return description;} + const string &getValue() const {return value;} + const string &getDefaultValue() const {return defaultValue;} + + void setName(const string &name) {this->name= name;} + void setVariableName(const string &variableName) {this->variableName= variableName;} + void setDescription(const string &description) {this->description= description;} + void setValue(const string &value) {this->value= value;} + void setDefaultValue(const string &defaultValue) {this->defaultValue= defaultValue;} + + virtual void loadSpecific(const XmlNode *fieldNode){}; + virtual string getInfo() const; + + virtual void createControl(wxWindow *parent, wxSizer *sizer)= 0; + virtual void updateValue()= 0; + virtual void updateControl()= 0; + virtual bool isValueValid(const string &value)= 0; +}; + +// =============================== +// class BoolField +// =============================== + +class BoolField: public Field{ +private: + wxCheckBox *checkBox; + +public: + virtual void createControl(wxWindow *parent, wxSizer *sizer); + virtual void updateValue(); + virtual void updateControl(); + virtual bool isValueValid(const string &value); +}; + +// =============================== +// class IntField +// =============================== + +class IntField: public Field{ +private: + wxTextCtrl *textCtrl; + +public: + virtual void createControl(wxWindow *parent, wxSizer *sizer); + virtual void updateValue(); + virtual void updateControl(); + virtual bool isValueValid(const string &value); +}; + +// =============================== +// class FloatField +// =============================== + +class FloatField: public Field{ +private: + wxTextCtrl *textCtrl; + +public: + virtual void createControl(wxWindow *parent, wxSizer *sizer); + virtual void updateValue(); + virtual void updateControl(); + virtual bool isValueValid(const string &value); +}; + +// =============================== +// class StringField +// =============================== + +class StringField: public Field{ +private: + wxTextCtrl *textCtrl; + +public: + virtual void createControl(wxWindow *parent, wxSizer *sizer); + virtual void updateValue(); + virtual void updateControl(); + virtual bool isValueValid(const string &value); +}; + +// =============================== +// class EnumField +// =============================== + +class EnumField: public Field{ +private: + wxComboBox *comboBox; + vector enumerants; + +public: + virtual void createControl(wxWindow *parent, wxSizer *sizer); + virtual void updateValue(); + virtual void updateControl(); + virtual bool isValueValid(const string &value); + + virtual void loadSpecific(const XmlNode *fieldNode); +}; + +// =============================== +// class IntRangeField +// =============================== + +class IntRangeField: public Field{ +private: + wxSlider *slider; + int min; + int max; + +public: + virtual void createControl(wxWindow *parent, wxSizer *sizer); + virtual void updateValue(); + virtual void updateControl(); + virtual bool isValueValid(const string &value); + + virtual void loadSpecific(const XmlNode *fieldNode); + virtual string getInfo() const; +}; + +// =============================== +// class FloatRangeField +// =============================== + +class FloatRangeField: public Field{ +private: + wxTextCtrl *textCtrl; + float min; + float max; + +public: + virtual void createControl(wxWindow *parent, wxSizer *sizer); + virtual void updateValue(); + virtual void updateControl(); + virtual bool isValueValid(const string &value); + + virtual void loadSpecific(const XmlNode *fieldNode); + virtual string getInfo() const; +}; + + +}//end namespace + +#endif diff --git a/source/configurator/main.cpp b/source/configurator/main.cpp new file mode 100644 index 00000000..3bfc8e81 --- /dev/null +++ b/source/configurator/main.cpp @@ -0,0 +1,197 @@ +#include "main.h" + +#include + +#include +#include +#include +#include +#include + +using namespace std; + +namespace Configurator{ + +// =============================================== +// class MainWindow +// =============================================== + + +const int MainWindow::margin= 10; +const int MainWindow::panelMargin= 20; +const int MainWindow::gridMarginHorizontal= 30; + +MainWindow::MainWindow(){ + + SetExtraStyle(wxFRAME_EX_CONTEXTHELP); + + configuration.load("configuration.xml"); + + Create(NULL, -1, "", wxDefaultPosition, wxDefaultSize, wxCAPTION | wxSYSTEM_MENU); + + SetTitle(("Configurator - " + configuration.getTitle() + " - Editing " + configuration.getFileName()).c_str()); + + if(configuration.getIcon()){ + wxIcon icon; + icon.LoadFile(configuration.getIconPath().c_str(), wxBITMAP_TYPE_ICO); + SetIcon(icon); + } + + notebook= new wxNotebook(this, -1); + + wxSizer *mainSizer= new wxBoxSizer(wxVERTICAL); + wxSizer *topSizer= new wxBoxSizer(wxHORIZONTAL); + topSizer->Add(notebook, 0, wxALL, 0); + mainSizer->Add(topSizer, 0, wxALL, margin); + + for(int i=0; iAddPage(panel, fg->getName().c_str()); + + //sizers + wxSizer *gridSizer= new wxFlexGridSizer(2, margin, gridMarginHorizontal); + wxSizer *panelSizer= new wxBoxSizer(wxVERTICAL); + panelSizer->Add(gridSizer, 0, wxALL, panelMargin); + panel->SetSizer(panelSizer); + + for(int j=0; jgetFieldCount(); ++j){ + Field *f= fg->getField(j); + FieldText *staticText= new FieldText(panel, this, f); + staticText->SetAutoLayout(true); + gridSizer->Add(staticText); + f->createControl(panel, gridSizer); + idMap.insert(IdPair(staticText->GetId(), staticText)); + } + } + + //buttons + wxSizer *bottomSizer= new wxBoxSizer(wxHORIZONTAL); + + buttonOk= new wxButton(this, biOk, "OK"); + buttonApply= new wxButton(this, biApply, "Apply"); + buttonCancel= new wxButton(this, biCancel, "Cancel"); + buttonDefault= new wxButton(this, biDefault, "Default"); + bottomSizer->Add(buttonOk, 0, wxALL, margin); + bottomSizer->Add(buttonApply, 0, wxRIGHT | wxDOWN | wxUP, margin); + bottomSizer->Add(buttonCancel, 0, wxRIGHT | wxDOWN | wxUP, margin); + bottomSizer->Add(buttonDefault, 0, wxRIGHT | wxDOWN | wxUP, margin); + + infoText= new wxTextCtrl(this, -1, "Info text.", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY); + infoText->SetSize(infoText->GetSize().x, 2*infoText->GetSize().y/3); + infoText->SetBackgroundColour(buttonOk->GetBackgroundColour()); + + mainSizer->Add(infoText, 1, wxGROW | wxALL | wxALIGN_CENTER, margin); + mainSizer->Add(bottomSizer, 0, wxALIGN_CENTER); + + SetBackgroundColour(buttonOk->GetBackgroundColour()); + + SetSizerAndFit(mainSizer); + + Refresh(); +} + +void MainWindow::onButtonOk(wxCommandEvent &event){ + configuration.save(); + Close(); +} + +void MainWindow::onButtonApply(wxCommandEvent &event){ + configuration.save(); +} + +void MainWindow::onButtonCancel(wxCommandEvent &event){ + Close(); +} + +void MainWindow::onButtonDefault(wxCommandEvent &event){ + for(int i=0; igetFieldCount(); ++j){ + Field *f= fg->getField(j); + f->setValue(f->getDefaultValue()); + f->updateControl(); + } + } +} + +void MainWindow::onClose(wxCloseEvent &event){ + Destroy(); +} + +void MainWindow::onMouseDown(wxMouseEvent &event){ + setInfoText(""); +} + +void MainWindow::setInfoText(const string &str){ + infoText->SetValue(str.c_str()); +} + +BEGIN_EVENT_TABLE(MainWindow, wxFrame) + EVT_BUTTON(biOk, MainWindow::onButtonOk) + EVT_BUTTON(biApply, MainWindow::onButtonApply) + EVT_BUTTON(biCancel, MainWindow::onButtonCancel) + EVT_BUTTON(biDefault, MainWindow::onButtonDefault) + EVT_CLOSE(MainWindow::onClose) + EVT_LEFT_DOWN(MainWindow::onMouseDown) +END_EVENT_TABLE() + +// =============================================== +// class FieldText +// =============================================== + +FieldText::FieldText(wxWindow *parent, MainWindow *mainWindow, const Field *field): + wxStaticText(parent, -1, field->getName().c_str()) +{ + this->mainWindow= mainWindow; + this->field= field; +} + +void FieldText::onHelp(wxHelpEvent &event){ + string str= field->getInfo()+"."; + if(!field->getDescription().empty()){ + str+= "\n"+field->getDescription()+"."; + } + mainWindow->setInfoText(str); +} + + +BEGIN_EVENT_TABLE(FieldText, wxStaticText) + EVT_HELP(-1, FieldText::onHelp) +END_EVENT_TABLE() + +// =============================================== +// class App +// =============================================== + +bool App::OnInit(){ + try{ + mainWindow= new MainWindow(); + mainWindow->Show(); + } + catch(const exception &e){ + wxMessageDialog(NULL, e.what(), "Exception", wxOK | wxICON_ERROR).ShowModal(); + return 0; + } + return true; +} + +int App::MainLoop(){ + try{ + return wxApp::MainLoop(); + } + catch(const exception &e){ + wxMessageDialog(NULL, e.what(), "Exception", wxOK | wxICON_ERROR).ShowModal(); + return 0; + } +} + +int App::OnExit(){ + return 0; +} + +}//end namespace + +IMPLEMENT_APP(Configurator::App) \ No newline at end of file diff --git a/source/configurator/main.h b/source/configurator/main.h new file mode 100644 index 00000000..eb1665aa --- /dev/null +++ b/source/configurator/main.h @@ -0,0 +1,101 @@ +#ifndef _CONFIGURATOR_MAIN_H_ +#define _CONFIGURATOR_MAIN_H_ + +#include + +#include +#include +#include +#include + +#include "configuration.h" + +using std::pair; +using std::map; + +namespace Configurator{ + +// =============================== +// class MainWindow +// =============================== + +class MainWindow: public wxFrame{ +private: + DECLARE_EVENT_TABLE() + +private: + typedef pair IdPair; + typedef map IdMap; + +private: + static const int margin; + static const int panelMargin; + static const int gridMarginHorizontal; + + enum ButtonId{ + biOk, + biApply, + biCancel, + biDefault + }; + +private: + IdMap idMap; + Configuration configuration; + wxButton *buttonOk; + wxButton *buttonApply; + wxButton *buttonCancel; + wxButton *buttonDefault; + wxNotebook *notebook; + wxTextCtrl *infoText; + +public: + MainWindow(); + + void onButtonOk(wxCommandEvent &event); + void onButtonApply(wxCommandEvent &event); + void onButtonCancel(wxCommandEvent &event); + void onButtonDefault(wxCommandEvent &event); + void onClose(wxCloseEvent &event); + void onMouseDown(wxMouseEvent &event); + + void setInfoText(const string &str); +}; + +// =============================== +// class FieldText +// =============================== + +class FieldText: public wxStaticText{ +private: + MainWindow *mainWindow; + const Field *field; + +private: + DECLARE_EVENT_TABLE() + +public: + FieldText(wxWindow *parentm, MainWindow *mainWindow, const Field *field); + + void onHelp(wxHelpEvent &event); +}; + +// =============================== +// class App +// =============================== + +class App: public wxApp{ +private: + MainWindow *mainWindow; + +public: + virtual bool OnInit(); + virtual int MainLoop(); + virtual int OnExit(); +}; + +}//end namespace + +DECLARE_APP(Configurator::App) + +#endif \ No newline at end of file diff --git a/source/g3d_viewer/main.cpp b/source/g3d_viewer/main.cpp new file mode 100644 index 00000000..5a47ce3a --- /dev/null +++ b/source/g3d_viewer/main.cpp @@ -0,0 +1,304 @@ +#include "main.h" + +#include + +#include "graphics_factory_basic_gl.h" +#include "graphics_interface.h" +#include "util.h" + +using namespace Shared::Platform; +using namespace Shared::Graphics; +using namespace Shared::Graphics::Gl; +using namespace Shared::Util; + +using namespace std; + +namespace Shared{ namespace G3dViewer{ + +// =============================================== +// class MainWindow +// =============================================== + +const string MainWindow::versionString= "v1.3.4"; +const string MainWindow::winHeader= "G3D viewer " + versionString + " - Built: " + __DATE__; + +MainWindow::MainWindow(const string &modelPath): + wxFrame( + NULL, -1, winHeader.c_str(), + wxPoint(Renderer::windowX, Renderer::windowY), + wxSize(Renderer::windowW, Renderer::windowH)) +{ + renderer= Renderer::getInstance(); + this->modelPath= modelPath; + model= NULL; + playerColor= Renderer::pcRed; + + speed= 0.025f; + + glCanvas = new GlCanvas(this); + + glCanvas->SetCurrent(); + + renderer->init(); + + + menu= new wxMenuBar(); + + //menu + menuFile= new wxMenu(); + menuFile->Append(miFileLoad, "Load"); + menu->Append(menuFile, "File"); + + //mode + menuMode= new wxMenu(); + menuMode->AppendCheckItem(miModeNormals, "Normals"); + menuMode->AppendCheckItem(miModeWireframe, "Wireframe"); + menuMode->AppendCheckItem(miModeGrid, "Grid"); + menu->Append(menuMode, "Mode"); + + //mode + menuSpeed= new wxMenu(); + menuSpeed->Append(miSpeedSlower, "Slower"); + menuSpeed->Append(miSpeedFaster, "Faster"); + menu->Append(menuSpeed, "Speed"); + + //custom color + menuCustomColor= new wxMenu(); + menuCustomColor->AppendCheckItem(miColorRed, "Red"); + menuCustomColor->AppendCheckItem(miColorBlue, "Blue"); + menuCustomColor->AppendCheckItem(miColorYellow, "Yellow"); + menuCustomColor->AppendCheckItem(miColorGreen, "Green"); + menu->Append(menuCustomColor, "Custom Color"); + + menuMode->Check(miModeGrid, true); + menuCustomColor->Check(miColorRed, true); + + SetMenuBar(menu); + + //misc + model= NULL; + rotX= 0.0f; + rotY= 0.0f; + zoom= 1.0f; + lastX= 0; + lastY= 0; + anim= 0.0f; + + CreateStatusBar(); + + timer = new wxTimer(this); + timer->Start(40); + + if(!modelPath.empty()){ + Model *tmpModel= new ModelGl(); + renderer->loadTheModel(tmpModel, modelPath); + model= tmpModel; + GetStatusBar()->SetStatusText(getModelInfo().c_str()); + } +} + +MainWindow::~MainWindow(){ + delete renderer; + delete model; + delete timer; + delete glCanvas; +} + +void MainWindow::onPaint(wxPaintEvent &event){ + renderer->reset(GetClientSize().x, GetClientSize().y, playerColor); + renderer->transform(rotX, rotY, zoom); + renderer->renderGrid(); + + renderer->renderTheModel(model, anim); + glCanvas->SwapBuffers(); +} + +void MainWindow::onClose(wxCloseEvent &event){ + delete this; +} + +void MainWindow::onMouseMove(wxMouseEvent &event){ + + int x= event.GetX(); + int y= event.GetY(); + wxPaintEvent paintEvent; + + if(event.LeftIsDown()){ + rotX+= clamp(lastX-x, -10, 10); + rotY+= clamp(lastY-y, -10, 10); + onPaint(paintEvent); + } + else if(event.RightIsDown()){ + zoom*= 1.0f+(lastX-x+lastY-y)/100.0f; + zoom= clamp(zoom, 0.1f, 10.0f); + onPaint(paintEvent); + } + + lastX= x; + lastY= y; +} + +void MainWindow::onMenuFileLoad(wxCommandEvent &event){ + string fileName; + wxFileDialog fileDialog(this); + fileDialog.SetWildcard("G3D files (*.g3d)|*.g3d"); + if(fileDialog.ShowModal()==wxID_OK){ + delete model; + Model *tmpModel= new ModelGl(); + renderer->loadTheModel(tmpModel, fileDialog.GetPath().c_str()); + model= tmpModel; + GetStatusBar()->SetStatusText(getModelInfo().c_str()); + } +} + +void MainWindow::onMenuModeNormals(wxCommandEvent &event){ + renderer->toggleNormals(); + menuMode->Check(miModeNormals, renderer->getNormals()); +} + +void MainWindow::onMenuModeWireframe(wxCommandEvent &event){ + renderer->toggleWireframe(); + menuMode->Check(miModeWireframe, renderer->getWireframe()); +} + +void MainWindow::onMenuModeGrid(wxCommandEvent &event){ + renderer->toggleGrid(); + menuMode->Check(miModeGrid, renderer->getGrid()); +} + +void MainWindow::onMenuSpeedSlower(wxCommandEvent &event){ + speed/= 1.5f; +} + +void MainWindow::onMenuSpeedFaster(wxCommandEvent &event){ + speed*= 1.5f; +} + +void MainWindow::onMenuColorRed(wxCommandEvent &event){ + playerColor= Renderer::pcRed; + menuCustomColor->Check(miColorRed, true); + menuCustomColor->Check(miColorBlue, false); + menuCustomColor->Check(miColorYellow, false); + menuCustomColor->Check(miColorGreen, false); +} + +void MainWindow::onMenuColorBlue(wxCommandEvent &event){ + playerColor= Renderer::pcBlue; + menuCustomColor->Check(miColorRed, false); + menuCustomColor->Check(miColorBlue, true); + menuCustomColor->Check(miColorYellow, false); + menuCustomColor->Check(miColorGreen, false); +} + +void MainWindow::onMenuColorYellow(wxCommandEvent &event){ + playerColor= Renderer::pcYellow; + menuCustomColor->Check(miColorRed, false); + menuCustomColor->Check(miColorBlue, false); + menuCustomColor->Check(miColorYellow, true); + menuCustomColor->Check(miColorGreen, false); +} + +void MainWindow::onMenuColorGreen(wxCommandEvent &event){ + playerColor= Renderer::pcGreen; + menuCustomColor->Check(miColorRed, false); + menuCustomColor->Check(miColorBlue, false); + menuCustomColor->Check(miColorYellow, false); + menuCustomColor->Check(miColorGreen, true); +} + +void MainWindow::onTimer(wxTimerEvent &event){ + wxPaintEvent paintEvent; + + anim= anim+speed; + if(anim>1.0f){ + anim-= 1.f; + } + onPaint(paintEvent); +} + +string MainWindow::getModelInfo(){ + string str; + + if(model!=NULL){ + str+= "Meshes: "+intToStr(model->getMeshCount()); + str+= ", Vertices: "+intToStr(model->getVertexCount()); + str+= ", Triangles: "+intToStr(model->getTriangleCount()); + str+= ", Version: "+intToStr(model->getFileVersion()); + } + + return str; +} + +BEGIN_EVENT_TABLE(MainWindow, wxFrame) + EVT_TIMER(-1, MainWindow::onTimer) + EVT_CLOSE(MainWindow::onClose) + EVT_MENU(miFileLoad, MainWindow::onMenuFileLoad) + + EVT_MENU(miModeWireframe, MainWindow::onMenuModeWireframe) + EVT_MENU(miModeNormals, MainWindow::onMenuModeNormals) + EVT_MENU(miModeGrid, MainWindow::onMenuModeGrid) + + EVT_MENU(miSpeedFaster, MainWindow::onMenuSpeedFaster) + EVT_MENU(miSpeedSlower, MainWindow::onMenuSpeedSlower) + + EVT_MENU(miColorRed, MainWindow::onMenuColorRed) + EVT_MENU(miColorBlue, MainWindow::onMenuColorBlue) + EVT_MENU(miColorYellow, MainWindow::onMenuColorYellow) + EVT_MENU(miColorGreen, MainWindow::onMenuColorGreen) +END_EVENT_TABLE() + +// ===================================================== +// class GlCanvas +// ===================================================== + +GlCanvas::GlCanvas(MainWindow * mainWindow): + wxGLCanvas(mainWindow, -1) +{ + this->mainWindow = mainWindow; +} + +void GlCanvas::onMouseMove(wxMouseEvent &event){ + mainWindow->onMouseMove(event); +} + +void GlCanvas::onPaint(wxPaintEvent &event){ + mainWindow->onPaint(event); +} + +BEGIN_EVENT_TABLE(GlCanvas, wxGLCanvas) + EVT_MOTION(GlCanvas::onMouseMove) + EVT_PAINT(GlCanvas::onPaint) +END_EVENT_TABLE() + +// =============================================== +// class App +// =============================================== + +bool App::OnInit(){ + string modelPath; + if(argc==2){ + modelPath= argv[1]; + } + + mainWindow= new MainWindow(modelPath); + mainWindow->Show(); + return true; +} + +int App::MainLoop(){ + try{ + return wxApp::MainLoop(); + } + catch(const exception &e){ + wxMessageDialog(NULL, e.what(), "Exception", wxOK | wxICON_ERROR).ShowModal(); + return 0; + } +} + +int App::OnExit(){ + return 0; +} + +}}//end namespace + +IMPLEMENT_APP(Shared::G3dViewer::App) diff --git a/source/g3d_viewer/main.h b/source/g3d_viewer/main.h new file mode 100644 index 00000000..c620e8bb --- /dev/null +++ b/source/g3d_viewer/main.h @@ -0,0 +1,129 @@ +#ifndef _SHADER_G3DVIEWER_MAIN_H_ +#define _SHADER_G3DVIEWER_MAIN_H_ + +#include + +#include +#include +#include + +#include "renderer.h" +#include "util.h" +#include "window.h" + +using Shared::Platform::Window; +using Shared::Platform::MouseState; + +using std::string; + +namespace Shared{ namespace G3dViewer{ + +class GlCanvas; + +// =============================== +// class MainWindow +// =============================== + +class MainWindow: public wxFrame{ +private: + DECLARE_EVENT_TABLE() + +public: + static const string versionString; + static const string winHeader; + + enum MenuId{ + miFileLoad, + miModeWireframe, + miModeNormals, + miModeGrid, + miSpeedSlower, + miSpeedFaster, + miColorRed, + miColorBlue, + miColorYellow, + miColorGreen + }; + +private: + GlCanvas *glCanvas; + Renderer *renderer; + + wxTimer *timer; + + wxMenuBar *menu; + wxMenu *menuFile; + wxMenu *menuMode; + wxMenu *menuSpeed; + wxMenu *menuCustomColor; + + Model *model; + string modelPath; + + float speed; + float anim; + float rotX, rotY, zoom; + int lastX, lastY; + Renderer::PlayerColor playerColor; + +public: + MainWindow(const string &modelPath); + ~MainWindow(); + + void Notify(); + + void onPaint(wxPaintEvent &event); + void onClose(wxCloseEvent &event); + void onMenuFileLoad(wxCommandEvent &event); + void onMenuModeNormals(wxCommandEvent &event); + void onMenuModeWireframe(wxCommandEvent &event); + void onMenuModeGrid(wxCommandEvent &event); + void onMenuSpeedSlower(wxCommandEvent &event); + void onMenuSpeedFaster(wxCommandEvent &event); + void onMenuColorRed(wxCommandEvent &event); + void onMenuColorBlue(wxCommandEvent &event); + void onMenuColorYellow(wxCommandEvent &event); + void onMenuColorGreen(wxCommandEvent &event); + void onMouseMove(wxMouseEvent &event); + void onTimer(wxTimerEvent &event); + + string getModelInfo(); +}; + +// ===================================================== +// class GlCanvas +// ===================================================== + +class GlCanvas: public wxGLCanvas{ +private: + DECLARE_EVENT_TABLE() + +public: + GlCanvas(MainWindow *mainWindow); + + void onMouseMove(wxMouseEvent &event); + void onPaint(wxPaintEvent &event); + +private: + MainWindow *mainWindow; +}; + +// =============================== +// class App +// =============================== + +class App: public wxApp{ +private: + MainWindow *mainWindow; + +public: + virtual bool OnInit(); + virtual int MainLoop(); + virtual int OnExit(); +}; + +}}//end namespace + +DECLARE_APP(Shared::G3dViewer::App) + +#endif diff --git a/source/g3d_viewer/renderer.cpp b/source/g3d_viewer/renderer.cpp new file mode 100644 index 00000000..858175fe --- /dev/null +++ b/source/g3d_viewer/renderer.cpp @@ -0,0 +1,272 @@ +#include "renderer.h" + +#include "opengl.h" +#include "texture_gl.h" +#include "graphics_interface.h" +#include "graphics_factory_gl.h" + +using namespace Shared::Graphics; +using namespace Shared::Graphics::Gl; + +namespace Shared{ namespace G3dViewer{ + +// =============================================== +// class MeshCallbackTeamColor +// =============================================== + +void MeshCallbackTeamColor::execute(const Mesh *mesh){ + + //team color + if(mesh->getCustomTexture() && teamTexture!=NULL){ + //texture 0 + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + //set color to interpolation + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); + + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE1); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA); + + //set alpha to 1 + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + //texture 1 + glActiveTexture(GL_TEXTURE1); + glMultiTexCoord2f(GL_TEXTURE1, 0.f, 0.f); + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, static_cast(teamTexture)->getHandle()); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + //set alpha to 1 + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + glActiveTexture(GL_TEXTURE0); + } + else{ + glActiveTexture(GL_TEXTURE1); + glDisable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } +} + +// =============================================== +// class Renderer +// =============================================== + +Renderer::Renderer(){ + normals= false; + wireframe= false; + grid= true; +} + +Renderer::~Renderer(){ + delete modelRenderer; + delete textureManager; +} + +Renderer * Renderer::getInstance(){ + static Renderer * renderer = new Renderer(); + return renderer; +} + +void Renderer::transform(float rotX, float rotY, float zoom){ + assertGl(); + + glMatrixMode(GL_MODELVIEW); + glRotatef(rotY, 1.0f, 0.0f, 0.0f); + glRotatef(rotX, 0.0f, 1.0f, 0.0f); + glScalef(zoom, zoom, zoom); + Vec4f pos(-8.0f, 5.0f, 10.0f, 0.0f); + glLightfv(GL_LIGHT0,GL_POSITION, pos.ptr()); + + assertGl(); +} + +void Renderer::init(){ + assertGl(); + + GraphicsFactory *gf= new GraphicsFactoryGl(); + + GraphicsInterface::getInstance().setFactory(gf); + + modelRenderer= gf->newModelRenderer(); + textureManager= gf->newTextureManager(); + + //red tex + customTextureRed= textureManager->newTexture2D(); + customTextureRed->getPixmap()->init(1, 1, 3); + customTextureRed->getPixmap()->setPixel(0, 0, Vec3f(1.f, 0.f, 0.f)); + + //blue tex + customTextureBlue= textureManager->newTexture2D(); + customTextureBlue->getPixmap()->init(1, 1, 3); + customTextureBlue->getPixmap()->setPixel(0, 0, Vec3f(0.f, 0.f, 1.f)); + + //yellow tex + customTextureYellow= textureManager->newTexture2D(); + customTextureYellow->getPixmap()->init(1, 1, 3); + customTextureYellow->getPixmap()->setPixel(0, 0, Vec3f(1.f, 1.f, 0.f)); + + //green + customTextureGreen= textureManager->newTexture2D(); + customTextureGreen->getPixmap()->init(1, 1, 3); + customTextureGreen->getPixmap()->setPixel(0, 0, Vec3f(0.f, 0.5f, 0.f)); + + glClearColor(0.3f, 0.3f, 0.3f, 1.0f); + glEnable(GL_TEXTURE_2D); + glFrontFace(GL_CW); + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.5f); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + + Vec4f diffuse= Vec4f(1.0f, 1.0f, 1.0f, 1.0f); + Vec4f ambient= Vec4f(0.3f, 0.3f, 0.3f, 1.0f); + Vec4f specular= Vec4f(0.1f, 0.1f, 0.1f, 1.0f); + + glLightfv(GL_LIGHT0,GL_AMBIENT, ambient.ptr()); + glLightfv(GL_LIGHT0,GL_DIFFUSE, diffuse.ptr()); + glLightfv(GL_LIGHT0,GL_SPECULAR, specular.ptr()); + + glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); + glEnable(GL_COLOR_MATERIAL); + + assertGl(); +} + +void Renderer::reset(int w, int h, PlayerColor playerColor){ + assertGl(); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glViewport(0, 0, w, h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(60.0f, static_cast(w)/h, 1.0f, 200.0f); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, -1.5, -5); + + Texture2D *customTexture; + switch(playerColor){ + case pcRed: + customTexture= customTextureRed; + break; + case pcBlue: + customTexture= customTextureBlue; + break; + case pcYellow: + customTexture= customTextureYellow; + break; + case pcGreen: + customTexture= customTextureGreen; + break; + default: + assert(false); + } + meshCallbackTeamColor.setTeamTexture(customTexture); + + if(wireframe){ + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glDisable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + glDisable(GL_LIGHT0); + } + else{ + glEnable(GL_TEXTURE_2D); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + + assertGl(); +} + +void Renderer::renderGrid(){ + if(grid){ + + float i; + + assertGl(); + + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + + glBegin(GL_LINES); + glColor3f(1.0f, 1.0f, 1.0f); + for(i=-10.0f; i<=10.0f; i+=1.0f){ + glVertex3f(i, 0.0f, 10.0f); + glVertex3f(i, 0.0f, -10.0f); + } + for(i=-10.0f; i<=10.0f; i+=1.0f){ + glVertex3f(10.f, 0.0f, i); + glVertex3f(-10.f, 0.0f, i); + } + glEnd(); + + glPopAttrib(); + + assertGl(); + } +} + +void Renderer::toggleNormals(){ + normals= normals? false: true; +} + +void Renderer::toggleWireframe(){ + wireframe= wireframe? false: true; +} + +void Renderer::toggleGrid(){ + grid= grid? false: true; +} + +void Renderer::loadTheModel(Model *model, string file){ + model->setTextureManager(textureManager); + model->loadG3d(file); + textureManager->init(); +} + +void Renderer::renderTheModel(Model *model, float f){ + if(model != NULL){ + modelRenderer->begin(true, true, !wireframe, &meshCallbackTeamColor); + model->updateInterpolationData(f, true); + modelRenderer->render(model); + + if(normals){ + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_LIGHTING); + glDisable(GL_TEXTURE_2D); + glColor3f(1.0f, 1.0f, 1.0f); + modelRenderer->renderNormalsOnly(model); + glPopAttrib(); + } + + modelRenderer->end(); + } +} + +}}//end namespace diff --git a/source/g3d_viewer/renderer.h b/source/g3d_viewer/renderer.h new file mode 100644 index 00000000..f0a9d960 --- /dev/null +++ b/source/g3d_viewer/renderer.h @@ -0,0 +1,92 @@ +#ifndef _SHADER_G3DVIEWER_RENDERER_H_ +#define _SHADER_G3DVIEWER_RENDERER_H_ + +#include "model_renderer.h" +#include "texture_manager.h" +#include "model.h" +#include "texture.h" + +using Shared::Graphics::ModelRenderer; +using Shared::Graphics::TextureManager; +using Shared::Graphics::Model; +using Shared::Graphics::Texture2D; + +#include "model_renderer.h" + +using Shared::Graphics::MeshCallback; +using Shared::Graphics::Mesh; +using Shared::Graphics::Texture; + +namespace Shared{ namespace G3dViewer{ + +// =============================================== +// class MeshCallbackTeamColor +// =============================================== + +class MeshCallbackTeamColor: public MeshCallback{ +private: + const Texture *teamTexture; + +public: + void setTeamTexture(const Texture *teamTexture) {this->teamTexture= teamTexture;} + virtual void execute(const Mesh *mesh); +}; + +// =============================== +// class Renderer +// =============================== + +class Renderer{ +public: + static const int windowX= 100; + static const int windowY= 100; + static const int windowW= 640; + static const int windowH= 480; + +public: + enum PlayerColor{ + pcRed, + pcBlue, + pcYellow, + pcGreen + }; + +private: + bool wireframe; + bool normals; + bool grid; + + ModelRenderer *modelRenderer; + TextureManager *textureManager; + Texture2D *customTextureRed; + Texture2D *customTextureBlue; + Texture2D *customTextureYellow; + Texture2D *customTextureGreen; + MeshCallbackTeamColor meshCallbackTeamColor; + + Renderer(); + +public: + ~Renderer(); + static Renderer *getInstance(); + + void init(); + void reset(int w, int h, PlayerColor playerColor); + void transform(float rotX, float rotY, float zoom); + void renderGrid(); + + bool getNormals() const {return normals;} + bool getWireframe() const {return wireframe;} + bool getGrid() const {return grid;} + + void toggleNormals(); + void toggleWireframe(); + void toggleGrid(); + + void loadTheModel(Model *model, string file); + void renderTheModel(Model *model, float f); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/ai/ai.cpp b/source/glest_game/ai/ai.cpp new file mode 100644 index 00000000..690cbf6a --- /dev/null +++ b/source/glest_game/ai/ai.cpp @@ -0,0 +1,482 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "ai.h" + +#include + +#include "ai_interface.h" +#include "ai_rule.h" +#include "unit_type.h" +#include "unit.h" +#include "program.h" +#include "config.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class ProduceTask +// ===================================================== + +ProduceTask::ProduceTask(UnitClass unitClass){ + taskClass= tcProduce; + this->unitClass= unitClass; + unitType= NULL; + resourceType= NULL; +} + +ProduceTask::ProduceTask(const UnitType *unitType){ + taskClass= tcProduce; + this->unitType= unitType; + resourceType= NULL; +} + +ProduceTask::ProduceTask(const ResourceType *resourceType){ + taskClass= tcProduce; + unitType= NULL; + this->resourceType= resourceType; +} + +string ProduceTask::toString() const{ + string str= "Produce "; + if(unitType!=NULL){ + str+= unitType->getName(); + } + return str; +} + +// ===================================================== +// class BuildTask +// ===================================================== + +BuildTask::BuildTask(const UnitType *unitType){ + taskClass= tcBuild; + this->unitType= unitType; + resourceType= NULL; + forcePos= false; +} + +BuildTask::BuildTask(const ResourceType *resourceType){ + taskClass= tcBuild; + unitType= NULL; + this->resourceType= resourceType; + forcePos= false; +} + +BuildTask::BuildTask(const UnitType *unitType, const Vec2i &pos){ + taskClass= tcBuild; + this->unitType= unitType; + resourceType= NULL; + forcePos= true; + this->pos= pos; +} + +string BuildTask::toString() const{ + string str= "Build "; + if(unitType!=NULL){ + str+= unitType->getName(); + } + return str; +} + +// ===================================================== +// class UpgradeTask +// ===================================================== + +UpgradeTask::UpgradeTask(const UpgradeType *upgradeType){ + taskClass= tcUpgrade; + this->upgradeType= upgradeType; +} + +string UpgradeTask::toString() const{ + string str= "Build "; + if(upgradeType!=NULL){ + str+= upgradeType->getName(); + } + return str; +} + +// ===================================================== +// class Ai +// ===================================================== + +void Ai::init(AiInterface *aiInterface){ + this->aiInterface= aiInterface; + startLoc= random.randRange(0, aiInterface->getMapMaxPlayers()-1); + upgradeCount= 0; + minWarriors= minMinWarriors; + randomMinWarriorsReached= false; + //add ai rules + aiRules.resize(14); + aiRules[0]= new AiRuleWorkerHarvest(this); + aiRules[1]= new AiRuleRefreshHarvester(this); + aiRules[2]= new AiRuleScoutPatrol(this); + aiRules[3]= new AiRuleReturnBase(this); + aiRules[4]= new AiRuleMassiveAttack(this); + aiRules[5]= new AiRuleAddTasks(this); + aiRules[6]= new AiRuleProduceResourceProducer(this); + aiRules[7]= new AiRuleBuildOneFarm(this); + aiRules[8]= new AiRuleProduce(this); + aiRules[9]= new AiRuleBuild(this); + aiRules[10]= new AiRuleUpgrade(this); + aiRules[11]= new AiRuleExpand(this); + aiRules[12]= new AiRuleRepair(this); + aiRules[13]= new AiRuleRepair(this); +} + +Ai::~Ai(){ + deleteValues(tasks.begin(), tasks.end()); + deleteValues(aiRules.begin(), aiRules.end()); +} + +void Ai::update(){ + //process ai rules + for(AiRules::iterator it= aiRules.begin(); it!=aiRules.end(); ++it){ + if((aiInterface->getTimer() % ((*it)->getTestInterval()*GameConstants::updateFps/1000))==0){ + if((*it)->test()){ + aiInterface->printLog(3, intToStr(1000*aiInterface->getTimer()/GameConstants::updateFps) + ": Executing rule: " + (*it)->getName() + '\n'); + (*it)->execute(); + } + } + } +} + + +// ==================== state requests ==================== + +int Ai::getCountOfType(const UnitType *ut){ + int count= 0; + for(int i=0; igetMyUnitCount(); ++i){ + if(ut == aiInterface->getMyUnit(i)->getType()){ + count++; + } + } + return count; +} + +int Ai::getCountOfClass(UnitClass uc){ + int count= 0; + for(int i=0; igetMyUnitCount(); ++i){ + if(aiInterface->getMyUnit(i)->getType()->isOfClass(uc)){ + ++count; + } + } + return count; +} + +float Ai::getRatioOfClass(UnitClass uc){ + if(aiInterface->getMyUnitCount()==0){ + return 0; + } + else{ + return static_cast(getCountOfClass(uc))/aiInterface->getMyUnitCount(); + } +} + +const ResourceType *Ai::getNeededResource(){ + + int amount= -1; + const ResourceType *neededResource= NULL; + const TechTree *tt= aiInterface->getTechTree(); + + for(int i=0; igetResourceTypeCount(); ++i){ + const ResourceType *rt= tt->getResourceType(i); + const Resource *r= aiInterface->getResource(rt); + if(rt->getClass()!=rcStatic && rt->getClass()!=rcConsumable && (r->getAmount()getAmount(); + neededResource= rt; + } + } + + return neededResource; +} + +bool Ai::beingAttacked(Vec2i &pos, Field &field, int radius){ + int count= aiInterface->onSightUnitCount(); + const Unit *unit; + + for(int i=0; igetOnSightUnit(i); + if(!aiInterface->isAlly(unit) && unit->isAlive()){ + pos= unit->getPos(); + field= unit->getCurrField(); + if(pos.dist(aiInterface->getHomeLocation())printLog(2, "Being attacked at pos "+intToStr(pos.x)+","+intToStr(pos.y)+"\n"); + return true; + } + } + } + return false; +} + +bool Ai::isStableBase(){ + + if(getCountOfClass(ucWarrior)>minWarriors){ + aiInterface->printLog(4, "Base is stable\n"); + return true; + } + else{ + aiInterface->printLog(4, "Base is not stable\n"); + return false; + } +} + +bool Ai::findAbleUnit(int *unitIndex, CommandClass ability, bool idleOnly){ + vector units; + + *unitIndex= -1; + for(int i=0; igetMyUnitCount(); ++i){ + const Unit *unit= aiInterface->getMyUnit(i); + if(unit->getType()->hasCommandClass(ability)){ + if(!idleOnly || !unit->anyCommand() || unit->getCurrCommand()->getCommandType()->getClass()==ccStop){ + units.push_back(i); + } + } + } + + if(units.empty()){ + return false; + } + else{ + *unitIndex= units[random.randRange(0, units.size()-1)]; + return true; + } +} + +bool Ai::findAbleUnit(int *unitIndex, CommandClass ability, CommandClass currentCommand){ + vector units; + + *unitIndex= -1; + for(int i=0; igetMyUnitCount(); ++i){ + const Unit *unit= aiInterface->getMyUnit(i); + if(unit->getType()->hasCommandClass(ability)){ + if(unit->anyCommand() && unit->getCurrCommand()->getCommandType()->getClass()==currentCommand){ + units.push_back(i); + } + } + } + + if(units.empty()){ + return false; + } + else{ + *unitIndex= units[random.randRange(0, units.size()-1)]; + return true; + } +} + +bool Ai::findPosForBuilding(const UnitType* building, const Vec2i &searchPos, Vec2i &outPos){ + + const int spacing= 1; + + for(int currRadius=0; currRadiusisFreeCells(outPos-Vec2i(spacing), building->getSize()+spacing*2, fLand)){ + return true; + } + } + } + } + + return false; + +} + + +// ==================== tasks ==================== + +void Ai::addTask(const Task *task){ + tasks.push_back(task); + aiInterface->printLog(2, "Task added: " + task->toString()); +} + +void Ai::addPriorityTask(const Task *task){ + deleteValues(tasks.begin(), tasks.end()); + tasks.clear(); + + tasks.push_back(task); + aiInterface->printLog(2, "Priority Task added: " + task->toString()); +} + +bool Ai::anyTask(){ + return !tasks.empty(); +} + +const Task *Ai::getTask() const{ + if(tasks.empty()){ + return NULL; + } + else{ + return tasks.front(); + } +} + +void Ai::removeTask(const Task *task){ + aiInterface->printLog(2, "Task removed: " + task->toString()); + tasks.remove(task); + delete task; +} + +void Ai::retryTask(const Task *task){ + tasks.remove(task); + tasks.push_back(task); +} +// ==================== expansions ==================== + +void Ai::addExpansion(const Vec2i &pos){ + + //check if there is a nearby expansion + for(Positions::iterator it= expansionPositions.begin(); it!=expansionPositions.end(); ++it){ + if((*it).dist(pos)maxExpansions){ + expansionPositions.pop_back(); + } +} + +Vec2i Ai::getRandomHomePosition(){ + + if(expansionPositions.empty() || random.randRange(0, 1) == 0){ + return aiInterface->getHomeLocation(); + } + + return expansionPositions[random.randRange(0, expansionPositions.size()-1)]; +} + +// ==================== actions ==================== + +void Ai::sendScoutPatrol(){ + Vec2i pos; + int unit; + + startLoc= (startLoc+1) % aiInterface->getMapMaxPlayers(); + pos= aiInterface->getStartLocation(startLoc); + + if(aiInterface->getFactionIndex()!=startLoc){ + if(findAbleUnit(&unit, ccAttack, false)){ + aiInterface->giveCommand(unit, ccAttack, pos); + aiInterface->printLog(2, "Scout patrol sent to: " + intToStr(pos.x)+","+intToStr(pos.y)+"\n"); + } + } + +} + +void Ai::massiveAttack(const Vec2i &pos, Field field, bool ultraAttack){ + int producerWarriorCount=0; + int maxProducerWarriors=random.randRange(1,11); + for(int i=0; igetMyUnitCount(); ++i){ + bool isWarrior; + const Unit *unit= aiInterface->getMyUnit(i); + const AttackCommandType *act= unit->getType()->getFirstAttackCommand(field); + if(act!=NULL && unit->getType()->hasCommandClass(ccProduce)) + { + producerWarriorCount++; + } + + if(aiInterface->getControlType()==ctCpuMega) + { + if(producerWarriorCount>maxProducerWarriors) + { + if( + unit->getCommandSize()>0 && + unit->getCurrCommand()->getCommandType()!=NULL && ( + unit->getCurrCommand()->getCommandType()->getClass()==ccBuild || + unit->getCurrCommand()->getCommandType()->getClass()==ccMorph || + unit->getCurrCommand()->getCommandType()->getClass()==ccProduce + ) + ) + { + isWarrior=false; + } + else + { + isWarrior=!unit->getType()->hasCommandClass(ccHarvest); + } + } + else + { + isWarrior= !unit->getType()->hasCommandClass(ccHarvest) && !unit->getType()->hasCommandClass(ccProduce); + } + } + else + { + isWarrior= !unit->getType()->hasCommandClass(ccHarvest) && !unit->getType()->hasCommandClass(ccProduce); + } + + + bool alreadyAttacking= unit->getCurrSkill()->getClass()==scAttack; + if(!alreadyAttacking && act!=NULL && (ultraAttack || isWarrior)){ + aiInterface->giveCommand(i, act, pos); + } + } + + if(aiInterface->getControlType()==ctCpuEasy) + { + minWarriors+= 1; + } + else if(aiInterface->getControlType()==ctCpuMega) + { + minWarriors+= 3; + if(minWarriors>maxMinWarriors-1 || randomMinWarriorsReached) + { + randomMinWarriorsReached=true; + minWarriors=random.randRange(maxMinWarriors-10, maxMinWarriors*2); + } + } + else if(minWarriorsprintLog(2, "Massive attack to pos: "+ intToStr(pos.x)+", "+intToStr(pos.y)+"\n"); +} + +void Ai::returnBase(int unitIndex){ + Vec2i pos; + CommandResult r; + int fi; + + fi= aiInterface->getFactionIndex(); + pos= Vec2i( + random.randRange(-villageRadius, villageRadius), random.randRange(-villageRadius, villageRadius)) + + getRandomHomePosition(); + r= aiInterface->giveCommand(unitIndex, ccMove, pos); + + //aiInterface->printLog(1, "Order return to base pos:" + intToStr(pos.x)+", "+intToStr(pos.y)+": "+rrToStr(r)+"\n"); +} + +void Ai::harvest(int unitIndex){ + + const ResourceType *rt= getNeededResource(); + + if(rt!=NULL){ + const HarvestCommandType *hct= aiInterface->getMyUnit(unitIndex)->getType()->getFirstHarvestCommand(rt); + Vec2i resPos; + if(hct!=NULL && aiInterface->getNearestSightedResource(rt, aiInterface->getHomeLocation(), resPos)){ + resPos= resPos+Vec2i(random.randRange(-2, 2), random.randRange(-2, 2)); + aiInterface->giveCommand(unitIndex, hct, resPos); + //aiInterface->printLog(4, "Order harvest pos:" + intToStr(resPos.x)+", "+intToStr(resPos.y)+": "+rrToStr(r)+"\n"); + } + } +} + +}}//end namespace diff --git a/source/glest_game/ai/ai.h b/source/glest_game/ai/ai.h new file mode 100644 index 00000000..a90421a7 --- /dev/null +++ b/source/glest_game/ai/ai.h @@ -0,0 +1,189 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_AI_H_ +#define _GLEST_GAME_AI_H_ + +#include +#include + +#include "world.h" +#include "commander.h" +#include "command.h" +#include "random.h" + +using std::deque; +using std::vector; +using std::list; +using Shared::Util::Random; + +namespace Glest{ namespace Game{ + +class AiInterface; +class AiRule; + +// ===================================================== +// class Task +// +/// An action that has to be performed by the IA +// ===================================================== + +enum TaskClass{ + tcProduce, + tcBuild, + tcUpgrade +}; + +class Task{ +protected: + TaskClass taskClass; + +public: + virtual ~Task(){} + TaskClass getClass() const {return taskClass;} + virtual string toString() const= 0; +}; + +// ==================== ProduceTask ==================== + +class ProduceTask: public Task{ +private: + UnitClass unitClass; + const UnitType *unitType; + const ResourceType *resourceType; + +public: + ProduceTask(UnitClass unitClass); + ProduceTask(const UnitType *unitType); + ProduceTask(const ResourceType *resourceType); + + UnitClass getUnitClass() const {return unitClass;} + const UnitType *getUnitType() const {return unitType;} + const ResourceType *getResourceType() const {return resourceType;} + virtual string toString() const; +}; + +// ==================== BuildTask ==================== + +class BuildTask: public Task{ +private: + const UnitType *unitType; + const ResourceType *resourceType; + bool forcePos; + Vec2i pos; + +public: + BuildTask(const UnitType *unitType= NULL); + BuildTask(const ResourceType *resourceType); + BuildTask(const UnitType *unitType, const Vec2i &pos); + + const UnitType *getUnitType() const {return unitType;} + const ResourceType *getResourceType() const {return resourceType;} + bool getForcePos() const {return forcePos;} + Vec2i getPos() const {return pos;} + virtual string toString() const; +}; + +// ==================== UpgradeTask ==================== + +class UpgradeTask: public Task{ +private: + const UpgradeType *upgradeType; + +public: + UpgradeTask(const UpgradeType *upgradeType= NULL); + const UpgradeType *getUpgradeType() const {return upgradeType;} + virtual string toString() const; +}; + +// =============================== +// class AI +// +/// Main AI class +// =============================== + +class Ai{ +private: + static const int harvesterPercent= 30; + static const int maxBuildRadius= 40; + static const int minMinWarriors= 7; + static const int maxMinWarriors= 20; + static const int minStaticResources= 10; + static const int minConsumableResources= 20; + static const int maxExpansions= 2; + static const int villageRadius= 15; + +public: + enum ResourceUsage{ + ruHarvester, + ruWarrior, + ruBuilding, + ruUpgrade + }; + +private: + typedef vector AiRules; + typedef list Tasks; + typedef deque Positions; + +private: + AiInterface *aiInterface; + AiRules aiRules; + int startLoc; + bool randomMinWarriorsReached; + int upgradeCount; + Tasks tasks; + Positions expansionPositions; + Random random; + +public: + int minWarriors; + ~Ai(); + void init(AiInterface *aiInterface); + void update(); + + //state requests + AiInterface *getAiInterface() const {return aiInterface;} + Random* getRandom() {return &random;} + int getCountOfType(const UnitType *ut); + + int getCountOfClass(UnitClass uc); + float getRatioOfClass(UnitClass uc); + + const ResourceType *getNeededResource(); + bool isStableBase(); + bool findPosForBuilding(const UnitType* building, const Vec2i &searchPos, Vec2i &pos); + bool findAbleUnit(int *unitIndex, CommandClass ability, bool idleOnly); + bool findAbleUnit(int *unitIndex, CommandClass ability, CommandClass currentCommand); + bool beingAttacked(Vec2i &pos, Field &field, int radius); + + //tasks + void addTask(const Task *task); + void addPriorityTask(const Task *task); + bool anyTask(); + const Task *getTask() const; + void removeTask(const Task *task); + void retryTask(const Task *task); + + //expansions + void addExpansion(const Vec2i &pos); + Vec2i getRandomHomePosition(); + + //actions + void sendScoutPatrol(); + void massiveAttack(const Vec2i &pos, Field field, bool ultraAttack= false); + void returnBase(int unitIndex); + void harvest(int unitIndex); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/ai/ai_interface.cpp b/source/glest_game/ai/ai_interface.cpp new file mode 100644 index 00000000..e33f9a6c --- /dev/null +++ b/source/glest_game/ai/ai_interface.cpp @@ -0,0 +1,238 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "ai_interface.h" + +#include "ai.h" +#include "command_type.h" +#include "faction.h" +#include "unit.h" +#include "unit_type.h" +#include "object.h" +#include "game.h" +#include "config.h" +#include "leak_dumper.h" + +using namespace Shared::Util; +using namespace Shared::Graphics; + +// ===================================================== +// class AiInterface +// ===================================================== + +namespace Glest{ namespace Game{ + +AiInterface::AiInterface(Game &game, int factionIndex, int teamIndex){ + this->world= game.getWorld(); + this->commander= game.getCommander(); + this->console= game.getConsole(); + + this->factionIndex= factionIndex; + this->teamIndex= teamIndex; + timer= 0; + + //init ai + ai.init(this); + + //config + logLevel= Config::getInstance().getInt("AiLog"); + redir= Config::getInstance().getBool("AiRedir"); + + //clear log file + if(logLevel>0){ + FILE *f= fopen(getLogFilename().c_str(), "wt"); + if(f==NULL){ + throw runtime_error("Can't open file: "+getLogFilename()); + } + fprintf(f, "%s", "Glest AI log file\n\n"); + fclose(f); + } +} + +// ==================== main ==================== + +void AiInterface::update(){ + timer++; + ai.update(); +} + +// ==================== misc ==================== + +void AiInterface::printLog(int logLevel, const string &s){ + if(this->logLevel>=logLevel){ + string logString= "(" + intToStr(factionIndex) + ") " + s; + + //print log to file + FILE *f= fopen(getLogFilename().c_str(), "at"); + if(f==NULL){ + throw runtime_error("Can't open file: "+getLogFilename()); + } + fprintf(f, "%s\n", logString.c_str()); + fclose(f); + + //redirect to console + if(redir) { + console->addLine(logString); + } + } +} + +// ==================== interaction ==================== + +CommandResult AiInterface::giveCommand(int unitIndex, CommandClass commandClass, const Vec2i &pos){ + Command *c= new Command (world->getFaction(factionIndex)->getUnit(unitIndex)->getType()->getFirstCtOfClass(commandClass), pos); + return world->getFaction(factionIndex)->getUnit(unitIndex)->giveCommand(c); +} + +CommandResult AiInterface::giveCommand(int unitIndex, const CommandType *commandType, const Vec2i &pos){ + return world->getFaction(factionIndex)->getUnit(unitIndex)->giveCommand(new Command(commandType, pos)); +} + +CommandResult AiInterface::giveCommand(int unitIndex, const CommandType *commandType, const Vec2i &pos, const UnitType *ut){ + return world->getFaction(factionIndex)->getUnit(unitIndex)->giveCommand(new Command(commandType, pos, ut)); +} + +CommandResult AiInterface::giveCommand(int unitIndex, const CommandType *commandType, Unit *u){ + return world->getFaction(factionIndex)->getUnit(unitIndex)->giveCommand(new Command(commandType, u)); +} + +// ==================== get data ==================== + +int AiInterface::getMapMaxPlayers(){ + return world->getMaxPlayers(); +} + +Vec2i AiInterface::getHomeLocation(){ + return world->getMap()->getStartLocation(world->getFaction(factionIndex)->getStartLocationIndex()); +} + +Vec2i AiInterface::getStartLocation(int loactionIndex){ + return world->getMap()->getStartLocation(loactionIndex); +} + +int AiInterface::getFactionCount(){ + return world->getFactionCount(); +} + +int AiInterface::getMyUnitCount() const{ + return world->getFaction(factionIndex)->getUnitCount(); +} + +int AiInterface::getMyUpgradeCount() const{ + return world->getFaction(factionIndex)->getUpgradeManager()->getUpgradeCount(); +} + +int AiInterface::onSightUnitCount(){ + int count=0; + Map *map= world->getMap(); + for(int i=0; igetFactionCount(); ++i){ + for(int j=0; jgetFaction(i)->getUnitCount(); ++j){ + SurfaceCell *sc= map->getSurfaceCell(Map::toSurfCoords(world->getFaction(i)->getUnit(j)->getPos())); + if(sc->isVisible(teamIndex)){ + count++; + } + } + } + return count; +} + +const Resource *AiInterface::getResource(const ResourceType *rt){ + return world->getFaction(factionIndex)->getResource(rt); +} + +const Unit *AiInterface::getMyUnit(int unitIndex){ + return world->getFaction(factionIndex)->getUnit(unitIndex); +} + +const Unit *AiInterface::getOnSightUnit(int unitIndex){ + + int count=0; + Map *map= world->getMap(); + + for(int i=0; igetFactionCount(); ++i){ + for(int j=0; jgetFaction(i)->getUnitCount(); ++j){ + Unit *u= world->getFaction(i)->getUnit(j); + if(map->getSurfaceCell(Map::toSurfCoords(u->getPos()))->isVisible(teamIndex)){ + if(count==unitIndex){ + return u; + } + else{ + count ++; + } + } + } + } + return NULL; +} + +const FactionType * AiInterface::getMyFactionType(){ + return world->getFaction(factionIndex)->getType(); +} + +const ControlType AiInterface::getControlType(){ + return world->getFaction(factionIndex)->getControlType(); +} + +const TechTree *AiInterface::getTechTree(){ + return world->getTechTree(); +} + +bool AiInterface::getNearestSightedResource(const ResourceType *rt, const Vec2i &pos, Vec2i &resultPos){ + float tmpDist; + + float nearestDist= infinity; + bool anyResource= false; + + const Map *map= world->getMap(); + + for(int i=0; igetW(); ++i){ + for(int j=0; jgetH(); ++j){ + Vec2i surfPos= Map::toSurfCoords(Vec2i(i, j)); + + //if explored cell + if(map->getSurfaceCell(surfPos)->isExplored(teamIndex)){ + Resource *r= map->getSurfaceCell(surfPos)->getResource(); + + //if resource cell + if(r!=NULL && r->getType()==rt){ + tmpDist= pos.dist(Vec2i(i, j)); + if(tmpDistgetFaction(factionIndex)->isAlly(unit->getFaction()); +} +bool AiInterface::reqsOk(const RequirableType *rt){ + return world->getFaction(factionIndex)->reqsOk(rt); +} + +bool AiInterface::reqsOk(const CommandType *ct){ + return world->getFaction(factionIndex)->reqsOk(ct); +} + +bool AiInterface::checkCosts(const ProducibleType *pt){ + return world->getFaction(factionIndex)->checkCosts(pt); +} + +bool AiInterface::isFreeCells(const Vec2i &pos, int size, Field field){ + return world->getMap()->isFreeCells(pos, size, field); +} + +}}//end namespace diff --git a/source/glest_game/ai/ai_interface.h b/source/glest_game/ai/ai_interface.h new file mode 100644 index 00000000..950f2e99 --- /dev/null +++ b/source/glest_game/ai/ai_interface.h @@ -0,0 +1,93 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_AIINTERFACE_H_ +#define _GLEST_GAME_AIINTERFACE_H_ + +#include "world.h" +#include "commander.h" +#include "command.h" +#include "conversion.h" +#include "ai.h" + +using Shared::Util::intToStr; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class AiInterface +// +/// The AI will interact with the game through this interface +// ===================================================== + +class AiInterface{ +private: + World *world; + Commander *commander; + Console *console; + Ai ai; + + int timer; + int factionIndex; + int teamIndex; + + //config + bool redir; + int logLevel; + +public: + AiInterface(Game &game, int factionIndex, int teamIndex); + + //main + void update(); + + //get + int getTimer() const {return timer;} + int getFactionIndex() const {return factionIndex;} + + //misc + void printLog(int logLevel, const string &s); + + //interact + CommandResult giveCommand(int unitIndex, CommandClass commandClass, const Vec2i &pos=Vec2i(0)); + CommandResult giveCommand(int unitIndex, const CommandType *commandType, const Vec2i &pos, const UnitType* unitType); + CommandResult giveCommand(int unitIndex, const CommandType *commandType, const Vec2i &pos); + CommandResult giveCommand(int unitIndex, const CommandType *commandType, Unit *u= NULL); + + //get data + const ControlType getControlType(); + int getMapMaxPlayers(); + Vec2i getHomeLocation(); + Vec2i getStartLocation(int locationIndex); + int getFactionCount(); + int getMyUnitCount() const; + int getMyUpgradeCount() const; + int onSightUnitCount(); + const Resource *getResource(const ResourceType *rt); + const Unit *getMyUnit(int unitIndex); + const Unit *getOnSightUnit(int unitIndex); + const FactionType *getMyFactionType(); + const TechTree *getTechTree(); + bool getNearestSightedResource(const ResourceType *rt, const Vec2i &pos, Vec2i &resultPos); + bool isAlly(const Unit *unit) const; + bool isAlly(int factionIndex) const; + bool reqsOk(const RequirableType *rt); + bool reqsOk(const CommandType *ct); + bool checkCosts(const ProducibleType *pt); + bool isFreeCells(const Vec2i &pos, int size, Field field); + +private: + string getLogFilename() const {return "ai"+intToStr(factionIndex)+".log";} +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/ai/ai_rule.cpp b/source/glest_game/ai/ai_rule.cpp new file mode 100644 index 00000000..9067ce50 --- /dev/null +++ b/source/glest_game/ai/ai_rule.cpp @@ -0,0 +1,1050 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== +#include "ai_rule.h" + +#include +#include + +#include "ai.h" +#include "ai_interface.h" +#include "unit.h" +#include "leak_dumper.h" + +using Shared::Graphics::Vec2i; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class AiRule +// ===================================================== + +AiRule::AiRule(Ai *ai){ + this->ai= ai; +} + +// ===================================================== +// class AiRuleWorkerHarvest +// ===================================================== + +AiRuleWorkerHarvest::AiRuleWorkerHarvest(Ai *ai): + AiRule(ai) +{ + stoppedWorkerIndex= -1; +} + +bool AiRuleWorkerHarvest::test(){ + return ai->findAbleUnit(&stoppedWorkerIndex, ccHarvest, true); +} + +void AiRuleWorkerHarvest::execute(){ + ai->harvest(stoppedWorkerIndex); +} + +// ===================================================== +// class AiRuleRefreshHarvester +// ===================================================== + +AiRuleRefreshHarvester::AiRuleRefreshHarvester(Ai *ai): + AiRule(ai) +{ + workerIndex= -1; +} + +bool AiRuleRefreshHarvester::test(){ + return ai->findAbleUnit(&workerIndex, ccHarvest, ccHarvest); +} + +void AiRuleRefreshHarvester::execute(){ + ai->harvest(workerIndex); +} + +// ===================================================== +// class AiRuleScoutPatrol +// ===================================================== + +AiRuleScoutPatrol::AiRuleScoutPatrol(Ai *ai): + AiRule(ai) +{ +} + +bool AiRuleScoutPatrol::test(){ + return ai->isStableBase(); +} + +void AiRuleScoutPatrol::execute(){ + ai->sendScoutPatrol(); +} +// ===================================================== +// class AiRuleRepair +// ===================================================== + +AiRuleRepair::AiRuleRepair(Ai *ai): + AiRule(ai) +{ +} + +bool AiRuleRepair::test(){ + AiInterface *aiInterface= ai->getAiInterface(); + + //look for a damaged unit + for(int i=0; igetMyUnitCount(); ++i){ + const Unit *u= aiInterface->getMyUnit(i); + if(u->getHpRatio()<1.f){ + damagedUnitIndex= i; + return true; + } + } + return false; +} + +void AiRuleRepair::execute(){ + AiInterface *aiInterface= ai->getAiInterface(); + const Unit *damagedUnit= aiInterface->getMyUnit(damagedUnitIndex); + + //find a repairer and issue command + for(int i=0; igetMyUnitCount(); ++i){ + const Unit *u= aiInterface->getMyUnit(i); + const RepairCommandType *rct= static_cast(u->getType()->getFirstCtOfClass(ccRepair)); + if(rct!=NULL && (u->getCurrSkill()->getClass()==scStop || u->getCurrSkill()->getClass()==scMove)){ + if(rct->isRepairableUnitType(damagedUnit->getType())){ + aiInterface->giveCommand(i, rct, damagedUnit->getPos()); + aiInterface->printLog(3, "Repairing order issued"); + return; + } + } + } +} + +// ===================================================== +// class AiRuleReturnBase +// ===================================================== + +AiRuleReturnBase::AiRuleReturnBase(Ai *ai): + AiRule(ai) +{ + stoppedUnitIndex= -1; +} + +bool AiRuleReturnBase::test(){ + return ai->findAbleUnit(&stoppedUnitIndex, ccMove, true); +} + +void AiRuleReturnBase::execute(){ + ai->returnBase(stoppedUnitIndex); +} + +// ===================================================== +// class AiRuleMassiveAttack +// ===================================================== + +AiRuleMassiveAttack::AiRuleMassiveAttack(Ai *ai): + AiRule(ai) +{ +} + +bool AiRuleMassiveAttack::test(){ + + if(ai->isStableBase()){ + ultraAttack= false; + return ai->beingAttacked(attackPos, field, INT_MAX); + } + else{ + ultraAttack= true; + return ai->beingAttacked(attackPos, field, baseRadius); + } +} + +void AiRuleMassiveAttack::execute(){ + ai->massiveAttack(attackPos, field, ultraAttack); +} +// ===================================================== +// class AiRuleAddTasks +// ===================================================== + +AiRuleAddTasks::AiRuleAddTasks(Ai *ai): + AiRule(ai) +{ +} + +bool AiRuleAddTasks::test(){ + return !ai->anyTask() || ai->getCountOfClass(ucWorker)<4; +} + +void AiRuleAddTasks::execute(){ + int buildingCount= ai->getCountOfClass(ucBuilding); + int warriorCount= ai->getCountOfClass(ucWarrior); + int workerCount= ai->getCountOfClass(ucWorker); + int upgradeCount= ai->getAiInterface()->getMyUpgradeCount(); + + float buildingRatio= ai->getRatioOfClass(ucBuilding); + float warriorRatio= ai->getRatioOfClass(ucWarrior); + float workerRatio= ai->getRatioOfClass(ucWorker); + + //standard tasks + + //emergency workers + if(workerCount<4){ + ai->addPriorityTask(new ProduceTask(ucWorker)); + } + else{ + if(ai->getAiInterface()->getControlType()==ctCpuMega) + { + //workers + if(workerCount<5) ai->addTask(new ProduceTask(ucWorker)); + if(workerCount<10) ai->addTask(new ProduceTask(ucWorker)); + if(workerRatio<0.20) ai->addTask(new ProduceTask(ucWorker)); + if(workerRatio<0.30) ai->addTask(new ProduceTask(ucWorker)); + + //warriors + if(warriorCount<10) ai->addTask(new ProduceTask(ucWarrior)); + if(warriorRatio<0.20) ai->addTask(new ProduceTask(ucWarrior)); + if(warriorRatio<0.30) ai->addTask(new ProduceTask(ucWarrior)); + if(workerCount>=10) ai->addTask(new ProduceTask(ucWarrior)); + if(workerCount>=15) ai->addTask(new ProduceTask(ucWarrior)); + if(warriorCountminWarriors+2) + { + ai->addTask(new ProduceTask(ucWarrior)); + if( buildingCount>9 ) + { + ai->addTask(new ProduceTask(ucWarrior)); + ai->addTask(new ProduceTask(ucWarrior)); + } + if( buildingCount>12 ) + { + ai->addTask(new ProduceTask(ucWarrior)); + ai->addTask(new ProduceTask(ucWarrior)); + } + } + + //buildings + if(buildingCount<6 || buildingRatio<0.20) ai->addTask(new BuildTask()); + if(buildingCount<10 && workerCount>12) ai->addTask(new BuildTask()); + //upgrades + if(upgradeCount==0 && workerCount>5) ai->addTask(new UpgradeTask()); + if(upgradeCount==1 && workerCount>10) ai->addTask(new UpgradeTask()); + if(upgradeCount==2 && workerCount>15) ai->addTask(new UpgradeTask()); + if(ai->isStableBase()) ai->addTask(new UpgradeTask()); + } + else if(ai->getAiInterface()->getControlType()==ctCpuEasy) + {// Easy CPU + //workers + if(workerCountaddTask(new ProduceTask(ucWorker)); + if(workerCount>5 && workerRatio<0.20) ai->addTask(new ProduceTask(ucWorker)); + + //warriors + if(warriorCount<10) ai->addTask(new ProduceTask(ucWarrior)); + if(warriorRatio<0.20) ai->addTask(new ProduceTask(ucWarrior)); + if(warriorRatio<0.30) ai->addTask(new ProduceTask(ucWarrior)); + if(workerCount>=10) ai->addTask(new ProduceTask(ucWarrior)); + if(workerCount>=15) ai->addTask(new ProduceTask(ucWarrior)); + + //buildings + if(buildingCount<6 || buildingRatio<0.20) ai->addTask(new BuildTask()); + if(buildingCount<10 && ai->isStableBase()) ai->addTask(new BuildTask()); + + //upgrades + if(upgradeCount==0 && workerCount>6) ai->addTask(new UpgradeTask()); + if(upgradeCount==1 && workerCount>7) ai->addTask(new UpgradeTask()); + if(upgradeCount==2 && workerCount>9) ai->addTask(new UpgradeTask()); + //if(ai->isStableBase()) ai->addTask(new UpgradeTask()); + } + else + {// normal CPU / UltraCPU ... + //workers + if(workerCount<5) ai->addTask(new ProduceTask(ucWorker)); + if(workerCount<10) ai->addTask(new ProduceTask(ucWorker)); + if(workerRatio<0.20) ai->addTask(new ProduceTask(ucWorker)); + if(workerRatio<0.30) ai->addTask(new ProduceTask(ucWorker)); + + //warriors + if(warriorCount<10) ai->addTask(new ProduceTask(ucWarrior)); + if(warriorRatio<0.20) ai->addTask(new ProduceTask(ucWarrior)); + if(warriorRatio<0.30) ai->addTask(new ProduceTask(ucWarrior)); + if(workerCount>=10) ai->addTask(new ProduceTask(ucWarrior)); + if(workerCount>=15) ai->addTask(new ProduceTask(ucWarrior)); + + //buildings + if(buildingCount<6 || buildingRatio<0.20) ai->addTask(new BuildTask()); + if(buildingCount<10 && workerCount>12) ai->addTask(new BuildTask()); + + //upgrades + if(upgradeCount==0 && workerCount>5) ai->addTask(new UpgradeTask()); + if(upgradeCount==1 && workerCount>10) ai->addTask(new UpgradeTask()); + if(upgradeCount==2 && workerCount>15) ai->addTask(new UpgradeTask()); + if(ai->isStableBase()) ai->addTask(new UpgradeTask()); + } + } +} + +// ===================================================== +// class AiRuleBuildOneFarm +// ===================================================== + +AiRuleBuildOneFarm::AiRuleBuildOneFarm(Ai *ai): + AiRule(ai) +{ +} + +bool AiRuleBuildOneFarm::test(){ + AiInterface *aiInterface= ai->getAiInterface(); + + //for all units + for(int i=0; igetMyFactionType()->getUnitTypeCount(); ++i){ + const UnitType *ut= aiInterface->getMyFactionType()->getUnitType(i); + + //for all production commands + for(int j=0; jgetCommandTypeCount(); ++j){ + const CommandType *ct= ut->getCommandType(j); + if(ct->getClass()==ccProduce){ + const UnitType *producedType= static_cast(ct)->getProducedUnit(); + + //for all resources + for(int k=0; kgetCostCount(); ++k){ + const Resource *r= producedType->getCost(k); + + //find a food producer in the farm produced units + if(r->getAmount()<0 && r->getType()->getClass()==rcConsumable && ai->getCountOfType(ut)==0){ + farm= ut; + return true; + } + } + } + } + } + return false; +} + +void AiRuleBuildOneFarm::execute(){ + ai->addPriorityTask(new BuildTask(farm)); +} + +// ===================================================== +// class AiRuleProduceResourceProducer +// ===================================================== + +AiRuleProduceResourceProducer::AiRuleProduceResourceProducer(Ai *ai): + AiRule(ai) +{ + interval= shortInterval; +} + +bool AiRuleProduceResourceProducer::test(){ + //emergency tasks: resource buildings + AiInterface *aiInterface= ai->getAiInterface(); + + //consumables first + for(int i=0; igetTechTree()->getResourceTypeCount(); ++i){ + rt= aiInterface->getTechTree()->getResourceType(i); + const Resource *r= aiInterface->getResource(rt); + if(rt->getClass()==rcConsumable && r->getBalance()<0){ + interval= longInterval; + return true; + + } + } + + //statics second + for(int i=0; igetTechTree()->getResourceTypeCount(); ++i){ + rt= aiInterface->getTechTree()->getResourceType(i); + const Resource *r= aiInterface->getResource(rt); + if(rt->getClass()==rcStatic && r->getAmount()addPriorityTask(new ProduceTask(rt)); + ai->addTask(new BuildTask(rt)); +} + +// ===================================================== +// class AiRuleProduce +// ===================================================== + +AiRuleProduce::AiRuleProduce(Ai *ai): + AiRule(ai) +{ + produceTask= NULL; +} + +bool AiRuleProduce::test(){ + const Task *task= ai->getTask(); + + if(task==NULL || task->getClass()!=tcProduce){ + return false; + } + + produceTask= static_cast(task); + return true; +} + +void AiRuleProduce::execute(){ + if(produceTask!=NULL){ + + //generic produce task, produce random unit that has the skill or produces the resource + if(produceTask->getUnitType()==NULL){ + produceGeneric(produceTask); + } + + //specific produce task, produce if possible, retry if not enough resources + else{ + produceSpecific(produceTask); + } + + //remove the task + ai->removeTask(produceTask); + } +} + +void AiRuleProduce::produceGeneric(const ProduceTask *pt){ + typedef vector UnitTypes; + UnitTypes ableUnits; + AiInterface *aiInterface= ai->getAiInterface(); + + //for each unit, produce it if possible + for(int i=0; igetMyUnitCount(); ++i){ + + //for each command + const UnitType *ut= aiInterface->getMyUnit(i)->getType(); + for(int j=0; jgetCommandTypeCount(); ++j){ + const CommandType *ct= ut->getCommandType(j); + + //if the command is produce + if(ct->getClass()==ccProduce || ct->getClass()==ccMorph){ + + const UnitType *producedUnit= static_cast(ct->getProduced()); + bool produceIt= false; + + //if the unit produces the resource + if(pt->getResourceType()!=NULL){ + const Resource *r= producedUnit->getCost(pt->getResourceType()); + if(r!=NULL && r->getAmount()<0){ + produceIt= true; + } + } + + else{ + //if the unit is from the right class + if(producedUnit->isOfClass(pt->getUnitClass())){ + if(aiInterface->reqsOk(ct) && aiInterface->reqsOk(producedUnit)){ + produceIt= true; + } + } + } + + if(produceIt){ + //if the unit is not already on the list + if(find(ableUnits.begin(), ableUnits.end(), producedUnit)==ableUnits.end()){ + ableUnits.push_back(producedUnit); + } + } + } + } + } + + //add specific produce task + if(!ableUnits.empty()){ + + //priority for non produced units + for(unsigned int i=0; igetCountOfType(ableUnits[i])==0){ + if(ai->getRandom()->randRange(0, 1)==0){ + ai->addTask(new ProduceTask(ableUnits[i])); + return; + } + } + } + + //normal case + ai->addTask(new ProduceTask(ableUnits[ai->getRandom()->randRange(0, ableUnits.size()-1)])); + } +} + +void AiRuleProduce::produceSpecific(const ProduceTask *pt){ + + AiInterface *aiInterface= ai->getAiInterface(); + + //if unit meets requirements + if(aiInterface->reqsOk(pt->getUnitType())){ + + //if unit doesnt meet resources retry + if(!aiInterface->checkCosts(pt->getUnitType())){ + ai->retryTask(pt); + return; + } + + //produce specific unit + vector producers; + const CommandType *defCt= NULL; + + //for each unit + for(int i=0; igetMyUnitCount(); ++i){ + + //for each command + const UnitType *ut= aiInterface->getMyUnit(i)->getType(); + for(int j=0; jgetCommandTypeCount(); ++j){ + const CommandType *ct= ut->getCommandType(j); + + //if the command is produce + if(ct->getClass()==ccProduce || ct->getClass()==ccMorph){ + const UnitType *producedUnit= static_cast(ct->getProduced()); + + //if units match + if(producedUnit == pt->getUnitType()){ + if(aiInterface->reqsOk(ct)){ + defCt= ct; + producers.push_back(i); + } + } + } + } + } + + //produce from random producer + if(!producers.empty()){ + if(aiInterface->getControlType()==ctCpuMega) + {// mega cpu trys to balance the commands to the producers + int randomstart=ai->getRandom()->randRange(0, producers.size()-1); + int lowestCommandCount=1000000; + int currentProducerIndex=producers[randomstart]; + int bestIndex=-1; + int besti=0; + int currentCommandCount=0; +// printf("command sizes: "); + for(unsigned int i=randomstart; igetMyUnit(currentProducerIndex)->getCommandSize()); + currentCommandCount=aiInterface->getMyUnit(currentProducerIndex)->getCommandSize(); + if( currentCommandCount==1 && + aiInterface->getMyUnit(currentProducerIndex)->getCurrCommand()->getCommandType()->getClass()==ccStop) + {// special for non buildings + currentCommandCount=0; + } + if(lowestCommandCount>currentCommandCount) + { + lowestCommandCount=aiInterface->getMyUnit(currentProducerIndex)->getCommandSize(); + bestIndex=currentProducerIndex; + besti=i%(producers.size()); + } + } + +// printf("\ncurrent team=%d producercount=%d bestindex=%d commandsize=%d besti=%d\n", +// aiInterface->getMyUnit(currentProducerIndex)->getTeam(), +// producers.size(), +// bestIndex, +// aiInterface->getMyUnit(currentProducerIndex)->getCommandSize(), +// besti +// ); + + if( aiInterface->getMyUnit(bestIndex)->getCommandSize()>2) + { + // maybe we need another producer of this kind if possible! + if(aiInterface->reqsOk(aiInterface->getMyUnit(bestIndex)->getType())) + { + if(ai->getCountOfClass(ucBuilding)>5) + ai->addTask(new BuildTask(aiInterface->getMyUnit(bestIndex)->getType())); + } + // need to calculte another producer, maybe its better to produce another warrior with another producer + vector backupProducers; +// printf("start ----- need to calculate a new producer! \n"); + // find another producer unit which is free and produce any kind of warrior. + //for each unit + for(int i=0; igetMyUnitCount(); ++i){ + const UnitType *ut= aiInterface->getMyUnit(i)->getType(); +// printf("unit%d\n",i); + //for each command + for(int j=0; jgetCommandTypeCount(); ++j){ + const CommandType *ct= ut->getCommandType(j); +// printf("command%d\n",j); + //if the command is produce + if(ct->getClass()==ccProduce) + { + const UnitType *unitType= static_cast(ct->getProduced()); + if(unitType->hasSkillClass(scAttack) && !unitType->hasCommandClass(ccHarvest) && aiInterface->reqsOk(ct)) + {//this can produce a warrior + backupProducers.push_back(i); + } + } + } + } + if(!backupProducers.empty()) + { +// printf("Number of possible backupproducers %d",backupProducers.size()); + + vector productionCommandIndexes; + int randomstart=ai->getRandom()->randRange(0, backupProducers.size()-1); + int lowestCommandCount=1000000; + int currentProducerIndex=backupProducers[randomstart]; + int bestIndex=-1; + int currentCommandCount=0; +// printf("command sizes: "); + for(unsigned int i=randomstart; igetMyUnit(currentProducerIndex)->getCommandSize()); + currentCommandCount=aiInterface->getMyUnit(currentProducerIndex)->getCommandSize(); + if( currentCommandCount==1 && + aiInterface->getMyUnit(currentProducerIndex)->getCurrCommand()->getCommandType()->getClass()==ccStop) + {// special for non buildings + currentCommandCount=0; + } + if(lowestCommandCount>currentCommandCount) + { + lowestCommandCount=currentCommandCount; + bestIndex=currentProducerIndex; + if(lowestCommandCount==0) break; + } + } + // a good producer is found, lets choose a warrior production + const UnitType *ut=aiInterface->getMyUnit(bestIndex)->getType(); + for(int j=0; jgetCommandTypeCount(); ++j){ + const CommandType *ct= ut->getCommandType(j); + + //if the command is produce + if(ct->getClass()==ccProduce) + { + const UnitType *unitType= static_cast(ct->getProduced()); + if(unitType->hasSkillClass(scAttack) && !unitType->hasCommandClass(ccHarvest) && aiInterface->reqsOk(ct)) + {//this can produce a warrior + productionCommandIndexes.push_back(j); + } + } + } + int commandIndex=productionCommandIndexes[ai->getRandom()->randRange(0, productionCommandIndexes.size()-1)]; + aiInterface->giveCommand(bestIndex, ut->getCommandType(commandIndex)); +// printf("\nbestindex=%d\n",bestIndex); +// printf("end ----- need to calculate a new producer! \n"); + } + else + {// do it like normal CPU + aiInterface->giveCommand(bestIndex, defCt); + } + } + else + { + if(currentCommandCount==0) + { + aiInterface->giveCommand(bestIndex, defCt); + } + aiInterface->giveCommand(bestIndex, defCt); + } + } + else + { + int producerIndex= producers[ai->getRandom()->randRange(0, producers.size()-1)]; + aiInterface->giveCommand(producerIndex, defCt); + } + } + } +} + +// ======================================== +// class AiRuleBuild +// ======================================== + +AiRuleBuild::AiRuleBuild(Ai *ai): + AiRule(ai) +{ + buildTask= NULL; +} + +bool AiRuleBuild::test(){ + const Task *task= ai->getTask(); + + if(task==NULL || task->getClass()!=tcBuild){ + return false; + } + + buildTask= static_cast(task); + return true; +} + + +void AiRuleBuild::execute(){ + + if(buildTask!=NULL){ + + //generic build task, build random building that can be built + if(buildTask->getUnitType()==NULL){ + buildGeneric(buildTask); + } + //specific building task, build if possible, retry if not enough resources or not position + else{ + buildSpecific(buildTask); + } + + //remove the task + ai->removeTask(buildTask); + } +} + +void AiRuleBuild::buildGeneric(const BuildTask *bt){ + + //find buildings that can be built + AiInterface *aiInterface= ai->getAiInterface(); + typedef vector UnitTypes; + UnitTypes buildings; + + //for each unit + for(int i=0; igetMyUnitCount(); ++i){ + + //for each command + const UnitType *ut= aiInterface->getMyUnit(i)->getType(); + for(int j=0; jgetCommandTypeCount(); ++j){ + const CommandType *ct= ut->getCommandType(j); + + //if the command is build + if(ct->getClass()==ccBuild){ + const BuildCommandType *bct= static_cast(ct); + + //for each building + for(int k=0; kgetBuildingCount(); ++k){ + const UnitType *building= bct->getBuilding(k); + if(aiInterface->reqsOk(bct) && aiInterface->reqsOk(building)){ + + //if any building, or produces resource + const ResourceType *rt= bt->getResourceType(); + const Resource *cost= building->getCost(rt); + if(rt==NULL || (cost!=NULL && cost->getAmount()<0)){ + buildings.push_back(building); + } + } + } + } + } + } + + //add specific build task + buildBestBuilding(buildings); +} + +void AiRuleBuild::buildBestBuilding(const vector &buildings){ + + if(!buildings.empty()){ + + //build the least built building + bool buildingFound= false; + for(int i=0; i<10 && !buildingFound; ++i){ + + if(i>0){ + + //Defensive buildings have priority + for(int j=0; jgetCountOfType(building)<=i+1 && isDefensive(building)) + { + ai->addTask(new BuildTask(building)); + buildingFound= true; + } + } + + //Warrior producers next + for(unsigned int j=0; jgetCountOfType(building)<=i+1 && isWarriorProducer(building)) + { + ai->addTask(new BuildTask(building)); + buildingFound= true; + } + } + + //Resource producers next + for(unsigned int j=0; jgetCountOfType(building)<=i+1 && isResourceProducer(building)) + { + ai->addTask(new BuildTask(building)); + buildingFound= true; + } + } + } + + //Any building + for(unsigned int j=0; jgetCountOfType(building)<=i) + { + ai->addTask(new BuildTask(building)); + buildingFound= true; + } + } + } + } +} + +void AiRuleBuild::buildSpecific(const BuildTask *bt){ + AiInterface *aiInterface= ai->getAiInterface(); + //if reqs ok + if(aiInterface->reqsOk(bt->getUnitType())){ + + //retry if not enough resources + if(!aiInterface->checkCosts(bt->getUnitType())){ + ai->retryTask(bt); + return; + } + + vector builders; + const BuildCommandType *defBct= NULL; + + //for each unit + for(int i=0; igetMyUnitCount(); ++i){ + + //if the unit is not going to build + const Unit *u= aiInterface->getMyUnit(i); + if(!u->anyCommand() || u->getCurrCommand()->getCommandType()->getClass()!=ccBuild){ + + //for each command + const UnitType *ut= aiInterface->getMyUnit(i)->getType(); + for(int j=0; jgetCommandTypeCount(); ++j){ + const CommandType *ct= ut->getCommandType(j); + + //if the command is build + if(ct->getClass()==ccBuild){ + const BuildCommandType *bct= static_cast(ct); + + //for each building + for(int k=0; kgetBuildingCount(); ++k){ + const UnitType *building= bct->getBuilding(k); + + //if building match + if(bt->getUnitType()==building){ + if(aiInterface->reqsOk(bct)){ + builders.push_back(i); + defBct= bct; + } + } + } + } + } + } + } + + //use random builder to build + if(!builders.empty()){ + int builderIndex= builders[ai->getRandom()->randRange(0, builders.size()-1)]; + Vec2i pos; + Vec2i searchPos= bt->getForcePos()? bt->getPos(): ai->getRandomHomePosition(); + + //if free pos give command, else retry + if(ai->findPosForBuilding(bt->getUnitType(), searchPos, pos)){ + aiInterface->giveCommand(builderIndex, defBct, pos, bt->getUnitType()); + } + else{ + ai->retryTask(bt); + return; + } + } + } +} + +bool AiRuleBuild::isDefensive(const UnitType *building){ + return building->hasSkillClass(scAttack); +} + +bool AiRuleBuild::isResourceProducer(const UnitType *building){ + for(int i= 0; igetCostCount(); i++){ + if(building->getCost(i)->getAmount()<0){ + return true; + } + } + return false; +} + +bool AiRuleBuild::isWarriorProducer(const UnitType *building){ + for(int i= 0; i < building->getCommandTypeCount(); i++){ + const CommandType *ct= building->getCommandType(i); + if(ct->getClass() == ccProduce){ + const UnitType *ut= static_cast(ct)->getProducedUnit(); + + if(ut->isOfClass(ucWarrior)){ + return true; + } + } + } + return false; +} + +// ======================================== +// class AiRuleUpgrade +// ======================================== + +AiRuleUpgrade::AiRuleUpgrade(Ai *ai): + AiRule(ai) +{ + upgradeTask= NULL; +} + +bool AiRuleUpgrade::test(){ + const Task *task= ai->getTask(); + + if(task==NULL || task->getClass()!=tcUpgrade){ + return false; + } + + upgradeTask= static_cast(task); + return true; +} + +void AiRuleUpgrade::execute(){ + + //upgrade any upgrade + if(upgradeTask->getUpgradeType()==NULL){ + upgradeGeneric(upgradeTask); + } + //upgrade specific upgrade + else{ + upgradeSpecific(upgradeTask); + } + + //remove the task + ai->removeTask(upgradeTask); +} + +void AiRuleUpgrade::upgradeGeneric(const UpgradeTask *upgt){ + + typedef vector UpgradeTypes; + AiInterface *aiInterface= ai->getAiInterface(); + + //find upgrades that can be upgraded + UpgradeTypes upgrades; + + //for each upgrade, upgrade it if possible + for(int i=0; igetMyUnitCount(); ++i){ + + //for each command + const UnitType *ut= aiInterface->getMyUnit(i)->getType(); + for(int j=0; jgetCommandTypeCount(); ++j){ + const CommandType *ct= ut->getCommandType(j); + + //if the command is upgrade + if(ct->getClass()==ccUpgrade){ + const UpgradeCommandType *upgct= static_cast(ct); + const UpgradeType *upgrade= upgct->getProducedUpgrade(); + if(aiInterface->reqsOk(upgct)){ + upgrades.push_back(upgrade); + } + } + } + } + + //add specific upgrade task + if(!upgrades.empty()){ + ai->addTask(new UpgradeTask(upgrades[ai->getRandom()->randRange(0, upgrades.size()-1)])); + } +} + +void AiRuleUpgrade::upgradeSpecific(const UpgradeTask *upgt){ + + AiInterface *aiInterface= ai->getAiInterface(); + + //if reqs ok + if(aiInterface->reqsOk(upgt->getUpgradeType())){ + + //if resources dont meet retry + if(!aiInterface->checkCosts(upgt->getUpgradeType())){ + ai->retryTask(upgt); + return; + } + + //for each unit + for(int i=0; igetMyUnitCount(); ++i){ + + //for each command + const UnitType *ut= aiInterface->getMyUnit(i)->getType(); + for(int j=0; jgetCommandTypeCount(); ++j){ + const CommandType *ct= ut->getCommandType(j); + + //if the command is upgrade + if(ct->getClass()==ccUpgrade){ + const UpgradeCommandType *uct= static_cast(ct); + const UpgradeType *producedUpgrade= uct->getProducedUpgrade(); + + //if upgrades match + if(producedUpgrade == upgt->getUpgradeType()){ + if(aiInterface->reqsOk(uct)){ + aiInterface->giveCommand(i, uct); + } + } + } + } + } + + } +} + +// ======================================== +// class AiRuleExpand +// ======================================== + +AiRuleExpand::AiRuleExpand(Ai *ai): + AiRule(ai) +{ + storeType= NULL; +} + +bool AiRuleExpand::test(){ + AiInterface *aiInterface = ai->getAiInterface(); + + for(int i= 0; igetTechTree()->getResourceTypeCount(); ++i){ + const ResourceType *rt = aiInterface->getTechTree()->getResourceType(i); + + if(rt->getClass()==rcTech){ + + // If any resource sighted + if(aiInterface->getNearestSightedResource(rt, aiInterface->getHomeLocation(), expandPos)){ + + int minDistance= INT_MAX; + storeType= NULL; + + //If there is no close store + for(int j=0; jgetMyUnitCount(); ++j){ + const Unit *u= aiInterface->getMyUnit(j); + const UnitType *ut= aiInterface->getMyUnit(j)->getType(); + + // If this building is a store + if(ut->getStore(rt)>0){ + storeType = ut; + int distance= static_cast (u->getPos().dist(expandPos)); + + if(distance < minDistance){ + minDistance = distance; + } + } + } + + if(minDistance>expandDistance) + { + return true; + } + } + else{ + // send patrol to look for resource + ai->sendScoutPatrol(); + } + } + } + + return false; +} + +void AiRuleExpand::execute(){ + ai->addExpansion(expandPos); + ai->addPriorityTask(new BuildTask(storeType, expandPos)); +} + +}}//end namespace diff --git a/source/glest_game/ai/ai_rule.h b/source/glest_game/ai/ai_rule.h new file mode 100644 index 00000000..3f1e3984 --- /dev/null +++ b/source/glest_game/ai/ai_rule.h @@ -0,0 +1,314 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_AIRULE_H_ +#define _GLEST_GAME_AIRULE_H_ + +#include + +#include "vec.h" +#include "skill_type.h" + +using std::string; + +using Shared::Graphics::Vec2i; + +namespace Glest{ namespace Game{ + +class Ai; +class Unit; +class UnitType; +class ProduceTask; +class BuildTask; +class UpgradeTask; +class ResourceType; + +// ===================================================== +// class AiRule +// +/// An action that the AI will perform periodically +/// if the test succeeds +// ===================================================== + +class AiRule{ +protected: + Ai *ai; + +public: + AiRule(Ai *ai); + virtual ~AiRule() {} + + virtual int getTestInterval() const= 0; //in milliseconds + virtual string getName() const= 0; + + virtual bool test()= 0; + virtual void execute()= 0; +}; + +// ===================================================== +// class AiRuleWorkerHarvest +// ===================================================== + +class AiRuleWorkerHarvest: public AiRule{ +private: + int stoppedWorkerIndex; + +public: + AiRuleWorkerHarvest(Ai *ai); + + virtual int getTestInterval() const {return 2000;} + virtual string getName() const {return "Worker stopped => Order worker to harvest";} + + virtual bool test(); + virtual void execute(); +}; + +// ===================================================== +// class AiRuleRefreshHarvester +// ===================================================== + +class AiRuleRefreshHarvester: public AiRule{ +private: + int workerIndex; + +public: + AiRuleRefreshHarvester(Ai *ai); + + virtual int getTestInterval() const {return 20000;} + virtual string getName() const {return "Worker reasigned to needed resource";} + + virtual bool test(); + virtual void execute(); +}; + +// ===================================================== +// class AiRuleScoutPatrol +// ===================================================== + +class AiRuleScoutPatrol: public AiRule{ +public: + AiRuleScoutPatrol(Ai *ai); + + virtual int getTestInterval() const {return 10000;} + virtual string getName() const {return "Base is stable => Send scout patrol";} + + virtual bool test(); + virtual void execute(); +}; + +// ===================================================== +// class AiRuleRepair +// ===================================================== + +class AiRuleRepair: public AiRule{ +private: + int damagedUnitIndex; + +public: + AiRuleRepair(Ai *ai); + + virtual int getTestInterval() const {return 10000;} + virtual string getName() const {return "Building Damaged => Repair";} + + virtual bool test(); + virtual void execute(); +}; + +// ===================================================== +// class AiRuleReturnBase +// ===================================================== + +class AiRuleReturnBase: public AiRule{ +private: + int stoppedUnitIndex; +public: + AiRuleReturnBase(Ai *ai); + + virtual int getTestInterval() const {return 5000;} + virtual string getName() const {return "Stopped unit => Order return base";} + + virtual bool test(); + virtual void execute(); +}; + +// ===================================================== +// class AiRuleMassiveAttack +// ===================================================== + +class AiRuleMassiveAttack: public AiRule{ +private: + static const int baseRadius= 25; + +private: + Vec2i attackPos; + Field field; + bool ultraAttack; + +public: + AiRuleMassiveAttack(Ai *ai); + + virtual int getTestInterval() const {return 1000;} + virtual string getName() const {return "Unit under attack => Order massive attack";} + + virtual bool test(); + virtual void execute(); +}; + +// ===================================================== +// class AiRuleAddTasks +// ===================================================== + +class AiRuleAddTasks: public AiRule{ +public: + AiRuleAddTasks(Ai *ai); + + virtual int getTestInterval() const {return 5000;} + virtual string getName() const {return "Tasks empty => Add tasks";} + + virtual bool test(); + virtual void execute(); +}; + +// ===================================================== +// class AiRuleBuildOneFarm +// ===================================================== + +class AiRuleBuildOneFarm: public AiRule{ +private: + const UnitType *farm; + +public: + AiRuleBuildOneFarm(Ai *ai); + + virtual int getTestInterval() const {return 10000;} + virtual string getName() const {return "No farms => Build one";} + + virtual bool test(); + virtual void execute(); +}; + +// ===================================================== +// class AiRuleProduceResourceProducer +// ===================================================== + +class AiRuleProduceResourceProducer: public AiRule{ +private: + static const int minStaticResources= 20; + static const int longInterval= 60000; + static const int shortInterval= 5000; + const ResourceType *rt; + int interval; + +public: + AiRuleProduceResourceProducer(Ai *ai); + + virtual int getTestInterval() const {return interval;} + virtual string getName() const {return "No resources => Build Resource Producer";} + + virtual bool test(); + virtual void execute(); +}; + +// ===================================================== +// class AiRuleProduce +// ===================================================== + +class AiRuleProduce: public AiRule{ +private: + const ProduceTask *produceTask; + +public: + AiRuleProduce(Ai *ai); + + virtual int getTestInterval() const {return 2000;} + virtual string getName() const {return "Performing produce task";} + + virtual bool test(); + virtual void execute(); + +private: + void produceGeneric(const ProduceTask *pt); + void produceSpecific(const ProduceTask *pt); +}; +// ===================================================== +// class AiRuleBuild +// ===================================================== + +class AiRuleBuild: public AiRule{ +private: + const BuildTask *buildTask; + +public: + AiRuleBuild(Ai *ai); + + virtual int getTestInterval() const {return 2000;} + virtual string getName() const {return "Performing build task";} + + virtual bool test(); + virtual void execute(); + +private: + void buildGeneric(const BuildTask *bt); + void buildSpecific(const BuildTask *bt); + void buildBestBuilding(const vector &buildings); + + bool isDefensive(const UnitType *building); + bool isResourceProducer(const UnitType *building); + bool isWarriorProducer(const UnitType *building); +}; + +// ===================================================== +// class AiRuleUpgrade +// ===================================================== + +class AiRuleUpgrade: public AiRule{ +private: + const UpgradeTask *upgradeTask; + +public: + AiRuleUpgrade(Ai *ai); + + virtual int getTestInterval() const {return 2000;} + virtual string getName() const {return "Performing upgrade task";} + + virtual bool test(); + virtual void execute(); + +private: + void upgradeSpecific(const UpgradeTask *upgt); + void upgradeGeneric(const UpgradeTask *upgt); +}; + +// ===================================================== +// class AiRuleExpand +// ===================================================== + +class AiRuleExpand: public AiRule{ +private: + static const int expandDistance= 30; + +private: + Vec2i expandPos; + const UnitType *storeType; + +public: + AiRuleExpand(Ai *ai); + + virtual int getTestInterval() const {return 30000;} + virtual string getName() const {return "Expanding";} + + virtual bool test(); + virtual void execute(); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/ai/path_finder.cpp b/source/glest_game/ai/path_finder.cpp new file mode 100644 index 00000000..23f6b49d --- /dev/null +++ b/source/glest_game/ai/path_finder.cpp @@ -0,0 +1,304 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "path_finder.h" + +#include +#include + +#include "config.h" +#include "map.h" +#include "unit.h" +#include "unit_type.h" +#include "leak_dumper.h" + +using namespace std; +using namespace Shared::Graphics; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class PathFinder +// ===================================================== + +// ===================== PUBLIC ======================== + +const int PathFinder::maxFreeSearchRadius= 10; +const int PathFinder::pathFindNodesMax= 400; +const int PathFinder::pathFindRefresh= 10; + + +PathFinder::PathFinder(){ + nodePool= NULL; +} + +PathFinder::PathFinder(const Map *map){ + init(map); + nodePool= NULL; +} + +void PathFinder::init(const Map *map){ + nodePool= new Node[pathFindNodesMax]; + this->map= map; +} + +PathFinder::~PathFinder(){ + delete [] nodePool; +} + +PathFinder::TravelState PathFinder::findPath(Unit *unit, const Vec2i &finalPos){ + + //route cache + UnitPath *path= unit->getPath(); + if(finalPos==unit->getPos()){ + //if arrived + unit->setCurrSkill(scStop); + return tsArrived; + } + else if(!path->isEmpty()){ + //route cache + Vec2i pos= path->pop(); + if(map->canMove(unit, unit->getPos(), pos)){ + unit->setTargetPos(pos); + return tsOnTheWay; + } + } + + //route cache miss + TravelState ts= aStar(unit, finalPos); + + //post actions + switch(ts){ + case tsBlocked: + case tsArrived: + unit->setCurrSkill(scStop); + break; + case tsOnTheWay: + Vec2i pos= path->pop(); + if(map->canMove(unit, unit->getPos(), pos)){ + unit->setTargetPos(pos); + } + else{ + unit->setCurrSkill(scStop); + return tsBlocked; + } + break; + } + return ts; +} + +// ==================== PRIVATE ==================== + +//route a unit using A* algorithm +PathFinder::TravelState PathFinder::aStar(Unit *unit, const Vec2i &targetPos){ + + nodePoolCount= 0; + const Vec2i finalPos= computeNearestFreePos(unit, targetPos); + + //if arrived + if(finalPos==unit->getPos()){ + return tsArrived; + } + + //path find algorithm + + //a) push starting pos into openNodes + Node *firstNode= newNode(); + assert(firstNode!=NULL);; + firstNode->next= NULL; + firstNode->prev= NULL; + firstNode->pos= unit->getPos(); + firstNode->heuristic= heuristic(unit->getPos(), finalPos); + firstNode->exploredCell= true; + openNodes.push_back(firstNode); + + //b) loop + bool pathFound= true; + bool nodeLimitReached= false; + Node *node= NULL; + + while(!nodeLimitReached){ + + //b1) is open nodes is empty => failed to find the path + if(openNodes.empty()){ + pathFound= false; + break; + } + + //b2) get the minimum heuristic node + Nodes::iterator it = minHeuristic(); + node= *it; + + //b3) if minHeuristic is the finalNode, or the path is no more explored => path was found + if(node->pos==finalPos || !node->exploredCell){ + pathFound= true; + break; + } + + //b4) move this node from closedNodes to openNodes + //add all succesors that are not in closedNodes or openNodes to openNodes + closedNodes.push_back(node); + openNodes.erase(it); + for(int i=-1; i<=1 && !nodeLimitReached; ++i){ + for(int j=-1; j<=1 && !nodeLimitReached; ++j){ + Vec2i sucPos= node->pos + Vec2i(i, j); + if(!openPos(sucPos) && map->aproxCanMove(unit, node->pos, sucPos)){ + //if node is not open and canMove then generate another node + Node *sucNode= newNode(); + if(sucNode!=NULL){ + sucNode->pos= sucPos; + sucNode->heuristic= heuristic(sucNode->pos, finalPos); + sucNode->prev= node; + sucNode->next= NULL; + sucNode->exploredCell= map->getSurfaceCell(Map::toSurfCoords(sucPos))->isExplored(unit->getTeam()); + openNodes.push_back(sucNode); + } + else{ + nodeLimitReached= true; + } + } + } + } + }//while + + Node *lastNode= node; + + //if consumed all nodes find best node (to avoid strage behaviour) + if(nodeLimitReached){ + for(Nodes::iterator it= closedNodes.begin(); it!=closedNodes.end(); ++it){ + if((*it)->heuristic < lastNode->heuristic){ + lastNode= *it; + } + } + } + + //check results of path finding + TravelState ts; + UnitPath *path= unit->getPath(); + if(pathFound==false || lastNode==firstNode){ + //blocked + ts= tsBlocked; + path->incBlockCount(); + } + else { + //on the way + ts= tsOnTheWay; + + //build next pointers + Node *currNode= lastNode; + while(currNode->prev!=NULL){ + currNode->prev->next= currNode; + currNode= currNode->prev; + } + //store path + path->clear(); + + currNode= firstNode; + for(int i=0; currNode->next!=NULL && inext, i++){ + path->push(currNode->next->pos); + } + } + + //clean nodes + openNodes.clear(); + closedNodes.clear(); + + return ts; +} + +PathFinder::Node *PathFinder::newNode(){ + if(nodePoolCountgetPos(); + int size= unit->getType()->getSize(); + Field field= unit->getCurrField(); + int teamIndex= unit->getTeam(); + + //if finalPos is free return it + if(map->isAproxFreeCells(finalPos, size, field, teamIndex)){ + return finalPos; + } + + //find nearest pos + Vec2i nearestPos= unitPos; + float nearestDist= unitPos.dist(finalPos); + for(int i= -maxFreeSearchRadius; i<=maxFreeSearchRadius; ++i){ + for(int j= -maxFreeSearchRadius; j<=maxFreeSearchRadius; ++j){ + Vec2i currPos= finalPos + Vec2i(i, j); + if(map->isAproxFreeCells(currPos, size, field, teamIndex)){ + float dist= currPos.dist(finalPos); + + //if nearer from finalPos + if(distheuristic < (*minNodeIt)->heuristic){ + minNodeIt= it; + } + } + + return minNodeIt; +} + +bool PathFinder::openPos(const Vec2i &sucPos){ + + for(Nodes::reverse_iterator it= closedNodes.rbegin(); it!=closedNodes.rend(); ++it){ + if(sucPos==(*it)->pos){ + return true; + } + } + + //use reverse iterator to find a node faster + for(Nodes::reverse_iterator it= openNodes.rbegin(); it!=openNodes.rend(); ++it){ + if(sucPos==(*it)->pos){ + return true; + } + } + + return false; +} + +}} //end namespace diff --git a/source/glest_game/ai/path_finder.h b/source/glest_game/ai/path_finder.h new file mode 100644 index 00000000..116c43ed --- /dev/null +++ b/source/glest_game/ai/path_finder.h @@ -0,0 +1,79 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_PATHFINDER_H_ +#define _GLEST_GAME_PATHFINDER_H_ + +#include "vec.h" + +#include + +using std::vector; +using Shared::Graphics::Vec2i; + +namespace Glest{ namespace Game{ + +class Map; +class Unit; + +// ===================================================== +// class PathFinder +// +/// Finds paths for units using a modification of the A* algorithm +// ===================================================== + +class PathFinder{ +public: + enum TravelState{ + tsArrived, + tsOnTheWay, + tsBlocked + }; + struct Node{ + Vec2i pos; + Node *next; + Node *prev; + float heuristic; + bool exploredCell; + }; + typedef vector Nodes; + +public: + static const int maxFreeSearchRadius; + static const int pathFindNodesMax; + static const int pathFindRefresh; + +private: + Nodes openNodes; + Nodes closedNodes; + Node *nodePool; + int nodePoolCount; + const Map *map; + +public: + PathFinder(); + PathFinder(const Map *map); + ~PathFinder(); + void init(const Map *map); + TravelState findPath(Unit *unit, const Vec2i &finalPos); + +private: + TravelState aStar(Unit *unit, const Vec2i &finalPos); + Node *newNode(); + Vec2i computeNearestFreePos(const Unit *unit, const Vec2i &targetPos); + float heuristic(const Vec2i &pos, const Vec2i &finalPos); + Nodes::iterator minHeuristic(); + bool openPos(const Vec2i &sucPos); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/facilities/auto_test.cpp b/source/glest_game/facilities/auto_test.cpp new file mode 100644 index 00000000..9683950e --- /dev/null +++ b/source/glest_game/facilities/auto_test.cpp @@ -0,0 +1,82 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2009 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "auto_test.h" + +#include "program.h" +#include "main_menu.h" +#include "menu_state_new_game.h" +#include "menu_state_scenario.h" +#include "game.h" + +#include "leak_dumper.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class AutoTest +// ===================================================== + +const time_t AutoTest::invalidTime = -1; +const time_t AutoTest::gameTime = 60*20; + +// ===================== PUBLIC ======================== + +AutoTest::AutoTest(){ + gameStartTime = invalidTime; + random.init(time(NULL)); +} + +AutoTest & AutoTest::getInstance(){ + static AutoTest autoTest; + return autoTest; +} + +void AutoTest::updateIntro(Program *program){ + program->setState(new MainMenu(program)); +} + +void AutoTest::updateRoot(Program *program, MainMenu *mainMenu){ + mainMenu->setState(new MenuStateNewGame(program, mainMenu)); +} + +void AutoTest::updateNewGame(Program *program, MainMenu *mainMenu){ + mainMenu->setState(new MenuStateScenario(program, mainMenu, "scenarios")); +} + +void AutoTest::updateScenario(MenuStateScenario *menuStateScenario){ + gameStartTime = invalidTime; + + int scenarioIndex = random.randRange(0, menuStateScenario->getScenarioCount()-1); + menuStateScenario->setScenario(scenarioIndex); + + menuStateScenario->launchGame(); +} + +void AutoTest::updateGame(Game *game){ + + // record start time + if(gameStartTime==invalidTime) + { + gameStartTime = time(NULL); + } + + // quit if we've espend enough time in the game + if(time(NULL)-gameStartTime>gameTime){ + game->quitGame(); + } +} + +void AutoTest::updateBattleEnd(Program *program){ + program->setState(new MainMenu(program)); +} + +}}//end namespace diff --git a/source/glest_game/facilities/auto_test.h b/source/glest_game/facilities/auto_test.h new file mode 100644 index 00000000..187ac2a4 --- /dev/null +++ b/source/glest_game/facilities/auto_test.h @@ -0,0 +1,57 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2009 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _SHARED_UTIL_AUTO_TEST_H_ +#define _SHARED_UTIL_AUTO_TEST_H_ + +#include + +#include "random.h" + +using Shared::Util::Random; + +namespace Glest{ namespace Game{ + +class Program; +class MainMenu; +class MenuStateScenario; +class Game; + +// ===================================================== +// class AutoTest +// +/// Interface to write log files +// ===================================================== + +class AutoTest{ +private: + int gameStartTime; + Random random; + +private: + static const time_t invalidTime; + static const time_t gameTime; + +public: + static AutoTest & getInstance(); + AutoTest(); + + void updateIntro(Program *program); + void updateRoot(Program *program, MainMenu *mainMenu); + void updateNewGame(Program *program, MainMenu *mainMenu); + void updateScenario(MenuStateScenario *menuStateScenario); + void updateGame(Game *game); + void updateBattleEnd(Program *program); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/facilities/components.cpp b/source/glest_game/facilities/components.cpp new file mode 100644 index 00000000..f0d99d46 --- /dev/null +++ b/source/glest_game/facilities/components.cpp @@ -0,0 +1,248 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "components.h" + +#include +#include + +#include "metrics.h" +#include "core_data.h" +#include "platform_util.h" + +#include "leak_dumper.h" + +using namespace std; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class GraphicComponent +// ===================================================== + +float GraphicComponent::anim= 0.f; +float GraphicComponent::fade= 0.f; +const float GraphicComponent::animSpeed= 0.02f; +const float GraphicComponent::fadeSpeed= 0.01f; + +GraphicComponent::GraphicComponent(){ + enabled= true; +} + +void GraphicComponent::init(int x, int y, int w, int h){ + this->x= x; + this->y= y; + this->w= w; + this->h= h; + font= CoreData::getInstance().getMenuFontNormal(); + enabled= true; +} + +bool GraphicComponent::mouseMove(int x, int y){ + return + x > this->x && + y > this->y && + x < this->x + w && + y < this->y + h; +} + +bool GraphicComponent::mouseClick(int x, int y){ + return mouseMove(x, y); +} + +void GraphicComponent::update(){ + fade+= fadeSpeed; + anim+= animSpeed; + if(fade>1.f) fade= 1.f; + if(anim>1.f) anim= 0.f; +} + +void GraphicComponent::resetFade(){ + fade= 0.f; +} + +// ===================================================== +// class GraphicLabel +// ===================================================== + +const int GraphicLabel::defH= 20; +const int GraphicLabel::defW= 70; + +void GraphicLabel::init(int x, int y, int w, int h, bool centered){ + GraphicComponent::init(x, y, w, h); + this->centered= centered; +} + +// ===================================================== +// class GraphicButton +// ===================================================== + +const int GraphicButton::defH= 22; +const int GraphicButton::defW= 90; + +void GraphicButton::init(int x, int y, int w, int h){ + GraphicComponent::init(x, y, w, h); + lighted= false; +} + +bool GraphicButton::mouseMove(int x, int y){ + bool b= GraphicComponent::mouseMove(x, y); + lighted= b; + return b; +} + +// ===================================================== +// class GraphicListBox +// ===================================================== + +const int GraphicListBox::defH= 22; +const int GraphicListBox::defW= 140; + +void GraphicListBox::init(int x, int y, int w, int h){ + GraphicComponent::init(x, y, w, h); + + graphButton1.init(x, y, 22, h); + graphButton2.init(x+w-22, y, 22, h); + graphButton1.setText("<"); + graphButton2.setText(">"); + selectedItemIndex=-1; +} + +//queryes +void GraphicListBox::pushBackItem(string item){ + items.push_back(item); + setSelectedItemIndex(0); +} + +void GraphicListBox::setItems(const vector &items){ + this->items= items; + setSelectedItemIndex(0); +} + +void GraphicListBox::setSelectedItemIndex(int index){ + assert(index>=0 && index::iterator iter; + + iter= find(items.begin(), items.end(), item); + + if(iter==items.end()){ + throw runtime_error("Value not found on list box: "+item); + } + + setSelectedItemIndex(iter-items.begin()); + +} + +bool GraphicListBox::mouseMove(int x, int y){ + return + graphButton1.mouseMove(x, y) || + graphButton2.mouseMove(x, y); +} + +bool GraphicListBox::mouseClick(int x, int y){ + if(!items.empty()){ + bool b1= graphButton1.mouseClick(x, y); + bool b2= graphButton2.mouseClick(x, y); + + if(b1){ + selectedItemIndex--; + if(selectedItemIndex<0){ + selectedItemIndex=items.size()-1; + } + } + else if(b2){ + selectedItemIndex++; + if(selectedItemIndex>=items.size()){ + selectedItemIndex=0; + } + } + setText(getSelectedItem()); + + return b1 || b2; + } + return false; +} + +// ===================================================== +// class GraphicMessageBox +// ===================================================== + +const int GraphicMessageBox::defH= 240; +const int GraphicMessageBox::defW= 350; + +void GraphicMessageBox::init(const string &button1Str, const string &button2Str){ + init(button1Str); + + button1.init(x+(w-GraphicButton::defW)/4, y+25); + button1.setText(button1Str); + button2.init(x+3*(w-GraphicButton::defW)/4, y+25); + button2.setText(button2Str); + buttonCount= 2; +} + +void GraphicMessageBox::init(const string &button1Str){ + font= CoreData::getInstance().getMenuFontNormal(); + + h= defH; + w= defW; + + const Metrics &metrics= Metrics::getInstance(); + + x= (metrics.getVirtualW()-w)/2; + y= (metrics.getVirtualH()-h)/2; + + button1.init(x+(w-GraphicButton::defW)/2, y+25); + button1.setText(button1Str); + buttonCount= 1; +} + +bool GraphicMessageBox::mouseMove(int x, int y){ + return button1.mouseMove(x, y) || button2.mouseMove(x, y); +} + +bool GraphicMessageBox::mouseClick(int x, int y){ + bool b1= button1.mouseClick(x, y); + bool b2= button2.mouseClick(x, y); + if(buttonCount==1){ + return b1; + } + else{ + return b1 ||b2; + } +} + +bool GraphicMessageBox::mouseClick(int x, int y, int &clickedButton){ + bool b1= button1.mouseClick(x, y); + bool b2= button2.mouseClick(x, y); + + if(buttonCount==1){ + clickedButton= 1; + return b1; + } + else{ + if(b1){ + clickedButton= 1; + return true; + } + else if(b2){ + clickedButton= 2; + return true; + } + } + return false; +} + +}}//end namespace diff --git a/source/glest_game/facilities/components.h b/source/glest_game/facilities/components.h new file mode 100644 index 00000000..8201c0a3 --- /dev/null +++ b/source/glest_game/facilities/components.h @@ -0,0 +1,182 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_GRAPHCOMPONENT_H_ +#define _GLEST_GAME_GRAPHCOMPONENT_H_ + +#include +#include + +#include "font.h" + +using std::string; +using std::vector; + +using Shared::Graphics::Font2D; + +namespace Glest{ namespace Game{ + +// =========================================================== +// class GraphicComponent +// +// OpenGL renderer GUI components +// =========================================================== + +class GraphicComponent{ +public: + static const float animSpeed; + static const float fadeSpeed; + +protected: + int x, y, w, h; + string text; + const Font2D *font; + bool enabled; + + static float anim; + static float fade; + +public: + GraphicComponent(); + virtual ~GraphicComponent(){} + + void init(int x, int y, int w, int h); + + int getX() const {return x;} + int getY() const {return y;} + int getW() const {return w;} + int getH() const {return h;} + const string &getText() const {return text;} + const Font2D *getFont() const {return font;} + bool getEnabled() const {return enabled;} + + void setX(int x) {this->x= x;} + void setY(int y) {this->y= y;} + void setText(const string &text) {this->text= text;} + void setFont(const Font2D *font) {this->font= font;} + void setEnabled(bool enabled) {this->enabled= enabled;} + + virtual bool mouseMove(int x, int y); + virtual bool mouseClick(int x, int y); + + static void update(); + static void resetFade(); + static float getAnim() {return anim;} + static float getFade() {return fade;} +}; + +// =========================================================== +// class GraphicLabel +// =========================================================== + +class GraphicLabel: public GraphicComponent{ +public: + static const int defH; + static const int defW; + +private: + bool centered; + +public: + void init(int x, int y, int w=defW, int h=defH, bool centered= false); + + bool getCentered() const {return centered;} + + void setCentered(bool centered) {this->centered= centered;} +}; + +// =========================================================== +// class GraphicButton +// =========================================================== + +class GraphicButton: public GraphicComponent{ +public: + static const int defH; + static const int defW; + +private: + bool lighted; + +public: + void init(int x, int y, int w=defW, int h=defH); + + bool getLighted() const {return lighted;} + + void setLighted(bool lighted) {this->lighted= lighted;} + virtual bool mouseMove(int x, int y); +}; + +// =========================================================== +// class GraphicListBox +// =========================================================== + +class GraphicListBox: public GraphicComponent{ +public: + static const int defH; + static const int defW; + +private: + GraphicButton graphButton1, graphButton2; + vector items; + int selectedItemIndex; + +public: + void init(int x, int y, int w=defW, int h=defH); + + int getItemCount() const {return items.size();} + int getSelectedItemIndex() const {return selectedItemIndex;} + string getSelectedItem() const {return items[selectedItemIndex];} + const GraphicButton *getButton1() const {return &graphButton1;} + const GraphicButton *getButton2() const {return &graphButton2;} + + void pushBackItem(string item); + void setItems(const vector &items); + void setSelectedItemIndex(int index); + void setSelectedItem(string item); + + virtual bool mouseMove(int x, int y); + virtual bool mouseClick(int x, int y); +}; + +// =========================================================== +// class GraphicMessageBox +// =========================================================== + +class GraphicMessageBox: public GraphicComponent{ +public: + static const int defH; + static const int defW; + +private: + GraphicButton button1; + GraphicButton button2; + int buttonCount; + string header; + +public: + void init(const string &button1Str, const string &button2Str); + void init(const string &button1Str); + + int getButtonCount() const {return buttonCount;} + const GraphicButton *getButton1() const {return &button1;} + const GraphicButton *getButton2() const {return &button2;} + string getHeader() const {return header;} + + void setHeader(string header) {this->header= header;} + + virtual bool mouseMove(int x, int y); + virtual bool mouseClick(int x, int y); + bool mouseClick(int x, int y, int &clickedButton); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/facilities/game_util.cpp b/source/glest_game/facilities/game_util.cpp new file mode 100644 index 00000000..591d3109 --- /dev/null +++ b/source/glest_game/facilities/game_util.cpp @@ -0,0 +1,104 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "game_util.h" + +#include "util.h" +#include "lang.h" +#include "game_constants.h" +#include "config.h" +#include "leak_dumper.h" + +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +const string mailString= "contact_game@glest.org"; +const string glestVersionString= "v3.2.3-beta3"; + +string getCrashDumpFileName(){ + return "glest"+glestVersionString+".dmp"; +} + +string getNetworkVersionString(){ + return glestVersionString + " - " + string(__DATE__) + " - " + string(__TIME__); +} + +string getAboutString1(int i){ + switch(i){ + case 0: return "Glest " + glestVersionString + " (" + "Shared Library " + sharedLibVersionString + ")"; + case 1: return "Built: " + string(__DATE__); + case 2: return "Copyright 2001-2009 The Glest Team"; + } + return ""; +} + +string getAboutString2(int i){ + switch(i){ + case 0: return "Web: http://glest.org"; + case 1: return "Mail: " + mailString; + case 2: return "Irc: irc://irc.freenode.net/glest"; + } + return ""; +} + +string getTeammateName(int i){ + switch(i){ + case 0: return "Marti�o Figueroa"; + case 1: return "Jos� Luis Gonz�lez"; + case 2: return "Tucho Fern�ndez"; + case 3: return "Jos� Zanni"; + case 4: return "F�lix Men�ndez"; + case 5: return "Marcos Caruncho"; + case 6: return "Matthias Braun"; + } + return ""; +} + +string getTeammateRole(int i){ + Lang &l= Lang::getInstance(); + + switch(i){ + case 0: return l.get("Programming"); + case 1: return l.get("SoundAndMusic"); + case 2: return l.get("3dAnd2dArt"); + case 3: return l.get("2dArtAndWeb"); + case 4: return l.get("Animation"); + case 5: return l.get("3dArt"); + case 6: return l.get("LinuxPort"); + } + return ""; +} + +string formatString(const string &str){ + string outStr = str; + + if(!outStr.empty()){ + outStr[0]= toupper(outStr[0]); + } + + bool afterSeparator= false; + for(int i= 0; i +#include + +#include "util.h" + +using std::string; +using Shared::Util::sharedLibVersionString; + +namespace Glest{ namespace Game{ + +extern const string mailString; +extern const string glestVersionString; +extern const string networkVersionString; + +string getCrashDumpFileName(); +string getNetworkVersionString(); +string getAboutString1(int i); +string getAboutString2(int i); +string getTeammateName(int i); +string getTeammateRole(int i); + +string formatString(const string &str); + +}}//end namespace + +#endif diff --git a/source/glest_game/facilities/logger.cpp b/source/glest_game/facilities/logger.cpp new file mode 100644 index 00000000..a7c25a21 --- /dev/null +++ b/source/glest_game/facilities/logger.cpp @@ -0,0 +1,94 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "logger.h" + +#include "util.h" +#include "renderer.h" +#include "core_data.h" +#include "metrics.h" +#include "lang.h" +#include "leak_dumper.h" + +using namespace std; +using namespace Shared::Graphics; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Logger +// ===================================================== + +const int Logger::logLineCount= 15; + +// ===================== PUBLIC ======================== + +Logger::Logger(){ + fileName= "log.txt"; +} + +Logger & Logger::getInstance(){ + static Logger logger; + return logger; +} + +void Logger::add(const string &str, bool renderScreen){ + FILE *f=fopen(fileName.c_str(), "at+"); + if(f!=NULL){ + fprintf(f, "%s\n", str.c_str()); + fclose(f); + } + current= str; + if(renderScreen){ + renderLoadingScreen(); + } +} + +void Logger::clear(){ + string s="Log file\n"; + + FILE *f= fopen(fileName.c_str(), "wt+"); + if(f==NULL){ + throw runtime_error("Error opening log file"+ fileName); + } + + fprintf(f, "%s", s.c_str()); + fprintf(f, "\n"); + + fclose(f); +} + +// ==================== PRIVATE ==================== + +void Logger::renderLoadingScreen(){ + + Renderer &renderer= Renderer::getInstance(); + CoreData &coreData= CoreData::getInstance(); + const Metrics &metrics= Metrics::getInstance(); + + renderer.reset2d(); + renderer.clearBuffers(); + + renderer.renderBackground(CoreData::getInstance().getBackgroundTexture()); + + renderer.renderText( + state, coreData.getMenuFontBig(), Vec3f(1.f), + metrics.getVirtualW()/4, 65*metrics.getVirtualH()/100, false); + + renderer.renderText( + current, coreData.getMenuFontNormal(), 1.0f, + metrics.getVirtualW()/4, + 62*metrics.getVirtualH()/100, false); + + renderer.swapBuffers(); +} + +}}//end namespace diff --git a/source/glest_game/facilities/logger.h b/source/glest_game/facilities/logger.h new file mode 100644 index 00000000..984fa226 --- /dev/null +++ b/source/glest_game/facilities/logger.h @@ -0,0 +1,60 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _SHARED_UTIL_LOGGER_H_ +#define _SHARED_UTIL_LOGGER_H_ + +#include +#include + +using std::string; +using std::deque; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Logger +// +/// Interface to write log files +// ===================================================== + +class Logger{ +private: + static const int logLineCount; + +private: + typedef deque Strings; + +private: + string fileName; + string state; + string subtitle; + string current; + +private: + Logger(); + +public: + static Logger & getInstance(); + + void setFile(const string &fileName) {this->fileName= fileName;} + void setState(const string &state) {this->state= state;} + void setSubtitle(const string &subtitle) {this->subtitle= subtitle;} + + void add(const string &str, bool renderScreen= false); + void renderLoadingScreen(); + + void clear(); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/game/chat_manager.cpp b/source/glest_game/game/chat_manager.cpp new file mode 100644 index 00000000..4f829952 --- /dev/null +++ b/source/glest_game/game/chat_manager.cpp @@ -0,0 +1,103 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "chat_manager.h" + +#include "window.h" +#include "console.h" +#include "network_manager.h" +#include "lang.h" +#include "leak_dumper.h" + +using namespace Shared::Platform; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class ChatManager +// ===================================================== + +const int ChatManager::maxTextLenght= 64; + +ChatManager::ChatManager(){ + console= NULL; + editEnabled= false; + teamMode= false; + thisTeamIndex= -1; +} + +void ChatManager::init(Console* console, int thisTeamIndex){ + this->console= console; + this->thisTeamIndex= thisTeamIndex; +} + +void ChatManager::keyDown(char key){ + + Lang &lang= Lang::getInstance(); + + //toggle team mode + if(!editEnabled && key=='H'){ + if(teamMode){ + teamMode= false; + console->addLine(lang.get("ChatMode") + ": " + lang.get("All")); + } + else{ + teamMode= true; + console->addLine(lang.get("ChatMode") + ": " + lang.get("Team")); + } + } + + if(key==vkReturn){ + if(editEnabled){ + GameNetworkInterface *gameNetworkInterface= NetworkManager::getInstance().getGameNetworkInterface(); + + editEnabled= false; + if(!text.empty()){ + console->addLine(gameNetworkInterface->getHostName() + ": " + text); + gameNetworkInterface->sendTextMessage(text, teamMode? thisTeamIndex: -1); + } + } + else{ + editEnabled= true; + text.clear(); + } + } + else if(key==vkBack){ + if(!text.empty()){ + text.erase(text.end() -1); + } + } +} + +void ChatManager::keyPress(char c){ + if(editEnabled && text.size()=' '){ + text+= c; + } + } +} + +void ChatManager::updateNetwork(){ + GameNetworkInterface *gameNetworkInterface= NetworkManager::getInstance().getGameNetworkInterface(); + string text; + string sender; + + if(!gameNetworkInterface->getChatText().empty()){ + int teamIndex= gameNetworkInterface->getChatTeamIndex(); + + if(teamIndex==-1 || teamIndex==thisTeamIndex){ + console->addLine(gameNetworkInterface->getChatSender()+": "+gameNetworkInterface->getChatText(), true); + } + } +} + +}}//end namespace diff --git a/source/glest_game/game/chat_manager.h b/source/glest_game/game/chat_manager.h new file mode 100644 index 00000000..fe718b1c --- /dev/null +++ b/source/glest_game/game/chat_manager.h @@ -0,0 +1,53 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_CHATMANAGER_H_ +#define _GLEST_GAME_CHATMANAGER_H_ + +#include + +using std::string; + +namespace Glest{ namespace Game{ + +class Console; + +// ===================================================== +// class ChatManager +// ===================================================== + +class ChatManager{ +private: + static const int maxTextLenght; + +private: + bool editEnabled; + bool teamMode; + Console* console; + string text; + int thisTeamIndex; + +public: + ChatManager(); + void init(Console* console, int thisTeamIndex); + + void keyDown(char key); + void keyPress(char c); + void updateNetwork(); + + bool getEditEnabled() const {return editEnabled;} + bool getTeamMode() const {return teamMode;} + string getText() const {return text;} +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/game/commander.cpp b/source/glest_game/game/commander.cpp new file mode 100644 index 00000000..226cf737 --- /dev/null +++ b/source/glest_game/game/commander.cpp @@ -0,0 +1,310 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "commander.h" + +#include "world.h" +#include "unit.h" +#include "conversion.h" +#include "upgrade.h" +#include "command.h" +#include "command_type.h" +#include "network_manager.h" +#include "console.h" +#include "config.h" +#include "platform_util.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; +using namespace Shared::Util; +using namespace Shared::Platform; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Commander +// ===================================================== + +// ===================== PUBLIC ======================== + +void Commander::init(World *world){ + this->world= world; +} + +CommandResult Commander::tryGiveCommand(const Unit* unit, const CommandType *commandType, const Vec2i &pos, const UnitType* unitType) const{ + NetworkCommand networkCommand(nctGiveCommand, unit->getId(), commandType->getId(), pos, unitType->getId()); + return pushNetworkCommand(&networkCommand); +} + +CommandResult Commander::tryGiveCommand(const Selection *selection, CommandClass commandClass, const Vec2i &pos, const Unit *targetUnit) const{ + + if(!selection->isEmpty()){ + Vec2i refPos, currPos; + CommandResultContainer results; + + refPos= computeRefPos(selection); + + //give orders to all selected units + for(int i=0; igetCount(); ++i){ + const Unit *unit= selection->getUnit(i); + const CommandType *ct= unit->getType()->getFirstCtOfClass(commandClass); + if(ct!=NULL){ + int targetId= targetUnit==NULL? Unit::invalidId: targetUnit->getId(); + int unitId= selection->getUnit(i)->getId(); + Vec2i currPos= computeDestPos(refPos, selection->getUnit(i)->getPos(), pos); + NetworkCommand networkCommand(nctGiveCommand, unitId, ct->getId(), currPos, -1, targetId); + + //every unit is ordered to a different pos + CommandResult result= pushNetworkCommand(&networkCommand); + results.push_back(result); + } + else{ + results.push_back(crFailUndefined); + } + } + return computeResult(results); + } + else{ + return crFailUndefined; + } +} + +CommandResult Commander::tryGiveCommand(const Selection *selection, const CommandType *commandType, const Vec2i &pos, const Unit *targetUnit) const{ + if(!selection->isEmpty() && commandType!=NULL){ + Vec2i refPos; + CommandResultContainer results; + + refPos= computeRefPos(selection); + + //give orders to all selected units + for(int i=0; igetCount(); ++i){ + int targetId= targetUnit==NULL? Unit::invalidId: targetUnit->getId(); + int unitId= selection->getUnit(i)->getId(); + Vec2i currPos= computeDestPos(refPos, selection->getUnit(i)->getPos(), pos); + NetworkCommand networkCommand(nctGiveCommand, unitId, commandType->getId(), currPos, -1, targetId); + + //every unit is ordered to a different position + CommandResult result= pushNetworkCommand(&networkCommand); + results.push_back(result); + } + + return computeResult(results); + } + else{ + return crFailUndefined; + } +} + +//auto command +CommandResult Commander::tryGiveCommand(const Selection *selection, const Vec2i &pos, const Unit *targetUnit) const{ + if(!selection->isEmpty()){ + + Vec2i refPos, currPos; + CommandResultContainer results; + + //give orders to all selected units + refPos= computeRefPos(selection); + for(int i=0; igetCount(); ++i){ + + //every unit is ordered to a different pos + currPos= computeDestPos(refPos, selection->getUnit(i)->getPos(), pos); + + //get command type + const CommandType *commandType= selection->getUnit(i)->computeCommandType(pos, targetUnit); + + //give commands + if(commandType!=NULL){ + int targetId= targetUnit==NULL? Unit::invalidId: targetUnit->getId(); + int unitId= selection->getUnit(i)->getId(); + NetworkCommand networkCommand(nctGiveCommand, unitId, commandType->getId(), currPos, -1, targetId); + + CommandResult result= pushNetworkCommand(&networkCommand); + results.push_back(result); + } + else{ + results.push_back(crFailUndefined); + } + } + return computeResult(results); + } + else{ + return crFailUndefined; + } +} + +CommandResult Commander::tryCancelCommand(const Selection *selection) const{ + + for(int i=0; igetCount(); ++i){ + NetworkCommand command(nctCancelCommand, selection->getUnit(i)->getId()); + pushNetworkCommand(&command); + } + + return crSuccess; +} + +void Commander::trySetMeetingPoint(const Unit* unit, const Vec2i &pos)const{ + NetworkCommand command(nctSetMeetingPoint, unit->getId(), -1, pos); + pushNetworkCommand(&command); +} + + +// ==================== PRIVATE ==================== + +Vec2i Commander::computeRefPos(const Selection *selection) const{ + Vec2i total= Vec2i(0); + for(int i=0; igetCount(); ++i){ + total= total+selection->getUnit(i)->getPos(); + } + + return Vec2i(total.x/ selection->getCount(), total.y/ selection->getCount()); +} + +Vec2i Commander::computeDestPos(const Vec2i &refUnitPos, const Vec2i &unitPos, const Vec2i &commandPos) const{ + Vec2i pos; + Vec2i posDiff= unitPos-refUnitPos; + + if(abs(posDiff.x)>=3){ + posDiff.x= posDiff.x % 3; + } + + if(abs(posDiff.y)>=3){ + posDiff.y= posDiff.y % 3; + } + + pos= commandPos+posDiff; + world->getMap()->clampPos(pos); + return pos; +} + +CommandResult Commander::computeResult(const CommandResultContainer &results) const{ + switch(results.size()){ + case 0: + return crFailUndefined; + case 1: + return results.front(); + default: + for(int i=0; ifindUnitById(networkCommand->getUnitId()); + CommandResult cr= crSuccess; + + //validate unit + if(unit==NULL){ + throw runtime_error("Command refers to non existant unit. Game out of synch."); + } + + //add the command to the interface + gameNetworkInterface->requestCommand(networkCommand); + + //calculate the result of the command + if(networkCommand->getNetworkCommandType()==nctGiveCommand){ + Command* command= buildCommand(networkCommand); + cr= unit->checkCommand(command); + delete command; + } + return cr; +} + +void Commander::updateNetwork(){ + NetworkManager &networkManager= NetworkManager::getInstance(); + + //chech that this is a keyframe + if( !networkManager.isNetworkGame() || (world->getFrameCount() % GameConstants::networkFramePeriod)==0){ + + GameNetworkInterface *gameNetworkInterface= NetworkManager::getInstance().getGameNetworkInterface(); + + //update the keyframe + gameNetworkInterface->updateKeyframe(world->getFrameCount()); + + //give pending commands + for(int i= 0; i < gameNetworkInterface->getPendingCommandCount(); ++i){ + giveNetworkCommand(gameNetworkInterface->getPendingCommand(i)); + } + gameNetworkInterface->clearPendingCommands(); + } +} + +void Commander::giveNetworkCommand(const NetworkCommand* networkCommand) const{ + + Unit* unit= world->findUnitById(networkCommand->getUnitId()); + + //exec ute command, if unit is still alive + if(unit!=NULL){ + switch(networkCommand->getNetworkCommandType()){ + case nctGiveCommand:{ + assert(networkCommand->getCommandTypeId()!=CommandType::invalidId); + Command* command= buildCommand(networkCommand); + unit->giveCommand(command); + } + break; + case nctCancelCommand: + unit->cancelCommand(); + break; + case nctSetMeetingPoint: + unit->setMeetingPos(networkCommand->getPosition()); + break; + default: + assert(false); + } + } +} + +Command* Commander::buildCommand(const NetworkCommand* networkCommand) const{ + assert(networkCommand->getNetworkCommandType()==nctGiveCommand); + + Unit* target= NULL; + const CommandType* ct= NULL; + const Unit* unit= world->findUnitById(networkCommand->getUnitId()); + const UnitType* unitType= world->findUnitTypeById(unit->getFaction()->getType(), networkCommand->getUnitTypeId()); + + //validate unit + if(unit==NULL){ + throw runtime_error("Can not find unit with id: " + intToStr(networkCommand->getUnitId()) + ". Game out of synch."); + } + + ct= unit->getType()->findCommandTypeById(networkCommand->getCommandTypeId()); + + //validate command type + if(ct==NULL){ + throw runtime_error("Can not find command type with id: " + intToStr(networkCommand->getCommandTypeId()) + " in unit: " + unit->getType()->getName() + ". Game out of synch."); + } + + //get target, the target might be dead due to lag, cope with it + if(networkCommand->getTargetId()!=Unit::invalidId){ + target= world->findUnitById(networkCommand->getTargetId()); + } + + //create command + Command *command= NULL; + if(unitType!=NULL){ + command= new Command(ct, networkCommand->getPosition(), unitType); + } + else if(target==NULL){ + command= new Command(ct, networkCommand->getPosition()); + } + else{ + command= new Command(ct, target); + } + + //issue command + return command; +} + +}}//end namespace diff --git a/source/glest_game/game/commander.h b/source/glest_game/game/commander.h new file mode 100644 index 00000000..fe8f822d --- /dev/null +++ b/source/glest_game/game/commander.h @@ -0,0 +1,68 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_COMMANDER_H_ +#define _GLEST_GAME_COMMANDER_H_ + +#include + +#include "vec.h" +#include "selection.h" +#include "command_type.h" + +using std::vector; + +namespace Glest{ namespace Game{ + +using Shared::Graphics::Vec2i; + +class World; +class Unit; +class Command; +class CommandType; +class NetworkCommand; + +// ===================================================== +// class Commander +// +/// Gives commands to the units +// ===================================================== + +class Commander{ +private: + typedef vector CommandResultContainer; + +private: + World *world; + +public: + void init(World *world); + void updateNetwork(); + + CommandResult tryGiveCommand(const Unit* unit, const CommandType *commandType, const Vec2i &pos, const UnitType* unitType) const; + CommandResult tryGiveCommand(const Selection *selection, CommandClass commandClass, const Vec2i &pos= Vec2i(0), const Unit *targetUnit= NULL) const; + CommandResult tryGiveCommand(const Selection *selection, const CommandType *commandType, const Vec2i &pos= Vec2i(0), const Unit *targetUnit= NULL) const; + CommandResult tryGiveCommand(const Selection *selection, const Vec2i &pos, const Unit *targetUnit= NULL) const; + CommandResult tryCancelCommand(const Selection *selection) const; + void trySetMeetingPoint(const Unit* unit, const Vec2i &pos) const; + CommandResult pushNetworkCommand(const NetworkCommand* networkCommand) const; + +private: + Vec2i computeRefPos(const Selection *selection) const; + Vec2i computeDestPos(const Vec2i &refUnitPos, const Vec2i &unitPos, const Vec2i &commandPos) const; + CommandResult computeResult(const CommandResultContainer &results) const; + void giveNetworkCommand(const NetworkCommand* networkCommand) const; + Command* buildCommand(const NetworkCommand* networkCommand) const; +}; + +}} //end namespace + +#endif diff --git a/source/glest_game/game/console.cpp b/source/glest_game/game/console.cpp new file mode 100644 index 00000000..0cab8818 --- /dev/null +++ b/source/glest_game/game/console.cpp @@ -0,0 +1,65 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "console.h" + +#include "lang.h" +#include "config.h" +#include "program.h" +#include "game_constants.h" +#include "sound_renderer.h" +#include "core_data.h" +#include "leak_dumper.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Console +// ===================================================== + +Console::Console(){ + //config + maxLines= Config::getInstance().getInt("ConsoleMaxLines"); + timeout= Config::getInstance().getInt("ConsoleTimeout"); + + timeElapsed= 0.0f; +} + +void Console::addStdMessage(const string &s){ + addLine(Lang::getInstance().get(s)); +} + +void Console::addLine(string line, bool playSound){ + if(playSound){ + SoundRenderer::getInstance().playFx(CoreData::getInstance().getClickSoundA()); + } + lines.insert(lines.begin(), StringTimePair(line, timeElapsed)); + if(lines.size()>maxLines){ + lines.pop_back(); + } +} + +void Console::update(){ + timeElapsed+= 1.f/GameConstants::updateFps; + + if(!lines.empty()){ + if(lines.back().second +#include +#include + +using std::string; +using std::vector; +using std::pair; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Console +// +// In-game console that shows various types of messages +// ===================================================== + +class Console{ +private: + static const int consoleLines= 5; + +public: + typedef pair StringTimePair; + typedef vector Lines; + typedef Lines::const_iterator LineIterator; + +private: + float timeElapsed; + Lines lines; + + //this should be deleted from here someday + bool won, lost; + + //config + int maxLines; + float timeout; + +public: + Console(); + + int getLineCount() const {return lines.size();} + string getLine(int i) const {return lines[i].first;} + + + void addStdMessage(const string &s); + void addLine(string line, bool playSound= false); + void update(); + bool isEmpty(); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/game/game.cpp b/source/glest_game/game/game.cpp new file mode 100644 index 00000000..ba4c6fe5 --- /dev/null +++ b/source/glest_game/game/game.cpp @@ -0,0 +1,883 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "game.h" + +#include "config.h" +#include "renderer.h" +#include "particle_renderer.h" +#include "commander.h" +#include "battle_end.h" +#include "sound_renderer.h" +#include "profiler.h" +#include "core_data.h" +#include "metrics.h" +#include "faction.h" +#include "network_manager.h" +#include "checksum.h" +#include "auto_test.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; +using namespace Shared::Util; +using namespace Shared::Platform; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Game +// ===================================================== + +// ===================== PUBLIC ======================== + +Game::Game(Program *program, const GameSettings *gameSettings): + ProgramState(program) +{ + this->gameSettings= *gameSettings; + + mouseX=0; + mouseY=0; + mouse2d= 0; + loadingText=""; + weatherParticleSystem= NULL; + updateFps=0; + renderFps=0; + lastUpdateFps=0; + lastRenderFps=0; + paused= false; + gameOver= false; + renderNetworkStatus= false; + speed= sNormal; +} + +Game::~Game(){ + Logger &logger= Logger::getInstance(); + Renderer &renderer= Renderer::getInstance(); + + logger.setState(Lang::getInstance().get("Deleting")); + logger.add("Game", true); + + renderer.endGame(); + SoundRenderer::getInstance().stopAllSounds(); + + deleteValues(aiInterfaces.begin(), aiInterfaces.end()); + + gui.end(); //selection must be cleared before deleting units + world.end(); //must die before selection because of referencers +} + + +// ==================== init and load ==================== + +void Game::load(){ + Logger &logger= Logger::getInstance(); + string mapName= gameSettings.getMap(); + string tilesetName= gameSettings.getTileset(); + string techName= gameSettings.getTech(); + string scenarioName= gameSettings.getScenario(); + + logger.setState(Lang::getInstance().get("Loading")); + + if(scenarioName.empty()){ + logger.setSubtitle(formatString(mapName)+" - "+formatString(tilesetName)+" - "+formatString(techName)); + } + else{ + logger.setSubtitle(formatString(scenarioName)); + } + + //tileset + world.loadTileset("tilesets/"+tilesetName, &checksum); + + set factions; + for ( int i=0; i < gameSettings.getFactionCount(); ++i ) { + factions.insert(gameSettings.getFactionTypeName(i)); + } + + //tech, load before map because of resources + world.loadTech("techs/"+techName, factions, &checksum); + + //map + world.loadMap(Map::getMapPath(mapName), &checksum); + + //scenario + if(!scenarioName.empty()){ + Lang::getInstance().loadScenarioStrings(gameSettings.getScenarioDir(), scenarioName); + world.loadScenario(Scenario::getScenarioPath(gameSettings.getScenarioDir(), scenarioName), &checksum); + } +} + +void Game::init(){ + Lang &lang= Lang::getInstance(); + Logger &logger= Logger::getInstance(); + CoreData &coreData= CoreData::getInstance(); + Renderer &renderer= Renderer::getInstance(); + Map *map= world.getMap(); + NetworkManager &networkManager= NetworkManager::getInstance(); + + logger.setState(lang.get("Initializing")); + + //mesage box + mainMessageBox.init(lang.get("Yes"), lang.get("No")); + mainMessageBox.setEnabled(false); + + //check forog war + if(!Config::getInstance().getBool("FogOfWar") && networkManager.isNetworkGame() ){ + throw runtime_error("Can not play online games with for of war disabled"); + } + + //init world, and place camera + commander.init(&world); + world.init(this, gameSettings.getDefaultUnits()); + gui.init(this); + chatManager.init(&console, world.getThisTeamIndex()); + const Vec2i &v= map->getStartLocation(world.getThisFaction()->getStartLocationIndex()); + gameCamera.init(map->getW(), map->getH()); + gameCamera.setPos(Vec2f(v.x, v.y)); + scriptManager.init(&world, &gameCamera); + + //create IAs + aiInterfaces.resize(world.getFactionCount()); + for(int i=0; igetCpuControl()){ + aiInterfaces[i]= new AiInterface(*this, i, faction->getTeam()); + logger.add("Creating AI for faction " + intToStr(i), true); + } + else{ + aiInterfaces[i]= NULL; + } + } + + //wheather particle systems + if(world.getTileset()->getWeather() == wRainy){ + logger.add("Creating rain particle system", true); + weatherParticleSystem= new RainParticleSystem(); + weatherParticleSystem->setSpeed(12.f/GameConstants::updateFps); + weatherParticleSystem->setPos(gameCamera.getPos()); + renderer.manageParticleSystem(weatherParticleSystem, rsGame); + } + else if(world.getTileset()->getWeather() == wSnowy){ + logger.add("Creating snow particle system", true); + weatherParticleSystem= new SnowParticleSystem(1200); + weatherParticleSystem->setSpeed(1.5f/GameConstants::updateFps); + weatherParticleSystem->setPos(gameCamera.getPos()); + weatherParticleSystem->setTexture(coreData.getSnowTexture()); + renderer.manageParticleSystem(weatherParticleSystem, rsGame); + } + + //init renderer state + logger.add("Initializing renderer", true); + renderer.initGame(this); + + //sounds + SoundRenderer &soundRenderer= SoundRenderer::getInstance(); + + Tileset *tileset= world.getTileset(); + AmbientSounds *ambientSounds= tileset->getAmbientSounds(); + + //rain + if(tileset->getWeather()==wRainy && ambientSounds->isEnabledRain()){ + logger.add("Starting ambient stream", true); + soundRenderer.playAmbient(ambientSounds->getRain()); + } + + //snow + if(tileset->getWeather()==wSnowy && ambientSounds->isEnabledSnow()){ + logger.add("Starting ambient stream", true); + soundRenderer.playAmbient(ambientSounds->getSnow()); + } + + logger.add("Waiting for network", true); + networkManager.getGameNetworkInterface()->waitUntilReady(&checksum); + + logger.add("Starting music stream", true); + StrSound *gameMusic= world.getThisFaction()->getType()->getMusic(); + soundRenderer.playMusic(gameMusic); + + logger.add("Launching game"); +} + + +// ==================== update ==================== + +//update +void Game::update(){ + + // a) Updates non dependant on speed + + //misc + updateFps++; + mouse2d= (mouse2d+1) % Renderer::maxMouse2dAnim; + + //console + console.update(); + + // b) Updates depandant on speed + + int updateLoops= getUpdateLoops(); + + //update + for(int i=0; igetCpuControl() && scriptManager.getPlayerModifiers(i)->getAiEnabled()){ + aiInterfaces[i]->update(); + } + } + + //World + world.update(); + + // Commander + commander.updateNetwork(); + + //Gui + gui.update(); + + //Particle systems + if(weatherParticleSystem != NULL){ + weatherParticleSystem->setPos(gameCamera.getPos()); + } + renderer.updateParticleManager(rsGame); + } + + //call the chat manager + chatManager.updateNetwork(); + + //check for quiting status + if(NetworkManager::getInstance().getGameNetworkInterface()->getQuit()){ + quitGame(); + } + + //update auto test + if(Config::getInstance().getBool("AutoTest")){ + AutoTest::getInstance().updateGame(this); + } +} + +void Game::updateCamera(){ + gameCamera.update(); +} + + +// ==================== render ==================== + +//render +void Game::render(){ + renderFps++; + render3d(); + render2d(); + Renderer::getInstance().swapBuffers(); +} + +// ==================== tick ==================== + +void Game::tick(){ + lastUpdateFps= updateFps; + lastRenderFps= renderFps; + updateFps= 0; + renderFps= 0; + + //Win/lose check + checkWinner(); + gui.tick(); +} + + +// ==================== events ==================== + +void Game::mouseDownLeft(int x, int y){ + + Map *map= world.getMap(); + const Metrics &metrics= Metrics::getInstance(); + NetworkManager &networkManager= NetworkManager::getInstance(); + bool messageBoxClick= false; + + //scrip message box, only if the exit box is not enabled + if(!mainMessageBox.getEnabled() && scriptManager.getMessageBox()->getEnabled()){ + int button= 1; + if(scriptManager.getMessageBox()->mouseClick(x, y, button)){ + scriptManager.onMessageBoxOk(); + messageBoxClick= true; + } + } + + //minimap panel + if(!messageBoxClick){ + if(metrics.isInMinimap(x, y) && !gui.isSelectingPos()){ + + int xm= x - metrics.getMinimapX(); + int ym= y - metrics.getMinimapY(); + int xCell= static_cast(xm * (static_cast(map->getW()) / metrics.getMinimapW())); + int yCell= static_cast(map->getH() - ym * (static_cast(map->getH()) / metrics.getMinimapH())); + + if(map->isInside(xCell, yCell)){ + if(!gui.isSelectingPos()){ + gameCamera.setPos(Vec2f(static_cast(xCell), static_cast(yCell))); + } + } + } + + //display panel + else if(metrics.isInDisplay(x, y) && !gui.isSelectingPos()){ + int xd= x - metrics.getDisplayX(); + int yd= y - metrics.getDisplayY(); + if(gui.mouseValid(xd, yd)){ + gui.mouseDownLeftDisplay(xd, yd); + } + else{ + gui.mouseDownLeftGraphics(x, y); + } + } + + //graphics panel + else{ + gui.mouseDownLeftGraphics(x, y); + } + } + + //exit message box, has to be the last thing to do in this function + if(mainMessageBox.getEnabled()){ + int button= 1; + if(mainMessageBox.mouseClick(x, y, button)){ + if(button==1){ + networkManager.getGameNetworkInterface()->quitGame(); + quitGame(); + } + else{ + //close message box + mainMessageBox.setEnabled(false); + } + } + } +} + +void Game::mouseDownRight(int x, int y){ + gui.mouseDownRightGraphics(x, y); +} + +void Game::mouseUpLeft(int x, int y){ + gui.mouseUpLeftGraphics(x, y); +} + +void Game::mouseDoubleClickLeft(int x, int y){ + const Metrics &metrics= Metrics::getInstance(); + + //display panel + if(metrics.isInDisplay(x, y) && !gui.isSelectingPos()){ + int xd= x - metrics.getDisplayX(); + int yd= y - metrics.getDisplayY(); + if(gui.mouseValid(xd, yd)){ + return; + } + } + + //graphics panel + gui.mouseDoubleClickLeftGraphics(x, y); +} + +void Game::mouseMove(int x, int y, const MouseState *ms){ + + const Metrics &metrics= Metrics::getInstance(); + + mouseX= x; + mouseY= y; + + //main window + if(y<10){ + gameCamera.setMoveZ(-1); + } + else if(y> metrics.getVirtualH()-10){ + gameCamera.setMoveZ(1); + } + else{ + gameCamera.stopMoveZ(); + } + + if(x<10){ + gameCamera.setMoveX(-1); + } + else if(x> metrics.getVirtualW()-10){ + gameCamera.setMoveX(1); + } + else{ + gameCamera.stopMoveX(); + } + + if(mainMessageBox.getEnabled()){ + mainMessageBox.mouseMove(x, y); + } + + if(scriptManager.getMessageBox()->getEnabled()){ + scriptManager.getMessageBox()->mouseMove(x, y); + } + + //graphics + gui.mouseMoveGraphics(x, y); + + //display + if(metrics.isInDisplay(x, y) && !gui.isSelecting() && !gui.isSelectingPos()){ + if(!gui.isSelectingPos()){ + gui.mouseMoveDisplay(x - metrics.getDisplayX(), y - metrics.getDisplayY()); + } + } +} + +void Game::keyDown(char key){ + + Lang &lang= Lang::getInstance(); + bool speedChangesAllowed= !NetworkManager::getInstance().isNetworkGame(); + + //send ley to the chat manager + chatManager.keyDown(key); + + if(!chatManager.getEditEnabled()){ + + if(key=='N'){ + renderNetworkStatus= true; + } + else if(key=='E'){ + for(int i=0; i<100; ++i){ + string path= "screens/screen" + intToStr(i) + ".tga"; + + FILE *f= fopen(path.c_str(), "rb"); + if(f==NULL){ + Renderer::getInstance().saveScreen(path); + break; + } + else{ + fclose(f); + } + } + } + + //move camera left + else if(key==vkLeft){ + gameCamera.setMoveX(-1); + } + + //move camera right + else if(key==vkRight){ + gameCamera.setMoveX(1); + } + + //move camera up + else if(key==vkUp){ + gameCamera.setMoveZ(1); + } + + //move camera down + else if(key==vkDown){ + gameCamera.setMoveZ(-1); + } + + //change camera mode + else if(key=='F'){ + gameCamera.switchState(); + string stateString= gameCamera.getState()==GameCamera::sGame? lang.get("GameCamera"): lang.get("FreeCamera"); + console.addLine(lang.get("CameraModeSet")+" "+ stateString); + } + + //pause + else if(key=='P'){ + if(speedChangesAllowed){ + if(paused){ + console.addLine(lang.get("GameResumed")); + paused= false; + } + else{ + console.addLine(lang.get("GamePaused")); + paused= true; + } + } + } + + //increment speed + else if(key==vkAdd){ + if(speedChangesAllowed){ + incSpeed(); + } + } + + //decrement speed + else if(key==vkSubtract){ + if(speedChangesAllowed){ + decSpeed(); + } + } + + //exit + else if(key==vkEscape){ + showMessageBox(lang.get("ExitGame?"), "", true); + } + + //group + else if(key>='0' && key<'0'+Selection::maxGroups){ + gui.groupKey(key-'0'); + } + + //hotkeys + if(gameCamera.getState()==GameCamera::sGame){ + gui.hotKey(key); + } + else{ + //rotate camera leftt + if(key=='A'){ + gameCamera.setRotate(-1); + } + + //rotate camera right + else if(key=='D'){ + gameCamera.setRotate(1); + } + + //camera up + else if(key=='S'){ + gameCamera.setMoveY(1); + } + + //camera down + else if(key=='W'){ + gameCamera.setMoveY(-1); + } + } + } +} + +void Game::keyUp(char key){ + + if(!chatManager.getEditEnabled()){ + switch(key){ + case 'N': + renderNetworkStatus= false; + break; + case 'A': + case 'D': + gameCamera.setRotate(0); + break; + + case 'W': + case 'S': + gameCamera.stopMoveY(); + break; + + case vkUp: + case vkDown: + gameCamera.stopMoveZ(); + break; + + case vkLeft: + case vkRight: + gameCamera.stopMoveX(); + break; + } + } +} + +void Game::keyPress(char c){ + chatManager.keyPress(c); +} + +void Game::quitGame(){ + program->setState(new BattleEnd(program, world.getStats())); +} + +// ==================== PRIVATE ==================== + +// ==================== render ==================== + +void Game::render3d(){ + + Renderer &renderer= Renderer::getInstance(); + + //init + renderer.reset3d(); + renderer.computeVisibleQuad(); + renderer.loadGameCameraMatrix(); + renderer.setupLighting(); + + //shadow map + renderer.renderShadowsToTexture(); + + //clear buffers + renderer.clearBuffers(); + + //surface + renderer.renderSurface(); + + //selection circles + renderer.renderSelectionEffects(); + + //units + renderer.renderUnits(); + + //objects + renderer.renderObjects(); + + //water + renderer.renderWater(); + renderer.renderWaterEffects(); + + //particles + renderer.renderParticleManager(rsGame); + + //mouse 3d + renderer.renderMouse3d(); +} + +void Game::render2d(){ + Renderer &renderer= Renderer::getInstance(); + Config &config= Config::getInstance(); + CoreData &coreData= CoreData::getInstance(); + + //init + renderer.reset2d(); + + //display + renderer.renderDisplay(); + + //minimap + if(!config.getBool("PhotoMode")){ + renderer.renderMinimap(); + } + + //selection + renderer.renderSelectionQuad(); + + //exit message box + if(mainMessageBox.getEnabled()){ + renderer.renderMessageBox(&mainMessageBox); + } + + //script message box + if(!mainMessageBox.getEnabled() && scriptManager.getMessageBoxEnabled()){ + renderer.renderMessageBox(scriptManager.getMessageBox()); + } + + //script display text + if(!scriptManager.getDisplayText().empty() && !scriptManager.getMessageBoxEnabled()){ + renderer.renderText( + scriptManager.getDisplayText(), coreData.getMenuFontNormal(), + Vec3f(1.0f), 200, 680, false); + } + + + renderer.renderChatManager(&chatManager); + + //debug info + if(config.getBool("DebugMode")){ + string str; + + str+= "MouseXY: " + intToStr(mouseX) + "," + intToStr(mouseY)+"\n"; + str+= "PosObjWord: " + intToStr(gui.getPosObjWorld().x) + "," + intToStr(gui.getPosObjWorld().y)+"\n"; + str+= "Render FPS: "+intToStr(lastRenderFps)+"\n"; + str+= "Update FPS: "+intToStr(lastUpdateFps)+"\n"; + str+= "GameCamera pos: "+floatToStr(gameCamera.getPos().x)+","+floatToStr(gameCamera.getPos().y)+","+floatToStr(gameCamera.getPos().z)+"\n"; + str+= "Time: "+floatToStr(world.getTimeFlow()->getTime())+"\n"; + str+= "Triangle count: "+intToStr(renderer.getTriangleCount())+"\n"; + str+= "Vertex count: "+intToStr(renderer.getPointCount())+"\n"; + str+= "Frame count:"+intToStr(world.getFrameCount())+"\n"; + + //visible quad + Quad2i visibleQuad= renderer.getVisibleQuad(); + + str+= "Visible quad: "; + for(int i= 0; i<4; ++i){ + str+= "(" + intToStr(visibleQuad.p[i].x) + "," +intToStr(visibleQuad.p[i].y) + ") "; + } + str+= "\n"; + str+= "Visible quad area: " + floatToStr(visibleQuad.area()) +"\n"; + + // resources + for(int i=0; igetResourceTypeCount(); ++j){ + str+= intToStr(world.getFaction(i)->getResource(j)->getAmount()); + str+=" "; + } + str+="\n"; + } + + renderer.renderText( + str, coreData.getMenuFontNormal(), + Vec3f(1.0f), 10, 500, false); + } + + //network status + if(renderNetworkStatus){ + renderer.renderText( + NetworkManager::getInstance().getGameNetworkInterface()->getNetworkStatus(), + coreData.getMenuFontNormal(), + Vec3f(1.0f), 20, 500, false); + } + + //resource info + if(!config.getBool("PhotoMode")){ + renderer.renderResourceStatus(); + renderer.renderConsole(&console); + } + + //2d mouse + renderer.renderMouse2d(mouseX, mouseY, mouse2d, gui.isSelectingPos()? 1.f: 0.f); +} + + +// ==================== misc ==================== + +void Game::checkWinner(){ + if(!gameOver){ + if(gameSettings.getDefaultVictoryConditions()){ + checkWinnerStandard(); + } + else + { + checkWinnerScripted(); + } + } +} + +void Game::checkWinnerStandard(){ + //lose + bool lose= false; + if(!hasBuilding(world.getThisFaction())){ + lose= true; + for(int i=0; iisAlly(world.getThisFaction())){ + world.getStats()->setVictorious(i); + } + } + gameOver= true; + showLoseMessageBox(); + } + + //win + if(!lose){ + bool win= true; + for(int i=0; iisAlly(world.getThisFaction())){ + win= false; + } + } + } + + //if win + if(win){ + for(int i=0; i< world.getFactionCount(); ++i){ + if(world.getFaction(i)->isAlly(world.getThisFaction())){ + world.getStats()->setVictorious(i); + } + } + gameOver= true; + showWinMessageBox(); + } + } +} + +void Game::checkWinnerScripted(){ + if(scriptManager.getGameOver()){ + gameOver= true; + for(int i= 0; igetWinner()){ + world.getStats()->setVictorious(i); + } + } + if(scriptManager.getPlayerModifiers(world.getThisFactionIndex())->getWinner()){ + showWinMessageBox(); + } + else{ + showLoseMessageBox(); + } + } +} + +bool Game::hasBuilding(const Faction *faction){ + for(int i=0; igetUnitCount(); ++i){ + if(faction->getUnit(i)->getType()->hasSkillClass(scBeBuilt)){ + return true; + } + } + return false; +} + +void Game::incSpeed(){ + Lang &lang= Lang::getInstance(); + switch(speed){ + case sSlow: + speed= sNormal; + console.addLine(lang.get("GameSpeedSet")+" "+lang.get("Normal")); + break; + case sNormal: + speed= sFast; + console.addLine(lang.get("GameSpeedSet")+" "+lang.get("Fast")); + break; + default: + break; + } +} + +void Game::decSpeed(){ + Lang &lang= Lang::getInstance(); + switch(speed){ + case sNormal: + speed= sSlow; + console.addLine(lang.get("GameSpeedSet")+" "+lang.get("Slow")); + break; + case sFast: + speed= sNormal; + console.addLine(lang.get("GameSpeedSet")+" "+lang.get("Normal")); + break; + default: + break; + } +} + +int Game::getUpdateLoops(){ + if(paused){ + return 0; + } + else if(speed==sFast){ + return Config::getInstance().getInt("FastSpeedLoops"); + } + else if(speed==sSlow){ + return updateFps % 2 == 0? 1: 0; + } + return 1; +} + +void Game::showLoseMessageBox(){ + Lang &lang= Lang::getInstance(); + showMessageBox(lang.get("YouLose")+", "+lang.get("ExitGame?"), lang.get("BattleOver"), false); +} + +void Game::showWinMessageBox(){ + Lang &lang= Lang::getInstance(); + showMessageBox(lang.get("YouWin")+", "+lang.get("ExitGame?"), lang.get("BattleOver"), false); +} + +void Game::showMessageBox(const string &text, const string &header, bool toggle){ + if(!toggle){ + mainMessageBox.setEnabled(false); + } + + if(!mainMessageBox.getEnabled()){ + mainMessageBox.setText(text); + mainMessageBox.setHeader(header); + mainMessageBox.setEnabled(true); + } + else{ + mainMessageBox.setEnabled(false); + } +} + +}}//end namespace diff --git a/source/glest_game/game/game.h b/source/glest_game/game/game.h new file mode 100644 index 00000000..51631374 --- /dev/null +++ b/source/glest_game/game/game.h @@ -0,0 +1,135 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_GAME_H_ +#define _GLEST_GAME_GAME_H_ + +#include + +#include "gui.h" +#include "game_camera.h" +#include "world.h" +#include "ai_interface.h" +#include "program.h" +#include "chat_manager.h" +#include "script_manager.h" +#include "game_settings.h" + +using std::vector; + +namespace Glest{ namespace Game{ + +class GraphicMessageBox; + +// ===================================================== +// class Game +// +// Main game class +// ===================================================== + +class Game: public ProgramState{ +public: + enum Speed{ + sFast, + sNormal, + sSlow + }; + +private: + typedef vector Ais; + typedef vector AiInterfaces; + +private: + //main data + World world; + AiInterfaces aiInterfaces; + Gui gui; + GameCamera gameCamera; + Commander commander; + Console console; + ChatManager chatManager; + ScriptManager scriptManager; + + //misc + Checksum checksum; + string loadingText; + int mouse2d; + int mouseX, mouseY; //coords win32Api + int updateFps, lastUpdateFps; + int renderFps, lastRenderFps; + bool paused; + bool gameOver; + bool renderNetworkStatus; + Speed speed; + GraphicMessageBox mainMessageBox; + + //misc ptr + ParticleSystem *weatherParticleSystem; + GameSettings gameSettings; + +public: + Game(Program *program, const GameSettings *gameSettings); + ~Game(); + + //get + GameSettings *getGameSettings() {return &gameSettings;} + + const GameCamera *getGameCamera() const {return &gameCamera;} + GameCamera *getGameCamera() {return &gameCamera;} + const Commander *getCommander() const {return &commander;} + Gui *getGui() {return &gui;} + const Gui *getGui() const {return &gui;} + Commander *getCommander() {return &commander;} + Console *getConsole() {return &console;} + ScriptManager *getScriptManager() {return &scriptManager;} + World *getWorld() {return &world;} + const World *getWorld() const {return &world;} + + //init + virtual void load(); + virtual void init(); + virtual void update(); + virtual void updateCamera(); + virtual void render(); + virtual void tick(); + + //event managing + virtual void keyDown(char key); + virtual void keyUp(char key); + virtual void keyPress(char c); + virtual void mouseDownLeft(int x, int y); + virtual void mouseDownRight(int x, int y); + virtual void mouseUpLeft(int x, int y); + virtual void mouseDoubleClickLeft(int x, int y); + virtual void mouseMove(int x, int y, const MouseState *mouseState); + + void quitGame(); +private: + //render + void render3d(); + void render2d(); + + //misc + void checkWinner(); + void checkWinnerStandard(); + void checkWinnerScripted(); + bool hasBuilding(const Faction *faction); + void incSpeed(); + void decSpeed(); + int getUpdateLoops(); + void showLoseMessageBox(); + void showWinMessageBox(); + void showMessageBox(const string &text, const string &header, bool toggle); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/game/game_camera.cpp b/source/glest_game/game/game_camera.cpp new file mode 100644 index 00000000..b409a6df --- /dev/null +++ b/source/glest_game/game/game_camera.cpp @@ -0,0 +1,235 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "game_camera.h" + +#include "config.h" +#include "game_constants.h" +#include "metrics.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class GameCamera +// ===================================================== + +// ================== PUBLIC ===================== + +const float GameCamera::startingVAng= -60.f; +const float GameCamera::startingHAng= 0.f; +const float GameCamera::maxHeight= 20.f; +const float GameCamera::minHeight= 10.f; +const float GameCamera::transitionSpeed= 0.01f; +const float GameCamera::centerOffsetZ= 8.0f; + +// ================= Constructor ================= + +GameCamera::GameCamera(){ + this->pos= Vec3f(0.f, maxHeight, 0.f); + state= sGame; + + //config + speed= 15.f / GameConstants::cameraFps; + clampBounds= !Config::getInstance().getBool("PhotoMode"); + + vAng= startingVAng; + hAng= startingHAng; + rotate=0; + stateTransition= 1.f; + + move= Vec3f(0.f); + stopMove= Vec3b(false); +} + +void GameCamera::init(int limitX, int limitY){ + this->limitX= limitX; + this->limitY= limitY; +} + +// ==================== Misc ===================== + +void GameCamera::setPos(Vec2f pos){ + this->pos= Vec3f(pos.x, this->pos.y, pos.y); + clampPosXZ(0.0f, limitX, 0.0f, limitY); +} + +void GameCamera::update(){ + + //move speed XYZ + if(stopMove.x){ + move.x *= 0.9f; + } + if(stopMove.y){ + move.y *= 0.9f; + } + if(stopMove.z){ + move.z *= 0.9f; + } + + //move XZ + if(move.z!=0){ + moveForwardH(speed*move.z); + } + if(move.x!=0){ + moveSideH(speed*move.x); + } + + //free state + if(state==sFree){ + if(fabs(rotate) == 1){ + rotateHV(speed*5*rotate, 0); + } + if(move.y>0){ + moveUp(speed * move.y); + if(clampBounds && pos.yminHeight){ + rotateHV(0.f, -speed * 1.7f * move.y); + } + } + } + + //game state + else if(stateTransition<1.f){ + if(lastHAng<180){ + hAng= lastHAng + (stateTransition)*(startingHAng-lastHAng); + } + else{ + hAng= lastHAng + (stateTransition)*(startingHAng+360-lastHAng); + } + vAng= lastVAng*(1.f-stateTransition)+startingVAng*stateTransition; + pos.y= lastPos.y+maxHeight*stateTransition; + stateTransition+= transitionSpeed; + } + + if(clampBounds){ + clampPosXYZ(0.0f, limitX, minHeight, maxHeight, 0.0f, limitY); + } +} + +Quad2i GameCamera::computeVisibleQuad() const{ + float aspectRatio = Metrics::getInstance().getAspectRatio(); + Vec2i v= Vec2i(static_cast(pos.x), static_cast(pos.z)); + + //free state + if(state==sFree || stateTransition<1.f){ + const float nearDist= 20.f; + const float farDist= 90.f; + const float fov= 65.0f * aspectRatio * 0.5f; + const float dist= 20.f; + + Vec2f v(sin(degToRad(180-hAng)), cos(degToRad(180-hAng))); + Vec2f v1(sin(degToRad(180-hAng-fov)), cos(degToRad(180-hAng-fov))); + Vec2f v2(sin(degToRad(180-hAng+fov)), cos(degToRad(180-hAng+fov))); + v.normalize(); + v1.normalize(); + v2.normalize(); + + Vec2f p= Vec2f(pos.x, pos.z)-v*dist; + Vec2i p1(static_cast(p.x+v1.x*nearDist), static_cast(p.y+v1.y*nearDist)); + Vec2i p2(static_cast(p.x+v1.x*farDist), static_cast(p.y+v1.y*farDist)); + Vec2i p3(static_cast(p.x+v2.x*nearDist), static_cast(p.y+v2.y*nearDist)); + Vec2i p4(static_cast(p.x+v2.x*farDist), static_cast(p.y+v2.y*farDist)); + + if(hAng>=135 && hAng<=225){ + return Quad2i(p1, p2, p3, p4); + } + if(hAng>=45 && hAng<=135){ + return Quad2i(p3, p1, p4, p2); + } + if(hAng>=225 && hAng<=315) { + return Quad2i(p2, p4, p1, p3); + } + return Quad2i(p4, p3, p2, p1); + + } + + //game state + else{ + static const int widthNear= 19 * aspectRatio; + static const int widthFar= 23 * aspectRatio; + static const int near= 5; + static const int far= 40; + + return Quad2i( + Vec2i(v.x-widthNear, v.y-far), + Vec2i(v.x-widthFar, v.y+near), + Vec2i(v.x+widthNear, v.y-far), + Vec2i(v.x+widthFar, v.y+near)); + } +} + +void GameCamera::switchState(){ + if(state==sGame){ + state= sFree; + } + else{ + state= sGame; + stateTransition= 0.f; + lastHAng= hAng; + lastVAng= vAng; + lastPos= pos; + } +} + +void GameCamera::centerXZ(float x, float z){ + pos.x= x; + pos.z= z+centerOffsetZ; +} + +// ==================== PRIVATE ==================== + +void GameCamera::clampPosXZ(float x1, float x2, float z1, float z2){ + if(pos.x(x1); + if(pos.z(z1); + if(pos.x>x2) pos.x= static_cast(x2); + if(pos.z>z2) pos.z= static_cast(z2); +} + +void GameCamera::clampPosXYZ(float x1, float x2, float y1, float y2, float z1, float z2){ + if(pos.xx2) pos.x= x2; + if(pos.y>y2) pos.y= y2; + if(pos.z>z2) pos.z= z2; +} + +void GameCamera::rotateHV(float h, float v){ + vAng+=v; + hAng+=h; + if(hAng>360.f) hAng-=360.f; + if(hAng<0.f) hAng+=360.f; +} + +//move camera forwad but never change heightFactor +void GameCamera::moveForwardH(float d){ + pos=pos + Vec3f(sin(degToRad(hAng)), 0.f, -cos(degToRad(hAng))) * d; +} + +//move camera to a side but never change heightFactor +void GameCamera::moveSideH(float d){ + pos=pos + Vec3f(sin(degToRad(hAng+90)), 0.f, -cos(degToRad(hAng+90))) * d; + +} + +void GameCamera::moveUp(float d){ + pos.y+= d; +} + +}}//end namespace diff --git a/source/glest_game/game/game_camera.h b/source/glest_game/game/game_camera.h new file mode 100644 index 00000000..b43b09c1 --- /dev/null +++ b/source/glest_game/game/game_camera.h @@ -0,0 +1,114 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_GAMECAMERA_H_ +#define _GLEST_GAME_GAMECAMERA_H_ + +#include "vec.h" +#include "math_util.h" + +namespace Glest{ namespace Game{ + +using Shared::Graphics::Quad2i; +using Shared::Graphics::Vec3f; +using Shared::Graphics::Vec3b; +using Shared::Graphics::Vec2f; + +class Config; + +// ===================================================== +// class GameCamera +// +/// A basic camera that holds information about the game view +// ===================================================== + +class GameCamera{ +public: + static const float startingVAng; + static const float startingHAng; + static const float maxHeight; + static const float minHeight; + static const float transitionSpeed; + static const float centerOffsetZ; + +public: + enum State{ + sGame, + sFree + }; + +private: + Vec3f pos; + Vec3f lastPos; + + float hAng; //YZ plane positive -Z axis + float vAng; //XZ plane positive +Z axis + float lastHAng; + float lastVAng; + + float rotate; + + Vec3f move; + Vec3b stopMove; + + float stateTransition; + State state; + + int limitX; + int limitY; + + + //config + float speed; + bool clampBounds; + +public: + GameCamera(); + + void init(int limitX, int limitY); + + //get + float getHAng() const {return hAng;}; + float getVAng() const {return vAng;} + State getState() const {return state;} + const Vec3f &getPos() const {return pos;} + + //set + void setRotate(int rotate) {this->rotate= rotate;} + void setPos(Vec2f pos); + + void setMoveX(float f) {this->stopMove.x= false; this->move.x= f;} + void setMoveY(float f) {this->stopMove.y= false; this->move.y= f;} + void setMoveZ(float f) {this->stopMove.z= false; this->move.z= f;} + + void stopMoveX() {this->stopMove.x= true;} + void stopMoveY() {this->stopMove.y= true;} + void stopMoveZ() {this->stopMove.z= true;} + + //other + void update(); + Quad2i computeVisibleQuad() const; + void switchState(); + + void centerXZ(float x, float z); + +private: + void clampPosXYZ(float x1, float x2, float y1, float y2, float z1, float z2); + void clampPosXZ(float x1, float x2, float z1, float z2); + void rotateHV(float h, float v); + void moveForwardH(float dist); + void moveSideH(float dist); + void moveUp(float dist); +}; + +}} //end namespace + +#endif diff --git a/source/glest_game/game/game_constants.h b/source/glest_game/game/game_constants.h new file mode 100644 index 00000000..87e5ca1e --- /dev/null +++ b/source/glest_game/game/game_constants.h @@ -0,0 +1,43 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_GAMECONSTANTS_H_ +#define _GLEST_GAME_GAMECONSTANTS_H_ + +namespace Glest{ namespace Game{ + +// ===================================================== +// class GameConstants +// ===================================================== + +enum ControlType{ + ctClosed, + ctCpuEasy, + ctCpu, + ctCpuUltra, + ctCpuMega, + ctNetwork, + ctHuman +}; + +class GameConstants{ +public: + static const int maxPlayers= 4; + static const int serverPort= 61357; + static const int updateFps= 40; + static const int cameraFps= 100; + static const int networkFramePeriod= 10; + static const int networkExtraLatency= 200; +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/game/game_settings.h b/source/glest_game/game/game_settings.h new file mode 100644 index 00000000..ef5de454 --- /dev/null +++ b/source/glest_game/game/game_settings.h @@ -0,0 +1,88 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_GAMESETTINGS_H_ +#define _GLEST_GAME_GAMESETTINGS_H_ + +#include "game_constants.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class GameSettings +// ===================================================== + +class GameSettings{ +private: + string description; + string map; + string tileset; + string tech; + string scenario; + string scenarioDir; + string factionTypeNames[GameConstants::maxPlayers]; //faction names + + ControlType factionControls[GameConstants::maxPlayers]; + + int thisFactionIndex; + int factionCount; + int teams[GameConstants::maxPlayers]; + int startLocationIndex[GameConstants::maxPlayers]; + + bool defaultUnits; + bool defaultResources; + bool defaultVictoryConditions; + +public: + //get + const string &getDescription() const {return description;} + const string &getMap() const {return map;} + const string &getTileset() const {return tileset;} + const string &getTech() const {return tech;} + const string &getScenario() const {return scenario;} + const string &getScenarioDir() const {return scenarioDir;} + const string &getFactionTypeName(int factionIndex) const {return factionTypeNames[factionIndex];} + ControlType getFactionControl(int factionIndex) const {return factionControls[factionIndex];} + + int getThisFactionIndex() const {return thisFactionIndex;} + int getFactionCount() const {return factionCount;} + int getTeam(int factionIndex) const {return teams[factionIndex];} + int getStartLocationIndex(int factionIndex) const {return startLocationIndex[factionIndex];} + + bool getDefaultUnits() const {return defaultUnits;} + bool getDefaultResources() const {return defaultResources;} + bool getDefaultVictoryConditions() const {return defaultVictoryConditions;} + + //set + void setDescription(const string& description) {this->description= description;} + void setMap(const string& map) {this->map= map;} + void setTileset(const string& tileset) {this->tileset= tileset;} + void setTech(const string& tech) {this->tech= tech;} + void setScenario(const string& scenario) {this->scenario= scenario;} + void setScenarioDir(const string& scenarioDir) {this->scenarioDir= scenarioDir;} + + void setFactionTypeName(int factionIndex, const string& factionTypeName) {this->factionTypeNames[factionIndex]= factionTypeName;} + + void setFactionControl(int factionIndex, ControlType controller) {this->factionControls[factionIndex]= controller;} + + void setThisFactionIndex(int thisFactionIndex) {this->thisFactionIndex= thisFactionIndex;} + void setFactionCount(int factionCount) {this->factionCount= factionCount;} + void setTeam(int factionIndex, int team) {this->teams[factionIndex]= team;} + void setStartLocationIndex(int factionIndex, int startLocationIndex) {this->startLocationIndex[factionIndex]= startLocationIndex;} + + void setDefaultUnits(bool defaultUnits) {this->defaultUnits= defaultUnits;} + void setDefaultResources(bool defaultResources) {this->defaultResources= defaultResources;} + void setDefaultVictoryConditions(bool defaultVictoryConditions) {this->defaultVictoryConditions= defaultVictoryConditions;} +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/game/script_manager.cpp b/source/glest_game/game/script_manager.cpp new file mode 100644 index 00000000..19f9cc18 --- /dev/null +++ b/source/glest_game/game/script_manager.cpp @@ -0,0 +1,400 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "script_manager.h" + +#include "world.h" +#include "lang.h" +#include "game_camera.h" + +#include "leak_dumper.h" + +using namespace Shared::Platform; +using namespace Shared::Lua; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class PlayerModifiers +// ===================================================== + +PlayerModifiers::PlayerModifiers(){ + winner= false; + aiEnabled= true; +} + +// ===================================================== +// class ScriptManager +// ===================================================== + +ScriptManager* ScriptManager::thisScriptManager= NULL; +const int ScriptManager::messageWrapCount= 30; +const int ScriptManager::displayTextWrapCount= 64; + +void ScriptManager::init(World* world, GameCamera *gameCamera){ + const Scenario* scenario= world->getScenario(); + + this->world= world; + this->gameCamera= gameCamera; + + //set static instance + thisScriptManager= this; + + //register functions + luaScript.registerFunction(showMessage, "showMessage"); + luaScript.registerFunction(setDisplayText, "setDisplayText"); + luaScript.registerFunction(clearDisplayText, "clearDisplayText"); + luaScript.registerFunction(setCameraPosition, "setCameraPosition"); + luaScript.registerFunction(createUnit, "createUnit"); + luaScript.registerFunction(giveResource, "giveResource"); + luaScript.registerFunction(givePositionCommand, "givePositionCommand"); + luaScript.registerFunction(giveProductionCommand, "giveProductionCommand"); + luaScript.registerFunction(giveUpgradeCommand, "giveUpgradeCommand"); + luaScript.registerFunction(disableAi, "disableAi"); + luaScript.registerFunction(setPlayerAsWinner, "setPlayerAsWinner"); + luaScript.registerFunction(endGame, "endGame"); + + luaScript.registerFunction(getStartLocation, "startLocation"); + luaScript.registerFunction(getUnitPosition, "unitPosition"); + luaScript.registerFunction(getUnitFaction, "unitFaction"); + luaScript.registerFunction(getResourceAmount, "resourceAmount"); + luaScript.registerFunction(getLastCreatedUnitName, "lastCreatedUnitName"); + luaScript.registerFunction(getLastCreatedUnitId, "lastCreatedUnit"); + luaScript.registerFunction(getLastDeadUnitName, "lastDeadUnitName"); + luaScript.registerFunction(getLastDeadUnitId, "lastDeadUnit"); + luaScript.registerFunction(getUnitCount, "unitCount"); + luaScript.registerFunction(getUnitCountOfType, "unitCountOfType"); + + + //load code + for(int i= 0; igetScriptCount(); ++i){ + const Script* script= scenario->getScript(i); + luaScript.loadCode("function " + script->getName() + "()" + script->getCode() + "end\n", script->getName()); + } + + //setup message box + messageBox.init( Lang::getInstance().get("Ok") ); + messageBox.setEnabled(false); + + //last created unit + lastCreatedUnitId= -1; + lastDeadUnitId= -1; + gameOver= false; + + //call startup function + luaScript.beginCall("startup"); + luaScript.endCall(); +} + +// ========================== events =============================================== + +void ScriptManager::onMessageBoxOk(){ + Lang &lang= Lang::getInstance(); + + if(!messageQueue.empty()){ + messageQueue.pop(); + if(!messageQueue.empty()){ + messageBox.setText(wrapString(lang.getScenarioString(messageQueue.front().getText()), messageWrapCount)); + messageBox.setHeader(lang.getScenarioString(messageQueue.front().getHeader())); + } + } +} + +void ScriptManager::onResourceHarvested(){ + luaScript.beginCall("resourceHarvested"); + luaScript.endCall(); +} + +void ScriptManager::onUnitCreated(const Unit* unit){ + lastCreatedUnitName= unit->getType()->getName(); + lastCreatedUnitId= unit->getId(); + luaScript.beginCall("unitCreated"); + luaScript.endCall(); + luaScript.beginCall("unitCreatedOfType_"+unit->getType()->getName()); + luaScript.endCall(); +} + +void ScriptManager::onUnitDied(const Unit* unit){ + lastDeadUnitName= unit->getType()->getName(); + lastDeadUnitId= unit->getId(); + luaScript.beginCall("unitDied"); + luaScript.endCall(); +} + +// ========================== lua wrappers =============================================== + +string ScriptManager::wrapString(const string &str, int wrapCount){ + + string returnString; + + int letterCount= 0; + for(int i= 0; iwrapCount && str[i]==' '){ + returnString+= '\n'; + letterCount= 0; + } + else + { + returnString+= str[i]; + } + ++letterCount; + } + + return returnString; +} + +void ScriptManager::showMessage(const string &text, const string &header){ + Lang &lang= Lang::getInstance(); + + messageQueue.push(ScriptManagerMessage(text, header)); + messageBox.setEnabled(true); + messageBox.setText(wrapString(lang.getScenarioString(messageQueue.front().getText()), messageWrapCount)); + messageBox.setHeader(lang.getScenarioString(messageQueue.front().getHeader())); +} + +void ScriptManager::clearDisplayText(){ + displayText= ""; +} + +void ScriptManager::setDisplayText(const string &text){ + displayText= wrapString(Lang::getInstance().getScenarioString(text), displayTextWrapCount); +} + +void ScriptManager::setCameraPosition(const Vec2i &pos){ + gameCamera->centerXZ(pos.x, pos.y); +} + +void ScriptManager::createUnit(const string &unitName, int factionIndex, Vec2i pos){ + world->createUnit(unitName, factionIndex, pos); +} + +void ScriptManager::giveResource(const string &resourceName, int factionIndex, int amount){ + world->giveResource(resourceName, factionIndex, amount); +} + +void ScriptManager::givePositionCommand(int unitId, const string &commandName, const Vec2i &pos){ + world->givePositionCommand(unitId, commandName, pos); +} + +void ScriptManager::giveProductionCommand(int unitId, const string &producedName){ + world->giveProductionCommand(unitId, producedName); +} + +void ScriptManager::giveUpgradeCommand(int unitId, const string &producedName){ + world->giveUpgradeCommand(unitId, producedName); +} + +void ScriptManager::disableAi(int factionIndex){ + if(factionIndexgetStartLocation(factionIndex); +} + + +Vec2i ScriptManager::getUnitPosition(int unitId){ + return world->getUnitPosition(unitId); +} + +int ScriptManager::getUnitFaction(int unitId){ + return world->getUnitFactionIndex(unitId); +} + +int ScriptManager::getResourceAmount(const string &resourceName, int factionIndex){ + return world->getResourceAmount(resourceName, factionIndex); +} + +const string &ScriptManager::getLastCreatedUnitName(){ + return lastCreatedUnitName; +} + +int ScriptManager::getLastCreatedUnitId(){ + return lastCreatedUnitId; +} + +const string &ScriptManager::getLastDeadUnitName(){ + return lastDeadUnitName; +} + +int ScriptManager::getLastDeadUnitId(){ + return lastDeadUnitId; +} + +int ScriptManager::getUnitCount(int factionIndex){ + return world->getUnitCount(factionIndex); +} + +int ScriptManager::getUnitCountOfType(int factionIndex, const string &typeName){ + return world->getUnitCountOfType(factionIndex, typeName); +} + +// ========================== lua callbacks =============================================== + +int ScriptManager::showMessage(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + thisScriptManager->showMessage(luaArguments.getString(-2), luaArguments.getString(-1)); + return luaArguments.getReturnCount(); +} + +int ScriptManager::setDisplayText(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + thisScriptManager->setDisplayText(luaArguments.getString(-1)); + return luaArguments.getReturnCount(); +} + +int ScriptManager::clearDisplayText(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + thisScriptManager->clearDisplayText(); + return luaArguments.getReturnCount(); +} + +int ScriptManager::setCameraPosition(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + thisScriptManager->setCameraPosition(Vec2i(luaArguments.getVec2i(-1))); + return luaArguments.getReturnCount(); +} + +int ScriptManager::createUnit(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + thisScriptManager->createUnit( + luaArguments.getString(-3), + luaArguments.getInt(-2), + luaArguments.getVec2i(-1)); + return luaArguments.getReturnCount(); +} + +int ScriptManager::giveResource(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + thisScriptManager->giveResource(luaArguments.getString(-3), luaArguments.getInt(-2), luaArguments.getInt(-1)); + return luaArguments.getReturnCount(); +} + +int ScriptManager::givePositionCommand(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + thisScriptManager->givePositionCommand( + luaArguments.getInt(-3), + luaArguments.getString(-2), + luaArguments.getVec2i(-1)); + return luaArguments.getReturnCount(); +} + + +int ScriptManager::giveProductionCommand(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + thisScriptManager->giveProductionCommand( + luaArguments.getInt(-2), + luaArguments.getString(-1)); + return luaArguments.getReturnCount(); +} + +int ScriptManager::giveUpgradeCommand(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + thisScriptManager->giveUpgradeCommand( + luaArguments.getInt(-2), + luaArguments.getString(-1)); + return luaArguments.getReturnCount(); +} + +int ScriptManager::disableAi(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + thisScriptManager->disableAi(luaArguments.getInt(-1)); + return luaArguments.getReturnCount(); +} + +int ScriptManager::setPlayerAsWinner(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + thisScriptManager->setPlayerAsWinner(luaArguments.getInt(-1)); + return luaArguments.getReturnCount(); +} + +int ScriptManager::endGame(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + thisScriptManager->endGame(); + return luaArguments.getReturnCount(); +} + +int ScriptManager::getStartLocation(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + Vec2i pos= thisScriptManager->getStartLocation(luaArguments.getInt(-1)); + luaArguments.returnVec2i(pos); + return luaArguments.getReturnCount(); +} + +int ScriptManager::getUnitPosition(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + Vec2i pos= thisScriptManager->getUnitPosition(luaArguments.getInt(-1)); + luaArguments.returnVec2i(pos); + return luaArguments.getReturnCount(); +} + +int ScriptManager::getUnitFaction(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + int factionIndex= thisScriptManager->getUnitFaction(luaArguments.getInt(-1)); + luaArguments.returnInt(factionIndex); + return luaArguments.getReturnCount(); +} + +int ScriptManager::getResourceAmount(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + luaArguments.returnInt(thisScriptManager->getResourceAmount(luaArguments.getString(-2), luaArguments.getInt(-1))); + return luaArguments.getReturnCount(); +} + +int ScriptManager::getLastCreatedUnitName(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + luaArguments.returnString(thisScriptManager->getLastCreatedUnitName()); + return luaArguments.getReturnCount(); +} + +int ScriptManager::getLastCreatedUnitId(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + luaArguments.returnInt(thisScriptManager->getLastCreatedUnitId()); + return luaArguments.getReturnCount(); +} + +int ScriptManager::getLastDeadUnitName(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + luaArguments.returnString(thisScriptManager->getLastDeadUnitName()); + return luaArguments.getReturnCount(); +} + +int ScriptManager::getLastDeadUnitId(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + luaArguments.returnInt(thisScriptManager->getLastDeadUnitId()); + return luaArguments.getReturnCount(); +} + +int ScriptManager::getUnitCount(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + luaArguments.returnInt(thisScriptManager->getUnitCount(luaArguments.getInt(-1))); + return luaArguments.getReturnCount(); +} + +int ScriptManager::getUnitCountOfType(LuaHandle* luaHandle){ + LuaArguments luaArguments(luaHandle); + luaArguments.returnInt(thisScriptManager->getUnitCountOfType(luaArguments.getInt(-2), luaArguments.getString(-1))); + return luaArguments.getReturnCount(); +} + +}}//end namespace diff --git a/source/glest_game/game/script_manager.h b/source/glest_game/game/script_manager.h new file mode 100644 index 00000000..74c2751d --- /dev/null +++ b/source/glest_game/game/script_manager.h @@ -0,0 +1,183 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_SCRIPT_MANAGER_H_ +#define _GLEST_GAME_SCRIPT_MANAGER_H_ + +#include +#include + +#include "lua_script.h" +#include "vec.h" + +#include "components.h" +#include "game_constants.h" + +using std::string; +using std::queue; +using Shared::Graphics::Vec2i; +using Shared::Lua::LuaScript; +using Shared::Lua::LuaHandle; + +namespace Glest{ namespace Game{ + +class World; +class Unit; +class GameCamera; + +// ===================================================== +// class ScriptManagerMessage +// ===================================================== + +class ScriptManagerMessage{ +private: + string text; + string header; + +public: + ScriptManagerMessage(string text, string header) {this->text= text, this->header= header;} + const string &getText() const {return text;} + const string &getHeader() const {return header;} +}; + +class PlayerModifiers{ +public: + PlayerModifiers(); + + void disableAi() {aiEnabled= false;} + void setAsWinner() {winner= true;} + + bool getWinner() const {return winner;} + bool getAiEnabled() const {return aiEnabled;} + +private: + bool winner; + bool aiEnabled; +}; + +// ===================================================== +// class ScriptManager +// ===================================================== + +class ScriptManager{ +private: + typedef queue MessageQueue; + +private: + + //lua + string code; + LuaScript luaScript; + + //world + World *world; + GameCamera *gameCamera; + + //misc + MessageQueue messageQueue; + GraphicMessageBox messageBox; + string displayText; + + //last created unit + string lastCreatedUnitName; + int lastCreatedUnitId; + + //last dead unit + string lastDeadUnitName; + int lastDeadUnitId; + + // end game state + bool gameOver; + PlayerModifiers playerModifiers[GameConstants::maxPlayers]; + +private: + static ScriptManager* thisScriptManager; + +private: + static const int messageWrapCount; + static const int displayTextWrapCount; + +public: + void init(World* world, GameCamera *gameCamera); + + //message box functions + bool getMessageBoxEnabled() const {return !messageQueue.empty();} + GraphicMessageBox* getMessageBox() {return &messageBox;} + string getDisplayText() const {return displayText;} + bool getGameOver() const {return gameOver;} + const PlayerModifiers *getPlayerModifiers(int factionIndex) const {return &playerModifiers[factionIndex];} + + //events + void onMessageBoxOk(); + void onResourceHarvested(); + void onUnitCreated(const Unit* unit); + void onUnitDied(const Unit* unit); + +private: + + string wrapString(const string &str, int wrapCount); + + //wrappers, commands + void showMessage(const string &text, const string &header); + void clearDisplayText(); + void setDisplayText(const string &text); + void setCameraPosition(const Vec2i &pos); + void createUnit(const string &unitName, int factionIndex, Vec2i pos); + void giveResource(const string &resourceName, int factionIndex, int amount); + void givePositionCommand(int unitId, const string &producedName, const Vec2i &pos); + void giveProductionCommand(int unitId, const string &producedName); + void giveUpgradeCommand(int unitId, const string &upgradeName); + void disableAi(int factionIndex); + void setPlayerAsWinner(int factionIndex); + void endGame(); + + //wrappers, queries + Vec2i getStartLocation(int factionIndex); + Vec2i getUnitPosition(int unitId); + int getUnitFaction(int unitId); + int getResourceAmount(const string &resourceName, int factionIndex); + const string &getLastCreatedUnitName(); + int getLastCreatedUnitId(); + const string &getLastDeadUnitName(); + int getLastDeadUnitId(); + int getUnitCount(int factionIndex); + int getUnitCountOfType(int factionIndex, const string &typeName); + + //callbacks, commands + static int showMessage(LuaHandle* luaHandle); + static int setDisplayText(LuaHandle* luaHandle); + static int clearDisplayText(LuaHandle* luaHandle); + static int setCameraPosition(LuaHandle* luaHandle); + static int createUnit(LuaHandle* luaHandle); + static int giveResource(LuaHandle* luaHandle); + static int givePositionCommand(LuaHandle* luaHandle); + static int giveProductionCommand(LuaHandle* luaHandle); + static int giveUpgradeCommand(LuaHandle* luaHandle); + static int disableAi(LuaHandle* luaHandle); + static int setPlayerAsWinner(LuaHandle* luaHandle); + static int endGame(LuaHandle* luaHandle); + + //callbacks, queries + static int getStartLocation(LuaHandle* luaHandle); + static int getUnitPosition(LuaHandle* luaHandle); + static int getUnitFaction(LuaHandle* luaHandle); + static int getResourceAmount(LuaHandle* luaHandle); + static int getLastCreatedUnitName(LuaHandle* luaHandle); + static int getLastCreatedUnitId(LuaHandle* luaHandle); + static int getLastDeadUnitName(LuaHandle* luaHandle); + static int getLastDeadUnitId(LuaHandle* luaHandle); + static int getUnitCount(LuaHandle* luaHandle); + static int getUnitCountOfType(LuaHandle* luaHandle); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/game/stats.cpp b/source/glest_game/game/stats.cpp new file mode 100644 index 00000000..0bc6dba0 --- /dev/null +++ b/source/glest_game/game/stats.cpp @@ -0,0 +1,54 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "stats.h" + +#include "leak_dumper.h" + +namespace Glest{ namespace Game{ + +PlayerStats::PlayerStats(){ + victory= false; + + kills= 0; + deaths= 0; + unitsProduced= 0; + resourcesHarvested= 0; +} + +// ===================================================== +// class Stats +// ===================================================== + +void Stats::init(int factionCount, int thisFactionIndex, const string& description){ + this->thisFactionIndex= thisFactionIndex; + this->factionCount= factionCount; + this->description= description; +} + +void Stats::setVictorious(int playerIndex){ + playerStats[playerIndex].victory= true; +} + +void Stats::kill(int killerFactionIndex, int killedFactionIndex){ + playerStats[killerFactionIndex].kills++; + playerStats[killedFactionIndex].deaths++; +} + +void Stats::produce(int producerFactionIndex){ + playerStats[producerFactionIndex].unitsProduced++; +} + +void Stats::harvest(int harvesterFactionIndex, int amount){ + playerStats[harvesterFactionIndex].resourcesHarvested+= amount; +} + +}}//end namespace diff --git a/source/glest_game/game/stats.h b/source/glest_game/game/stats.h new file mode 100644 index 00000000..bca8c006 --- /dev/null +++ b/source/glest_game/game/stats.h @@ -0,0 +1,79 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_STATS_H_ +#define _GLEST_GAME_STATS_H_ + +#include + +#include "game_constants.h" +#include "faction.h" + +using std::string; + +namespace Glest{ namespace Game{ + +struct PlayerStats{ + PlayerStats(); + + ControlType control; + string factionTypeName; + int teamIndex; + bool victory; + int kills; + int deaths; + int unitsProduced; + int resourcesHarvested; +}; + +// ===================================================== +// class Stats +// +/// Player statistics that are shown after the game ends +// ===================================================== + +class Stats{ +private: + PlayerStats playerStats[GameConstants::maxPlayers]; + + string description; + int factionCount; + int thisFactionIndex; + +public: + void init(int factionCount, int thisFactionIndex, const string &description); + + string getDescription() const {return description;} + int getThisFactionIndex() const {return thisFactionIndex;} + int getFactionCount() const {return factionCount;} + + const string &getFactionTypeName(int factionIndex) const {return playerStats[factionIndex].factionTypeName;} + ControlType getControl(int factionIndex) const {return playerStats[factionIndex].control;} + bool getVictory(int factionIndex) const {return playerStats[factionIndex].victory;} + int getTeam(int factionIndex) const {return playerStats[factionIndex].teamIndex;} + int getKills(int factionIndex) const {return playerStats[factionIndex].kills;} + int getDeaths(int factionIndex) const {return playerStats[factionIndex].deaths;} + int getUnitsProduced(int factionIndex) const {return playerStats[factionIndex].unitsProduced;} + int getResourcesHarvested(int factionIndex) const {return playerStats[factionIndex].resourcesHarvested;} + + void setDescription(const string& description) {this->description = description;} + void setFactionTypeName(int playerIndex, const string& factionTypeName) {playerStats[playerIndex].factionTypeName= factionTypeName;} + void setControl(int playerIndex, ControlType control) {playerStats[playerIndex].control= control;} + void setTeam(int playerIndex, int teamIndex) {playerStats[playerIndex].teamIndex= teamIndex;} + void setVictorious(int playerIndex); + void kill(int killerFactionIndex, int killedFactionIndex); + void produce(int producerFactionIndex); + void harvest(int harvesterFactionIndex, int amount); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/global/config.cpp b/source/glest_game/global/config.cpp new file mode 100644 index 00000000..c61cff8f --- /dev/null +++ b/source/glest_game/global/config.cpp @@ -0,0 +1,73 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "config.h" + +#include "util.h" + +#include "leak_dumper.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Config +// ===================================================== + +Config::Config(){ + properties.load("glest.ini"); +} + +Config &Config::getInstance(){ + static Config config; + return config; +} + +void Config::save(const string &path){ + properties.save(path); +} + +int Config::getInt(const string &key) const{ + return properties.getInt(key); +} + +bool Config::getBool(const string &key) const{ + return properties.getBool(key); +} + +float Config::getFloat(const string &key) const{ + return properties.getFloat(key); +} + +const string &Config::getString(const string &key) const{ + return properties.getString(key); +} + +void Config::setInt(const string &key, int value){ + properties.setInt(key, value); +} + +void Config::setBool(const string &key, bool value){ + properties.setBool(key, value); +} + +void Config::setFloat(const string &key, float value){ + properties.setFloat(key, value); +} + +void Config::setString(const string &key, const string &value){ + properties.setString(key, value); +} + +string Config::toString(){ + return properties.toString(); +} + +}}// end namespace diff --git a/source/glest_game/global/config.h b/source/glest_game/global/config.h new file mode 100644 index 00000000..70976b1d --- /dev/null +++ b/source/glest_game/global/config.h @@ -0,0 +1,53 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_CONFIG_H_ +#define _GLEST_GAME_CONFIG_H_ + +#include "properties.h" + +namespace Glest{ namespace Game{ + +using Shared::Util::Properties; + +// ===================================================== +// class Config +// +// Game configuration +// ===================================================== + +class Config{ +private: + Properties properties; + +private: + Config(); + +public: + static Config &getInstance(); + void save(const string &path="glest.ini"); + + int getInt(const string &key) const; + bool getBool(const string &key) const; + float getFloat(const string &key) const; + const string &getString(const string &key) const; + + void setInt(const string &key, int value); + void setBool(const string &key, bool value); + void setFloat(const string &key, float value); + void setString(const string &key, const string &value); + + string toString(); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/global/core_data.cpp b/source/glest_game/global/core_data.cpp new file mode 100644 index 00000000..887f4fad --- /dev/null +++ b/source/glest_game/global/core_data.cpp @@ -0,0 +1,144 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "core_data.h" + +#include "logger.h" +#include "renderer.h" +#include "graphics_interface.h" +#include "config.h" +#include "util.h" +#include "leak_dumper.h" + +using namespace Shared::Sound; +using namespace Shared::Graphics; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class CoreData +// ===================================================== + +// ===================== PUBLIC ======================== + +CoreData &CoreData::getInstance(){ + static CoreData coreData; + return coreData; +} + +CoreData::~CoreData(){ + deleteValues(waterSounds.getSounds().begin(), waterSounds.getSounds().end()); +} + +void CoreData::load(){ + const string dir="data/core"; + Logger::getInstance().add("Core data"); + + Renderer &renderer= Renderer::getInstance(); + + //textures + backgroundTexture= renderer.newTexture2D(rsGlobal); + backgroundTexture->setMipmap(false); + backgroundTexture->getPixmap()->load(dir+"/menu/textures/back.tga"); + + fireTexture= renderer.newTexture2D(rsGlobal); + fireTexture->setFormat(Texture::fAlpha); + fireTexture->getPixmap()->init(1); + fireTexture->getPixmap()->load(dir+"/misc_textures/fire_particle.tga"); + + snowTexture= renderer.newTexture2D(rsGlobal); + snowTexture->setMipmap(false); + snowTexture->setFormat(Texture::fAlpha); + snowTexture->getPixmap()->init(1); + snowTexture->getPixmap()->load(dir+"/misc_textures/snow_particle.tga"); + + customTexture= renderer.newTexture2D(rsGlobal); + customTexture->getPixmap()->load("data/core/menu/textures/custom_texture.tga"); + + logoTexture= renderer.newTexture2D(rsGlobal); + logoTexture->setMipmap(false); + logoTexture->getPixmap()->load(dir+"/menu/textures/logo.tga"); + + waterSplashTexture= renderer.newTexture2D(rsGlobal); + waterSplashTexture->setFormat(Texture::fAlpha); + waterSplashTexture->getPixmap()->init(1); + waterSplashTexture->getPixmap()->load(dir+"/misc_textures/water_splash.tga"); + + buttonSmallTexture= renderer.newTexture2D(rsGlobal); + buttonSmallTexture->getPixmap()->load(dir+"/menu/textures/button_small.tga"); + + buttonBigTexture= renderer.newTexture2D(rsGlobal); + buttonBigTexture->getPixmap()->load(dir+"/menu/textures/button_big.tga"); + + //display font + Config &config= Config::getInstance(); + string displayFontName= config.getString("FontDisplay"); + + displayFont= renderer.newFont(rsGlobal); + displayFont->setType(displayFontName); + displayFont->setSize(computeFontSize(15)); + + //menu fonts + string menuFontName= config.getString("FontMenu"); + + menuFontSmall= renderer.newFont(rsGlobal); + menuFontSmall->setType(menuFontName); + menuFontSmall->setSize(computeFontSize(12)); + + menuFontNormal= renderer.newFont(rsGlobal); + menuFontNormal->setType(menuFontName); + menuFontNormal->setSize(computeFontSize(16)); + menuFontNormal->setWidth(Font::wBold); + + menuFontBig= renderer.newFont(rsGlobal); + menuFontBig->setType(menuFontName); + menuFontBig->setSize(computeFontSize(20)); + + menuFontVeryBig= renderer.newFont(rsGlobal); + menuFontVeryBig->setType(menuFontName); + menuFontVeryBig->setSize(computeFontSize(25)); + + //console font + string consoleFontName= Config::getInstance().getString("FontConsole"); + + consoleFont= renderer.newFont(rsGlobal); + consoleFont->setType(consoleFontName); + consoleFont->setSize(computeFontSize(16)); + + //sounds + clickSoundA.load(dir+"/menu/sound/click_a.wav"); + clickSoundB.load(dir+"/menu/sound/click_b.wav"); + clickSoundC.load(dir+"/menu/sound/click_c.wav"); + introMusic.open(dir+"/menu/music/intro_music.ogg"); + introMusic.setNext(&menuMusic); + menuMusic.open(dir+"/menu/music/menu_music.ogg"); + menuMusic.setNext(&menuMusic); + waterSounds.resize(6); + for(int i=0; i<6; ++i){ + waterSounds[i]= new StaticSound(); + waterSounds[i]->load(dir+"/water_sounds/water"+intToStr(i)+".wav"); + } + +} + +int CoreData::computeFontSize(int size){ + int screenH= Config::getInstance().getInt("ScreenHeight"); + int rs= size*screenH/750; + if(rs<12){ + rs= 12; + } + return rs; +} + +// ================== PRIVATE ======================== + +}}//end namespace diff --git a/source/glest_game/global/core_data.h b/source/glest_game/global/core_data.h new file mode 100644 index 00000000..923304c9 --- /dev/null +++ b/source/glest_game/global/core_data.h @@ -0,0 +1,98 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_COREDATA_H_ +#define _GLEST_GAME_COREDATA_H_ + +#include + +#include "sound.h" +#include "font.h" +#include "texture.h" +#include "sound_container.h" + +namespace Glest{ namespace Game{ + +using Shared::Graphics::Texture2D; +using Shared::Graphics::Texture3D; +using Shared::Graphics::Font2D; +using Shared::Sound::StrSound; +using Shared::Sound::StaticSound; + +// ===================================================== +// class CoreData +// +/// Data shared ammont all the ProgramStates +// ===================================================== + +class CoreData{ +private: + StrSound introMusic; + StrSound menuMusic; + StaticSound clickSoundA; + StaticSound clickSoundB; + StaticSound clickSoundC; + SoundContainer waterSounds; + + Texture2D *logoTexture; + Texture2D *backgroundTexture; + Texture2D *fireTexture; + Texture2D *snowTexture; + Texture2D *waterSplashTexture; + Texture2D *customTexture; + Texture2D *buttonSmallTexture; + Texture2D *buttonBigTexture; + + Font2D *displayFont; + Font2D *menuFontNormal; + Font2D *menuFontSmall; + Font2D *menuFontBig; + Font2D *menuFontVeryBig; + Font2D *consoleFont; + +public: + static CoreData &getInstance(); + ~CoreData(); + + void load(); + + Texture2D *getBackgroundTexture() const {return backgroundTexture;} + Texture2D *getFireTexture() const {return fireTexture;} + Texture2D *getSnowTexture() const {return snowTexture;} + Texture2D *getLogoTexture() const {return logoTexture;} + Texture2D *getWaterSplashTexture() const {return waterSplashTexture;} + Texture2D *getCustomTexture() const {return customTexture;} + Texture2D *getButtonSmallTexture() const {return buttonSmallTexture;} + Texture2D *getButtonBigTexture() const {return buttonBigTexture;} + + StrSound *getIntroMusic() {return &introMusic;} + StrSound *getMenuMusic() {return &menuMusic;} + StaticSound *getClickSoundA() {return &clickSoundA;} + StaticSound *getClickSoundB() {return &clickSoundB;} + StaticSound *getClickSoundC() {return &clickSoundC;} + StaticSound *getWaterSound() {return waterSounds.getRandSound();} + + Font2D *getDisplayFont() const {return displayFont;} + Font2D *getMenuFontSmall() const {return menuFontSmall;} + Font2D *getMenuFontNormal() const {return menuFontNormal;} + Font2D *getMenuFontBig() const {return menuFontBig;} + Font2D *getMenuFontVeryBig() const {return menuFontVeryBig;} + Font2D *getConsoleFont() const {return consoleFont;} + +private: + CoreData(){}; + + int computeFontSize(int size); +}; + +}} //end namespace + +#endif diff --git a/source/glest_game/global/lang.cpp b/source/glest_game/global/lang.cpp new file mode 100644 index 00000000..2c339bb1 --- /dev/null +++ b/source/glest_game/global/lang.cpp @@ -0,0 +1,76 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "lang.h" + +#include + +#include "logger.h" +#include "util.h" +#include "leak_dumper.h" + +using namespace std; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Lang +// ===================================================== + +Lang &Lang::getInstance(){ + static Lang lang; + return lang; +} + +void Lang::loadStrings(const string &language){ + this->language= language; + strings.clear(); + strings.load("data/lang/"+language+".lng"); +} + +void Lang::loadScenarioStrings(const string &scenarioDir, const string &scenarioName){ + string path= scenarioDir + "/" + scenarioName + "/" + scenarioName + "_" + language + ".lng"; + + scenarioStrings.clear(); + + //try to load the current language first + if(fileExists(path)){ + scenarioStrings.load(path); + } + else{ + //try english otherwise + string path= scenarioDir + "/" +scenarioName + "/" + scenarioName + "_english.lng"; + if(fileExists(path)){ + scenarioStrings.load(path); + } + } +} + +string Lang::get(const string &s){ + try{ + return strings.getString(s); + } + catch(exception &){ + return "???" + s + "???"; + } +} + +string Lang::getScenarioString(const string &s){ + try{ + return scenarioStrings.getString(s); + } + catch(exception &){ + return "???" + s + "???"; + } +} + +}}//end namespace diff --git a/source/glest_game/global/lang.h b/source/glest_game/global/lang.h new file mode 100644 index 00000000..8bdffc61 --- /dev/null +++ b/source/glest_game/global/lang.h @@ -0,0 +1,46 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_LANG_H_ +#define _GLEST_GAME_LANG_H_ + +#include "properties.h" + +namespace Glest{ namespace Game{ + +using Shared::Util::Properties; + +// ===================================================== +// class Lang +// +// String table +// ===================================================== + +class Lang{ +private: + string language; + Properties strings; + Properties scenarioStrings; + +private: + Lang(){}; + +public: + static Lang &getInstance(); + void loadStrings(const string &language); + void loadScenarioStrings(const string &scenarioDir, const string &scenarioName); + string get(const string &s); + string getScenarioString(const string &s); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/global/metrics.cpp b/source/glest_game/global/metrics.cpp new file mode 100644 index 00000000..99e03bcd --- /dev/null +++ b/source/glest_game/global/metrics.cpp @@ -0,0 +1,77 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "metrics.h" + +#include "element_type.h" +#include "leak_dumper.h" + +namespace Glest{ namespace Game{ + + +// ===================================================== +// class Metrics +// ===================================================== + +Metrics::Metrics(){ + Config &config= Config::getInstance(); + + virtualW= 1000; + virtualH= 750; + + screenW= config.getInt("ScreenWidth"); + screenH= config.getInt("ScreenHeight"); + + minimapX= 10; + minimapY= 750-128-30+16; + minimapW= 128; + minimapH= 128; + + displayX= 800; + displayY= 250; + displayW= 128; + displayH= 480; +} + +const Metrics &Metrics::getInstance(){ + static const Metrics metrics; + return metrics; +} + +float Metrics::getAspectRatio() const{ + return static_cast(screenW)/screenH; +} + +int Metrics::toVirtualX(int w) const{ + return w*virtualW/screenW; +} + +int Metrics::toVirtualY(int h) const{ + return h*virtualH/screenH; +} + +bool Metrics::isInDisplay(int x, int y) const{ + return + x > displayX && + y > displayY && + x < displayX+displayW && + y < displayY+displayH; +} + +bool Metrics::isInMinimap(int x, int y) const{ + return + x > minimapX && + y > minimapY && + x < minimapX+minimapW && + y < minimapY+minimapH; +} + +}}// end namespace diff --git a/source/glest_game/global/metrics.h b/source/glest_game/global/metrics.h new file mode 100644 index 00000000..97d430fa --- /dev/null +++ b/source/glest_game/global/metrics.h @@ -0,0 +1,67 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_METRICS_H_ +#define _GLEST_GAME_METRICS_H_ + +#include "config.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Metrics +// ===================================================== + +class Metrics{ +private: + int virtualW; + int virtualH; + int screenW; + int screenH; + int minimapX; + int minimapY; + int minimapW; + int minimapH; + int displayX; + int displayY; + int displayH; + int displayW; + +private: + Metrics(); + +public: + static const Metrics &getInstance(); + + int getVirtualW() const {return virtualW;} + int getVirtualH() const {return virtualH;} + int getScreenW() const {return screenW;} + int getScreenH() const {return screenH;} + int getMinimapX() const {return minimapX;} + int getMinimapY() const {return minimapY;} + int getMinimapW() const {return minimapW;} + int getMinimapH() const {return minimapH;} + int getDisplayX() const {return displayX;} + int getDisplayY() const {return displayY;} + int getDisplayH() const {return displayH;} + int getDisplayW() const {return displayW;} + float getAspectRatio() const; + + int toVirtualX(int w) const; + int toVirtualY(int h) const; + + bool isInDisplay(int x, int y) const; + bool isInMinimap(int x, int y) const; +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/graphics/particle_type.cpp b/source/glest_game/graphics/particle_type.cpp new file mode 100644 index 00000000..8a30e6f3 --- /dev/null +++ b/source/glest_game/graphics/particle_type.cpp @@ -0,0 +1,240 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "particle_type.h" + +#include "util.h" +#include "core_data.h" +#include "xml_parser.h" +#include "renderer.h" +#include "config.h" +#include "game_constants.h" + +#include "leak_dumper.h" + +using namespace Shared::Xml; +using namespace Shared::Graphics; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class ParticleSystemType +// ===================================================== + +ParticleSystemType::ParticleSystemType(){ +} + +void ParticleSystemType::load(const XmlNode *particleSystemNode, const string &dir){ + + Renderer &renderer= Renderer::getInstance(); + + //texture + const XmlNode *textureNode= particleSystemNode->getChild("texture"); + bool textureEnabled= textureNode->getAttribute("value")->getBoolValue(); + if(textureEnabled){ + texture= renderer.newTexture2D(rsGame); + if(textureNode->getAttribute("luminance")->getBoolValue()){ + texture->setFormat(Texture::fAlpha); + texture->getPixmap()->init(1); + } + else{ + texture->getPixmap()->init(4); + } + texture->load(dir + "/" + textureNode->getAttribute("path")->getRestrictedValue()); + } + else{ + texture= NULL; + } + + //model + const XmlNode *modelNode= particleSystemNode->getChild("model"); + bool modelEnabled= modelNode->getAttribute("value")->getBoolValue(); + if(modelEnabled){ + string path= modelNode->getAttribute("path")->getRestrictedValue(); + model= renderer.newModel(rsGame); + model->load(dir + "/" + path); + } + else{ + model= NULL; + } + + //primitive + const XmlNode *primitiveNode= particleSystemNode->getChild("primitive"); + primitive= primitiveNode->getAttribute("value")->getRestrictedValue(); + + //offset + const XmlNode *offsetNode= particleSystemNode->getChild("offset"); + offset.x= offsetNode->getAttribute("x")->getFloatValue(); + offset.y= offsetNode->getAttribute("y")->getFloatValue(); + offset.z= offsetNode->getAttribute("z")->getFloatValue(); + + //color + const XmlNode *colorNode= particleSystemNode->getChild("color"); + color.x= colorNode->getAttribute("red")->getFloatValue(0.f, 1.0f); + color.y= colorNode->getAttribute("green")->getFloatValue(0.f, 1.0f); + color.z= colorNode->getAttribute("blue")->getFloatValue(0.f, 1.0f); + color.w= colorNode->getAttribute("alpha")->getFloatValue(0.f, 1.0f); + + //color + const XmlNode *colorNoEnergyNode= particleSystemNode->getChild("color-no-energy"); + colorNoEnergy.x= colorNoEnergyNode->getAttribute("red")->getFloatValue(0.f, 1.0f); + colorNoEnergy.y= colorNoEnergyNode->getAttribute("green")->getFloatValue(0.f, 1.0f); + colorNoEnergy.z= colorNoEnergyNode->getAttribute("blue")->getFloatValue(0.f, 1.0f); + colorNoEnergy.w= colorNoEnergyNode->getAttribute("alpha")->getFloatValue(0.f, 1.0f); + + //size + const XmlNode *sizeNode= particleSystemNode->getChild("size"); + size= sizeNode->getAttribute("value")->getFloatValue(); + + //sizeNoEnergy + const XmlNode *sizeNoEnergyNode= particleSystemNode->getChild("size-no-energy"); + sizeNoEnergy= sizeNoEnergyNode->getAttribute("value")->getFloatValue(); + + //speed + const XmlNode *speedNode= particleSystemNode->getChild("speed"); + speed= speedNode->getAttribute("value")->getFloatValue()/GameConstants::updateFps; + + //gravity + const XmlNode *gravityNode= particleSystemNode->getChild("gravity"); + gravity= gravityNode->getAttribute("value")->getFloatValue()/GameConstants::updateFps; + + //emission rate + const XmlNode *emissionRateNode= particleSystemNode->getChild("emission-rate"); + emissionRate= emissionRateNode->getAttribute("value")->getIntValue(); + + //energy max + const XmlNode *energyMaxNode= particleSystemNode->getChild("energy-max"); + energyMax= energyMaxNode->getAttribute("value")->getIntValue(); + + //speed + const XmlNode *energyVarNode= particleSystemNode->getChild("energy-var"); + energyVar= energyVarNode->getAttribute("value")->getIntValue(); +} + +void ParticleSystemType::setValues(AttackParticleSystem *ats){ + ats->setTexture(texture); + ats->setPrimitive(AttackParticleSystem::strToPrimitive(primitive)); + ats->setOffset(offset); + ats->setColor(color); + ats->setColorNoEnergy(colorNoEnergy); + ats->setSpeed(speed); + ats->setGravity(gravity); + ats->setParticleSize(size); + ats->setSizeNoEnergy(sizeNoEnergy); + ats->setEmissionRate(emissionRate); + ats->setMaxParticleEnergy(energyMax); + ats->setVarParticleEnergy(energyVar); + ats->setModel(model); +} + +// =========================================================== +// class ParticleSystemTypeProjectile +// =========================================================== + +void ParticleSystemTypeProjectile::load(const string &dir, const string &path){ + + try{ + XmlTree xmlTree; + xmlTree.load(path); + const XmlNode *particleSystemNode= xmlTree.getRootNode(); + + ParticleSystemType::load(particleSystemNode, dir); + + //trajectory values + const XmlNode *tajectoryNode= particleSystemNode->getChild("trajectory"); + trajectory= tajectoryNode->getAttribute("type")->getRestrictedValue(); + + //trajectory speed + const XmlNode *tajectorySpeedNode= tajectoryNode->getChild("speed"); + trajectorySpeed= tajectorySpeedNode->getAttribute("value")->getFloatValue()/GameConstants::updateFps; + + if(trajectory=="parabolic" || trajectory=="spiral"){ + //trajectory scale + const XmlNode *tajectoryScaleNode= tajectoryNode->getChild("scale"); + trajectoryScale= tajectoryScaleNode->getAttribute("value")->getFloatValue(); + } + else{ + trajectoryScale= 1.0f; + } + + if(trajectory=="spiral"){ + //trajectory frequency + const XmlNode *tajectoryFrequencyNode= tajectoryNode->getChild("frequency"); + trajectoryFrequency= tajectoryFrequencyNode->getAttribute("value")->getFloatValue(); + } + else{ + trajectoryFrequency= 1.0f; + } + } + catch(const exception &e){ + throw runtime_error("Error loading ParticleSystem: "+ path + "\n" +e.what()); + } +} + +ProjectileParticleSystem *ParticleSystemTypeProjectile::create(){ + ProjectileParticleSystem *ps= new ProjectileParticleSystem(); + + ParticleSystemType::setValues(ps); + + ps->setTrajectory(ProjectileParticleSystem::strToTrajectory(trajectory)); + ps->setTrajectorySpeed(trajectorySpeed); + ps->setTrajectoryScale(trajectoryScale); + ps->setTrajectoryFrequency(trajectoryFrequency); + + return ps; +} + +// =========================================================== +// class ParticleSystemTypeSplash +// =========================================================== + +void ParticleSystemTypeSplash::load(const string &dir, const string &path){ + + try{ + XmlTree xmlTree; + xmlTree.load(path); + const XmlNode *particleSystemNode= xmlTree.getRootNode(); + + ParticleSystemType::load(particleSystemNode, dir); + + //emission rate fade + const XmlNode *emissionRateFadeNode= particleSystemNode->getChild("emission-rate-fade"); + emissionRateFade= emissionRateFadeNode->getAttribute("value")->getIntValue(); + + //spread values + const XmlNode *verticalSpreadNode= particleSystemNode->getChild("vertical-spread"); + verticalSpreadA= verticalSpreadNode->getAttribute("a")->getFloatValue(0.0f, 1.0f); + verticalSpreadB= verticalSpreadNode->getAttribute("b")->getFloatValue(-1.0f, 1.0f); + + const XmlNode *horizontalSpreadNode= particleSystemNode->getChild("horizontal-spread"); + horizontalSpreadA= horizontalSpreadNode->getAttribute("a")->getFloatValue(0.0f, 1.0f); + horizontalSpreadB= horizontalSpreadNode->getAttribute("b")->getFloatValue(-1.0f, 1.0f); + } + catch(const exception &e){ + throw runtime_error("Error loading ParticleSystem: "+ path + "\n" +e.what()); + } +} + +SplashParticleSystem *ParticleSystemTypeSplash::create(){ + SplashParticleSystem *ps= new SplashParticleSystem(); + + ParticleSystemType::setValues(ps); + + ps->setEmissionRateFade(emissionRateFade); + ps->setVerticalSpreadA(verticalSpreadA); + ps->setVerticalSpreadB(verticalSpreadB); + ps->setHorizontalSpreadA(horizontalSpreadA); + ps->setHorizontalSpreadB(horizontalSpreadB); + + return ps; +} + +}}//end mamespace diff --git a/source/glest_game/graphics/particle_type.h b/source/glest_game/graphics/particle_type.h new file mode 100644 index 00000000..6cec6b2d --- /dev/null +++ b/source/glest_game/graphics/particle_type.h @@ -0,0 +1,104 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_PARTICLETYPE_H_ +#define _GLEST_GAME_PARTICLETYPE_H_ + +#include + +#include "particle.h" +#include "factory.h" +#include "texture.h" +#include "vec.h" +#include "xml_parser.h" + +using std::string; + +namespace Glest{ namespace Game{ + +using Shared::Graphics::ParticleSystem; +using Shared::Graphics::AttackParticleSystem; +using Shared::Graphics::ProjectileParticleSystem; +using Shared::Graphics::SplashParticleSystem; +using Shared::Graphics::Texture2D; +using Shared::Graphics::Vec3f; +using Shared::Graphics::Vec4f; +using Shared::Graphics::Model; +using Shared::Util::MultiFactory; +using Shared::Xml::XmlNode; + +// =========================================================== +// class ParticleSystemType +// +/// A type of particle system +// =========================================================== + +class ParticleSystemType{ +protected: + string type; + Texture2D *texture; + Model *model; + string primitive; + Vec3f offset; + Vec4f color; + Vec4f colorNoEnergy; + float size; + float sizeNoEnergy; + float speed; + float gravity; + int emissionRate; + int energyMax; + int energyVar; + +public: + ParticleSystemType(); + void load(const XmlNode *particleSystemNode, const string &dir); + +protected: + void setValues(AttackParticleSystem *ats); +}; + +// =========================================================== +// class ParticleSystemTypeProjectile +// =========================================================== + +class ParticleSystemTypeProjectile: public ParticleSystemType{ +private: + string trajectory; + float trajectorySpeed; + float trajectoryScale; + float trajectoryFrequency; + +public: + void load(const string &dir, const string &path); + ProjectileParticleSystem *create(); +}; + +// =========================================================== +// class ParticleSystemTypeSplash +// =========================================================== + +class ParticleSystemTypeSplash: public ParticleSystemType{ +public: + void load(const string &dir, const string &path); + SplashParticleSystem *create(); + +private: + int emissionRateFade; + float verticalSpreadA; + float verticalSpreadB; + float horizontalSpreadA; + float horizontalSpreadB; +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/graphics/renderer.cpp b/source/glest_game/graphics/renderer.cpp new file mode 100644 index 00000000..de0059f1 --- /dev/null +++ b/source/glest_game/graphics/renderer.cpp @@ -0,0 +1,2854 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "renderer.h" + +#include "texture_gl.h" +#include "main_menu.h" +#include "config.h" +#include "components.h" +#include "time_flow.h" +#include "graphics_interface.h" +#include "object.h" +#include "core_data.h" +#include "game.h" +#include "metrics.h" +#include "opengl.h" +#include "faction.h" +#include "factory_repository.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; +using namespace Shared::Graphics::Gl; +using namespace Shared::Util; + +namespace Glest { namespace Game{ + +// ===================================================== +// class MeshCallbackTeamColor +// ===================================================== + +class MeshCallbackTeamColor: public MeshCallback{ +private: + const Texture *teamTexture; + +public: + void setTeamTexture(const Texture *teamTexture) {this->teamTexture= teamTexture;} + virtual void execute(const Mesh *mesh); +}; + +void MeshCallbackTeamColor::execute(const Mesh *mesh){ + + //team color + if(mesh->getCustomTexture() && teamTexture!=NULL){ + //texture 0 + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + //set color to interpolation + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); + + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE1); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA); + + //set alpha to 1 + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + //texture 1 + glActiveTexture(GL_TEXTURE1); + glMultiTexCoord2f(GL_TEXTURE1, 0.f, 0.f); + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, static_cast(teamTexture)->getHandle()); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + //set alpha to 1 + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + glActiveTexture(GL_TEXTURE0); + } + else{ + glActiveTexture(GL_TEXTURE1); + glDisable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } +} + +// =========================================================== +// class Renderer +// =========================================================== + +// ===================== PUBLIC ======================== + +const int Renderer::maxProgressBar= 100; +const Vec4f Renderer::progressBarBack1= Vec4f(0.7f, 0.7f, 0.7f, 0.7f); +const Vec4f Renderer::progressBarBack2= Vec4f(0.7f, 0.7f, 0.7f, 1.f); +const Vec4f Renderer::progressBarFront1= Vec4f(0.f, 0.5f, 0.f, 1.f); +const Vec4f Renderer::progressBarFront2= Vec4f(0.f, 0.1f, 0.f, 1.f); + +const float Renderer::sunDist= 10e6; +const float Renderer::moonDist= 10e6; +const float Renderer::lightAmbFactor= 0.4f; + +const int Renderer::maxMouse2dAnim= 100; + +const GLenum Renderer::baseTexUnit= GL_TEXTURE0; +const GLenum Renderer::fowTexUnit= GL_TEXTURE1; +const GLenum Renderer::shadowTexUnit= GL_TEXTURE2; + +const float Renderer::selectionCircleRadius= 0.7f; +const float Renderer::magicCircleRadius= 1.f; + +//perspective values +const float Renderer::perspFov= 60.f; +const float Renderer::perspNearPlane= 1.f; +const float Renderer::perspFarPlane= 50.f; + +const float Renderer::ambFactor= 0.7f; +const Vec4f Renderer::fowColor= Vec4f(0.0f, 0.0f, 0.0f, 1.0f); +const Vec4f Renderer::defSpecularColor= Vec4f(0.8f, 0.8f, 0.8f, 1.f); +const Vec4f Renderer::defDiffuseColor= Vec4f(1.f, 1.f, 1.f, 1.f); +const Vec4f Renderer::defAmbientColor= Vec4f(1.f * ambFactor, 1.f * ambFactor, 1.f * ambFactor, 1.f); +const Vec4f Renderer::defColor= Vec4f(1.f, 1.f, 1.f, 1.f); + +const float Renderer::maxLightDist= 50.f; + +// ==================== constructor and destructor ==================== + +Renderer::Renderer(){ + GraphicsInterface &gi= GraphicsInterface::getInstance(); + FactoryRepository &fr= FactoryRepository::getInstance(); + Config &config= Config::getInstance(); + + gi.setFactory(fr.getGraphicsFactory(config.getString("FactoryGraphics"))); + GraphicsFactory *graphicsFactory= GraphicsInterface::getInstance().getFactory(); + + modelRenderer= graphicsFactory->newModelRenderer(); + textRenderer= graphicsFactory->newTextRenderer2D(); + particleRenderer= graphicsFactory->newParticleRenderer(); + + //resources + for(int i=0; inewModelManager(); + textureManager[i]= graphicsFactory->newTextureManager(); + modelManager[i]->setTextureManager(textureManager[i]); + particleManager[i]= graphicsFactory->newParticleManager(); + fontManager[i]= graphicsFactory->newFontManager(); + } +} + +Renderer::~Renderer(){ + delete modelRenderer; + delete textRenderer; + delete particleRenderer; + + //resources + for(int i=0; iinit(); + textureManager[rsGlobal]->init(); + fontManager[rsGlobal]->init(); + + init2dList(); +} + +void Renderer::initGame(Game *game){ + this->game= game; + + //check gl caps + checkGlOptionalCaps(); + + //vars + shadowMapFrame= 0; + waterAnim= 0; + + //shadows + if(shadows==sProjected || shadows==sShadowMapping){ + static_cast(modelRenderer)->setSecondaryTexCoordUnit(2); + + glGenTextures(1, &shadowMapHandle); + glBindTexture(GL_TEXTURE_2D, shadowMapHandle); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if(shadows==sShadowMapping){ + + //shadow mapping + glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FAIL_VALUE_ARB, 1.0f-shadowAlpha); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, + shadowTextureSize, shadowTextureSize, + 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); + } + else{ + + //projected + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, + shadowTextureSize, shadowTextureSize, + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL); + } + + shadowMapFrame= -1; + } + + //texture init + modelManager[rsGame]->init(); + textureManager[rsGame]->init(); + fontManager[rsGame]->init(); + + init3dList(); +} + +void Renderer::initMenu(MainMenu *mm){ + modelManager[rsMenu]->init(); + textureManager[rsMenu]->init(); + fontManager[rsMenu]->init(); + //modelRenderer->setCustomTexture(CoreData::getInstance().getCustomTexture()); + + init3dListMenu(mm); +} + +void Renderer::reset3d(){ + assertGl(); + glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); + glCallList(list3d); + pointCount= 0; + triangleCount= 0; + assertGl(); +} + +void Renderer::reset2d(){ + assertGl(); + glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR); + glCallList(list2d); + assertGl(); +} + +void Renderer::reset3dMenu(){ + assertGl(); + glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR); + glCallList(list3dMenu); + assertGl(); +} + +// ==================== end ==================== + +void Renderer::end(){ + + //delete resources + modelManager[rsGlobal]->end(); + textureManager[rsGlobal]->end(); + fontManager[rsGlobal]->end(); + particleManager[rsGlobal]->end(); + + //delete 2d list + glDeleteLists(list2d, 1); +} + +void Renderer::endGame(){ + game= NULL; + + //delete resources + modelManager[rsGame]->end(); + textureManager[rsGame]->end(); + fontManager[rsGame]->end(); + particleManager[rsGame]->end(); + + if(shadows==sProjected || shadows==sShadowMapping){ + glDeleteTextures(1, &shadowMapHandle); + } + + glDeleteLists(list3d, 1); +} + +void Renderer::endMenu(){ + //delete resources + modelManager[rsMenu]->end(); + textureManager[rsMenu]->end(); + fontManager[rsMenu]->end(); + particleManager[rsMenu]->end(); + + glDeleteLists(list3dMenu, 1); +} + +void Renderer::reloadResources(){ + for(int i=0; iend(); + textureManager[i]->end(); + fontManager[i]->end(); + } + for(int i=0; iinit(); + textureManager[i]->init(); + fontManager[i]->init(); + } +} + + +// ==================== engine interface ==================== + +Model *Renderer::newModel(ResourceScope rs){ + return modelManager[rs]->newModel(); +} + +Texture2D *Renderer::newTexture2D(ResourceScope rs){ + return textureManager[rs]->newTexture2D(); +} + +Texture3D *Renderer::newTexture3D(ResourceScope rs){ + return textureManager[rs]->newTexture3D(); +} + +Font2D *Renderer::newFont(ResourceScope rs){ + return fontManager[rs]->newFont2D(); +} + +void Renderer::manageParticleSystem(ParticleSystem *particleSystem, ResourceScope rs){ + particleManager[rs]->manage(particleSystem); +} + +void Renderer::updateParticleManager(ResourceScope rs){ + particleManager[rs]->update(); +} + +void Renderer::renderParticleManager(ResourceScope rs){ + glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + glDepthFunc(GL_LESS); + particleRenderer->renderManager(particleManager[rs], modelRenderer); + glPopAttrib(); +} + +void Renderer::swapBuffers(){ + glFlush(); + GraphicsInterface::getInstance().getCurrentContext()->swapBuffers(); +} + +// ==================== lighting ==================== + +//places all the opengl lights +void Renderer::setupLighting(){ + + int lightCount= 0; + const World *world= game->getWorld(); + const GameCamera *gameCamera= game->getGameCamera(); + const TimeFlow *timeFlow= world->getTimeFlow(); + float time= timeFlow->getTime(); + + assertGl(); + + //sun/moon light + Vec3f lightColor= computeLightColor(time); + Vec3f fogColor= world->getTileset()->getFogColor(); + Vec4f lightPos= timeFlow->isDay()? computeSunPos(time): computeMoonPos(time); + nearestLightPos= lightPos; + + glLightfv(GL_LIGHT0, GL_POSITION, lightPos.ptr()); + glLightfv(GL_LIGHT0, GL_AMBIENT, Vec4f(lightColor*lightAmbFactor, 1.f).ptr()); + glLightfv(GL_LIGHT0, GL_DIFFUSE, Vec4f(lightColor, 1.f).ptr()); + glLightfv(GL_LIGHT0, GL_SPECULAR, Vec4f(0.0f, 0.0f, 0.f, 1.f).ptr()); + + glFogfv(GL_FOG_COLOR, Vec4f(fogColor*lightColor, 1.f).ptr()); + + lightCount++; + + //disable all secondary lights + for(int i= 1; iisTotalNight()){ + for(int i=0; igetFactionCount() && lightCountgetFaction(i)->getUnitCount() && lightCountgetFaction(i)->getUnit(j); + if(world->toRenderUnit(unit) && + unit->getCurrVector().dist(gameCamera->getPos())getType()->getLight() && unit->isOperative()){ + + Vec4f pos= Vec4f(unit->getCurrVector()); + pos.y+=4.f; + + GLenum lightEnum= GL_LIGHT0 + lightCount; + + glEnable(lightEnum); + glLightfv(lightEnum, GL_POSITION, pos.ptr()); + glLightfv(lightEnum, GL_AMBIENT, Vec4f(unit->getType()->getLightColor()).ptr()); + glLightfv(lightEnum, GL_DIFFUSE, Vec4f(unit->getType()->getLightColor()).ptr()); + glLightfv(lightEnum, GL_SPECULAR, Vec4f(unit->getType()->getLightColor()*0.3f).ptr()); + glLightf(lightEnum, GL_QUADRATIC_ATTENUATION, 0.05f); + + ++lightCount; + + const GameCamera *gameCamera= game->getGameCamera(); + + if(Vec3f(pos).dist(gameCamera->getPos())getPos())){ + nearestLightPos= pos; + } + } + } + } + } + + assertGl(); +} + +void Renderer::loadGameCameraMatrix(){ + const GameCamera *gameCamera= game->getGameCamera(); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glRotatef(gameCamera->getVAng(), -1, 0, 0); + glRotatef(gameCamera->getHAng(), 0, 1, 0); + glTranslatef(-gameCamera->getPos().x, -gameCamera->getPos().y, -gameCamera->getPos().z); +} + +void Renderer::loadCameraMatrix(const Camera *camera){ + Vec3f position= camera->getPosition(); + Quaternion orientation= camera->getOrientation().conjugate(); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMultMatrixf(orientation.toMatrix4().ptr()); + glTranslatef(-position.x, -position.y, -position.z); +} + +void Renderer::computeVisibleQuad(){ + const GameCamera *gameCamera= game->getGameCamera(); + visibleQuad= gameCamera->computeVisibleQuad(); +} + +// ======================================= +// basic rendering +// ======================================= + +void Renderer::renderMouse2d(int x, int y, int anim, float fade){ + float color1, color2; + + float fadeFactor= fade+1.f; + + anim= anim*2-maxMouse2dAnim; + + color2= (abs(anim*fadeFactor)/static_cast(maxMouse2dAnim))/2.f+0.4f; + color1= (abs(anim*fadeFactor)/static_cast(maxMouse2dAnim))/2.f+0.8f; + + glPushAttrib(GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT); + glEnable(GL_BLEND); + + //inside + glColor4f(0.4f*fadeFactor, 0.2f*fadeFactor, 0.2f*fadeFactor, 0.5f*fadeFactor); + glBegin(GL_TRIANGLES); + glVertex2i(x, y); + glVertex2i(x+20, y-10); + glVertex2i(x+10, y-20); + glEnd(); + + //biorder + glLineWidth(2); + glBegin(GL_LINE_LOOP); + glColor4f(1.f, 0.2f, 0, color1); + glVertex2i(x, y); + glColor4f(1.f, 0.4f, 0, color2); + glVertex2i(x+20, y-10); + glColor4f(1.f, 0.4f, 0, color2); + glVertex2i(x+10, y-20); + glEnd(); + glPopAttrib(); +} + +void Renderer::renderMouse3d(){ + + const Gui *gui= game->getGui(); + const Mouse3d *mouse3d= gui->getMouse3d(); + const Map *map= game->getWorld()->getMap(); + + GLUquadricObj *cilQuadric; + Vec4f color; + + assertGl(); + + if((mouse3d->isEnabled() || gui->isPlacingBuilding()) && gui->isValidPosObjWorld()){ + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glPushAttrib(GL_CURRENT_BIT | GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_BLEND); + glDisable(GL_STENCIL_TEST); + glDepthFunc(GL_LESS); + glEnable(GL_COLOR_MATERIAL); + glDepthMask(GL_FALSE); + + Vec2i pos= gui->getPosObjWorld(); + Vec3f pos3f= Vec3f(pos.x, map->getCell(pos)->getHeight(), pos.y); + + if(gui->isPlacingBuilding()){ + + const UnitType *building= gui->getBuilding(); + + //selection building emplacement + float offset= building->getSize()/2.f-0.5f; + glTranslatef(pos3f.x+offset, pos3f.y, pos3f.z+offset); + + //choose color + if(map->isFreeCells(pos, building->getSize(), fLand)){ + color= Vec4f(1.f, 1.f, 1.f, 0.5f); + } + else{ + color= Vec4f(1.f, 0.f, 0.f, 0.5f); + } + + modelRenderer->begin(true, true, false); + glColor4fv(color.ptr()); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color.ptr()); + const Model *buildingModel= building->getFirstStOfClass(scStop)->getAnimation(); + buildingModel->updateInterpolationData(0.f, false); + modelRenderer->render(buildingModel); + glDisable(GL_COLOR_MATERIAL); + modelRenderer->end(); + + } + else{ + //standard mouse + glDisable(GL_TEXTURE_2D); + glDisable(GL_CULL_FACE); + color= Vec4f(1.f, 0.f, 0.f, 1.f-mouse3d->getFade()); + glColor4fv(color.ptr()); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color.ptr()); + + glTranslatef(pos3f.x, pos3f.y+2.f, pos3f.z); + glRotatef(90.f, 1.f, 0.f, 0.f); + glRotatef(static_cast(mouse3d->getRot()), 0.f, 0.f, 1.f); + + cilQuadric= gluNewQuadric(); + gluQuadricDrawStyle(cilQuadric, GLU_FILL); + gluCylinder(cilQuadric, 0.5f, 0.f, 2.f, 4, 1); + gluCylinder(cilQuadric, 0.5f, 0.f, 0.f, 4, 1); + glTranslatef(0.f, 0.f, 1.f); + gluCylinder(cilQuadric, 0.7f, 0.f, 1.f, 4, 1); + gluCylinder(cilQuadric, 0.7f, 0.f, 0.f, 4, 1); + gluDeleteQuadric(cilQuadric); + } + + glPopAttrib(); + glPopMatrix(); + } + +} + +void Renderer::renderBackground(const Texture2D *texture){ + + const Metrics &metrics= Metrics::getInstance(); + + assertGl(); + + glPushAttrib(GL_ENABLE_BIT); + + glDisable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); + + renderQuad(0, 0, metrics.getVirtualW(), metrics.getVirtualH(), texture); + + glPopAttrib(); + + assertGl(); +} + +void Renderer::renderTextureQuad(int x, int y, int w, int h, const Texture2D *texture, float alpha){ + assertGl(); + + glPushAttrib(GL_ENABLE_BIT); + + glDisable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + + glColor4f(1.f, 1.f, 1.f, alpha); + renderQuad(x, y, w, h, texture); + + glPopAttrib(); + + assertGl(); +} + +void Renderer::renderConsole(const Console *console){ + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_BLEND); + + for(int i=0; igetLineCount(); ++i){ + renderTextShadow( + console->getLine(i), + CoreData::getInstance().getConsoleFont(), + 20, i*20+20); + } + + glPopAttrib(); +} + +void Renderer::renderChatManager(const ChatManager *chatManager){ + Lang &lang= Lang::getInstance(); + + if(chatManager->getEditEnabled()){ + string text; + + if(chatManager->getTeamMode()){ + text+= lang.get("Team"); + } + else + { + text+= lang.get("All"); + } + text+= ": " + chatManager->getText() + "_"; + + textRenderer->begin(CoreData::getInstance().getConsoleFont()); + textRenderer->render(text, 300, 150); + textRenderer->end(); + } +} + +void Renderer::renderResourceStatus(){ + + const Metrics &metrics= Metrics::getInstance(); + const World *world= game->getWorld(); + const Faction *thisFaction= world->getFaction(world->getThisFactionIndex()); + + assertGl(); + + glPushAttrib(GL_ENABLE_BIT); + + int j= 0; + for(int i= 0; igetTechTree()->getResourceTypeCount(); ++i){ + const ResourceType *rt= world->getTechTree()->getResourceType(i); + const Resource *r= thisFaction->getResource(rt); + + //if any unit produces the resource + bool showResource= false; + for(int k=0; kgetType()->getUnitTypeCount(); ++k){ + const UnitType *ut= thisFaction->getType()->getUnitType(k); + if(ut->getCost(rt)!=NULL){ + showResource= true; + break; + } + } + + //draw resource status + if(showResource){ + + string str= intToStr(r->getAmount()); + + glEnable(GL_TEXTURE_2D); + renderQuad(j*100+200, metrics.getVirtualH()-30, 16, 16, rt->getImage()); + + if(rt->getClass()!=rcStatic){ + str+= "/" + intToStr(thisFaction->getStoreAmount(rt)); + } + if(rt->getClass()==rcConsumable){ + str+= "("; + if(r->getBalance()>0){ + str+= "+"; + } + str+= intToStr(r->getBalance()) + ")"; + } + + glDisable(GL_TEXTURE_2D); + + renderTextShadow( + str, CoreData::getInstance().getMenuFontSmall(), + j*100+220, metrics.getVirtualH()-30, false); + ++j; + } + + } + + glPopAttrib(); + + assertGl(); +} + +void Renderer::renderSelectionQuad(){ + + const Gui *gui= game->getGui(); + const SelectionQuad *sq= gui->getSelectionQuad(); + + Vec2i down= sq->getPosDown(); + Vec2i up= sq->getPosUp(); + + if(gui->isSelecting()){ + glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT); + glColor3f(0,1,0); + glBegin(GL_LINE_LOOP); + glVertex2i(down.x, down.y); + glVertex2i(up.x, down.y); + glVertex2i(up.x, up.y); + glVertex2i(down.x, up.y); + glEnd(); + glPopAttrib(); + } +} + +Vec2i computeCenteredPos(const string &text, const Font2D *font, int x, int y){ + Vec2i textPos; + + const Metrics &metrics= Metrics::getInstance(); + const FontMetrics *fontMetrics= font->getMetrics(); + + textPos= Vec2i( + x-metrics.toVirtualX(static_cast(fontMetrics->getTextWidth(text)/2.f)), + y-metrics.toVirtualY(static_cast(fontMetrics->getHeight()/2.f))); + + return textPos; +} + +void Renderer::renderText(const string &text, const Font2D *font, float alpha, int x, int y, bool centered){ + glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT); + glEnable(GL_BLEND); + glColor4fv(Vec4f(1.f, 1.f, 1.f, alpha).ptr()); + + Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y); + + textRenderer->begin(font); + textRenderer->render(text, pos.x, pos.y); + textRenderer->end(); + + glPopAttrib(); +} + +void Renderer::renderText(const string &text, const Font2D *font, const Vec3f &color, int x, int y, bool centered){ + glPushAttrib(GL_CURRENT_BIT); + glColor3fv(color.ptr()); + + Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y); + + textRenderer->begin(font); + textRenderer->render(text, pos.x, pos.y); + textRenderer->end(); + + glPopAttrib(); +} + +void Renderer::renderTextShadow(const string &text, const Font2D *font, int x, int y, bool centered){ + glPushAttrib(GL_CURRENT_BIT); + + Vec2i pos= centered? computeCenteredPos(text, font, x, y): Vec2i(x, y); + + textRenderer->begin(font); + glColor3f(0.0f, 0.0f, 0.0f); + textRenderer->render(text, pos.x-1.0f, pos.y-1.0f); + glColor3f(1.0f, 1.0f, 1.0f); + textRenderer->render(text, pos.x, pos.y); + textRenderer->end(); + + glPopAttrib(); +} + +// ============= COMPONENTS ============================= + +void Renderer::renderLabel(const GraphicLabel *label){ + + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_BLEND); + + Vec2i textPos; + int x= label->getX(); + int y= label->getY(); + int h= label->getH(); + int w= label->getW(); + + if(label->getCentered()){ + textPos= Vec2i(x+w/2, y+h/2); + } + else{ + textPos= Vec2i(x, y+h/4); + } + + renderText(label->getText(), label->getFont(), GraphicComponent::getFade(), textPos.x, textPos.y, label->getCentered()); + + glPopAttrib(); +} + +void Renderer::renderButton(const GraphicButton *button){ + + int x= button->getX(); + int y= button->getY(); + int h= button->getH(); + int w= button->getW(); + + glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT); + + //background + CoreData &coreData= CoreData::getInstance(); + Texture2D *backTexture= w>3*h/2? coreData.getButtonBigTexture(): coreData.getButtonSmallTexture(); + + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + + glBindTexture(GL_TEXTURE_2D, static_cast(backTexture)->getHandle()); + + //button + Vec4f color= Vec4f(1.f, 1.f, 1.f, GraphicComponent::getFade()); + glColor4fv(color.ptr()); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0.f, 0.f); + glVertex2f(x, y); + + glTexCoord2f(0.f, 1.f); + glVertex2f(x, y+h); + + glTexCoord2f(1.f, 0.f); + glVertex2f(x+w, y); + + glTexCoord2f(1.f, 1.f); + glVertex2f(x+w, y+h); + + glEnd(); + + glDisable(GL_TEXTURE_2D); + + //lighting + float anim= GraphicComponent::getAnim(); + if(anim>0.5f) anim= 1.f-anim; + + if(button->getLighted()){ + const int lightSize= 0; + const Vec4f color1= Vec4f(1.f, 1.f, 1.f, 0.1f+anim*0.5f); + const Vec4f color2= Vec4f(1.f, 1.f, 1.f, 0.3f+anim); + + glBegin(GL_TRIANGLE_FAN); + + glColor4fv(color2.ptr()); + glVertex2f(x+w/2, y+h/2); + + glColor4fv(color1.ptr()); + glVertex2f(x-lightSize, y-lightSize); + + glColor4fv(color1.ptr()); + glVertex2f(x+w+lightSize, y-lightSize); + + glColor4fv(color1.ptr()); + glVertex2f(x+w+lightSize, y+h+lightSize); + + glColor4fv(color1.ptr()); + glVertex2f(x+w+lightSize, y+h+lightSize); + + glColor4fv(color1.ptr()); + glVertex2f(x-lightSize, y+h+lightSize); + + glColor4fv(color1.ptr()); + glVertex2f(x-lightSize, y-lightSize); + + glEnd(); + } + + Vec2i textPos= Vec2i(x+w/2, y+h/2); + + renderText( + button->getText(), button->getFont(), GraphicButton::getFade(), + x+w/2, y+h/2, true); + + glPopAttrib(); +} + +void Renderer::renderListBox(const GraphicListBox *listBox){ + + renderButton(listBox->getButton1()); + renderButton(listBox->getButton2()); + + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_BLEND); + + GraphicLabel label; + label.init(listBox->getX(), listBox->getY(), listBox->getW(), listBox->getH(), true); + label.setText(listBox->getText()); + label.setFont(listBox->getFont()); + renderLabel(&label); + + glPopAttrib(); +} + +void Renderer::renderMessageBox(const GraphicMessageBox *messageBox){ + + //background + glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT); + glEnable(GL_BLEND); + + glColor4f(0.0f, 0.0f, 0.0f, 0.5f) ; + glBegin(GL_TRIANGLE_STRIP); + glVertex2i(messageBox->getX(), messageBox->getY()+9*messageBox->getH()/10); + glVertex2i(messageBox->getX(), messageBox->getY()); + glVertex2i(messageBox->getX() + messageBox->getW(), messageBox->getY() + 9*messageBox->getH()/10); + glVertex2i(messageBox->getX() + messageBox->getW(), messageBox->getY()); + glEnd(); + + glColor4f(0.0f, 0.0f, 0.0f, 0.8f) ; + glBegin(GL_TRIANGLE_STRIP); + glVertex2i(messageBox->getX(), messageBox->getY()+messageBox->getH()); + glVertex2i(messageBox->getX(), messageBox->getY()+9*messageBox->getH()/10); + glVertex2i(messageBox->getX() + messageBox->getW(), messageBox->getY() + messageBox->getH()); + glVertex2i(messageBox->getX() + messageBox->getW(), messageBox->getY()+9*messageBox->getH()/10); + glEnd(); + + glBegin(GL_LINE_LOOP); + glColor4f(0.5f, 0.5f, 0.5f, 0.25f) ; + glVertex2i(messageBox->getX(), messageBox->getY()); + + glColor4f(0.0f, 0.0f, 0.0f, 0.25f) ; + glVertex2i(messageBox->getX()+ messageBox->getW(), messageBox->getY()); + + glColor4f(0.5f, 0.5f, 0.5f, 0.25f) ; + glVertex2i(messageBox->getX()+ messageBox->getW(), messageBox->getY() + messageBox->getH()); + + glColor4f(0.25f, 0.25f, 0.25f, 0.25f) ; + glVertex2i(messageBox->getX(), messageBox->getY() + messageBox->getH()); + glEnd(); + + glBegin(GL_LINE_STRIP); + glColor4f(1.0f, 1.0f, 1.0f, 0.25f) ; + glVertex2i(messageBox->getX(), messageBox->getY() + 90*messageBox->getH()/100); + + glColor4f(0.5f, 0.5f, 0.5f, 0.25f) ; + glVertex2i(messageBox->getX()+ messageBox->getW(), messageBox->getY() + 90*messageBox->getH()/100); + glEnd(); + + glPopAttrib(); + + //buttons + renderButton(messageBox->getButton1()); + if(messageBox->getButtonCount()==2){ + renderButton(messageBox->getButton2()); + } + + //text + renderText( + messageBox->getText(), messageBox->getFont(), Vec3f(1.0f, 1.0f, 1.0f), + messageBox->getX()+15, messageBox->getY()+7*messageBox->getH()/10, + false ); + + renderText( + messageBox->getHeader(), messageBox->getFont(),Vec3f(1.0f, 1.0f, 1.0f), + messageBox->getX()+15, messageBox->getY()+93*messageBox->getH()/100, + false ); +} + +// ==================== complex rendering ==================== + +void Renderer::renderSurface(){ + + int lastTex=-1; + int currTex; + const World *world= game->getWorld(); + const Map *map= world->getMap(); + const Rect2i mapBounds(0, 0, map->getSurfaceW()-1, map->getSurfaceH()-1); + float coordStep= world->getTileset()->getSurfaceAtlas()->getCoordStep(); + + assertGl(); + + const Texture2D *fowTex= world->getMinimap()->getFowTexture(); + + glPushAttrib(GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_FOG_BIT | GL_TEXTURE_BIT); + + glEnable(GL_BLEND); + glEnable(GL_COLOR_MATERIAL); + glDisable(GL_ALPHA_TEST); + + //fog of war tex unit + glActiveTexture(fowTexUnit); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, static_cast(fowTex)->getHandle()); + glTexSubImage2D( + GL_TEXTURE_2D, 0, 0, 0, + fowTex->getPixmap()->getW(), fowTex->getPixmap()->getH(), + GL_ALPHA, GL_UNSIGNED_BYTE, fowTex->getPixmap()->getPixels()); + + //shadow texture + if(shadows==sProjected || shadows==sShadowMapping){ + glActiveTexture(shadowTexUnit); + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, shadowMapHandle); + + static_cast(modelRenderer)->setDuplicateTexCoords(true); + enableProjectiveTexturing(); + } + + glActiveTexture(baseTexUnit); + + Quad2i scaledQuad= visibleQuad/Map::cellScale; + + PosQuadIterator pqi(map, scaledQuad); + while(pqi.next()){ + + const Vec2i &pos= pqi.getPos(); + + if(mapBounds.isInside(pos)){ + + SurfaceCell *tc00= map->getSurfaceCell(pos.x, pos.y); + SurfaceCell *tc10= map->getSurfaceCell(pos.x+1, pos.y); + SurfaceCell *tc01= map->getSurfaceCell(pos.x, pos.y+1); + SurfaceCell *tc11= map->getSurfaceCell(pos.x+1, pos.y+1); + + triangleCount+= 2; + pointCount+= 4; + + //set texture + currTex= static_cast(tc00->getSurfaceTexture())->getHandle(); + if(currTex!=lastTex){ + lastTex=currTex; + glBindTexture(GL_TEXTURE_2D, lastTex); + } + + Vec2f surfCoord= tc00->getSurfTexCoord(); + + glBegin(GL_TRIANGLE_STRIP); + + //draw quad using immediate mode + glMultiTexCoord2fv(fowTexUnit, tc01->getFowTexCoord().ptr()); + glMultiTexCoord2f(baseTexUnit, surfCoord.x, surfCoord.y+coordStep); + glNormal3fv(tc01->getNormal().ptr()); + glVertex3fv(tc01->getVertex().ptr()); + + glMultiTexCoord2fv(fowTexUnit, tc00->getFowTexCoord().ptr()); + glMultiTexCoord2f(baseTexUnit, surfCoord.x, surfCoord.y); + glNormal3fv(tc00->getNormal().ptr()); + glVertex3fv(tc00->getVertex().ptr()); + + glMultiTexCoord2fv(fowTexUnit, tc11->getFowTexCoord().ptr()); + glMultiTexCoord2f(baseTexUnit, surfCoord.x+coordStep, surfCoord.y+coordStep); + glNormal3fv(tc11->getNormal().ptr()); + glVertex3fv(tc11->getVertex().ptr()); + + glMultiTexCoord2fv(fowTexUnit, tc10->getFowTexCoord().ptr()); + glMultiTexCoord2f(baseTexUnit, surfCoord.x+coordStep, surfCoord.y); + glNormal3fv(tc10->getNormal().ptr()); + glVertex3fv(tc10->getVertex().ptr()); + + glEnd(); + } + } + glEnd(); + + //Restore + static_cast(modelRenderer)->setDuplicateTexCoords(false); + glPopAttrib(); + + //assert + glGetError(); //remove when first mtex problem solved + assertGl(); +} + +void Renderer::renderObjects(){ + const World *world= game->getWorld(); + const Map *map= world->getMap(); + + assertGl(); + const Texture2D *fowTex= world->getMinimap()->getFowTexture(); + Vec3f baseFogColor= world->getTileset()->getFogColor()*computeLightColor(world->getTimeFlow()->getTime()); + + glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_FOG_BIT | GL_LIGHTING_BIT | GL_TEXTURE_BIT); + + if(shadows==sShadowMapping){ + glActiveTexture(shadowTexUnit); + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, shadowMapHandle); + + static_cast(modelRenderer)->setDuplicateTexCoords(true); + enableProjectiveTexturing(); + } + + glActiveTexture(baseTexUnit); + + glEnable(GL_COLOR_MATERIAL); + glAlphaFunc(GL_GREATER, 0.5f); + + modelRenderer->begin(true, true, false); + int thisTeamIndex= world->getThisTeamIndex(); + + PosQuadIterator pqi(map, visibleQuad, Map::cellScale); + while(pqi.next()){ + const Vec2i pos= pqi.getPos(); + + if(map->isInside(pos)){ + + SurfaceCell *sc= map->getSurfaceCell(Map::toSurfCoords(pos)); + Object *o= sc->getObject(); + if(sc->isExplored(thisTeamIndex) && o!=NULL){ + + const Model *objModel= sc->getObject()->getModel(); + Vec3f v= o->getPos(); + + //ambient and diffuse color is taken from cell color + float fowFactor= fowTex->getPixmap()->getPixelf(pos.x/Map::cellScale, pos.y/Map::cellScale); + Vec4f color= Vec4f(Vec3f(fowFactor), 1.f); + glColor4fv(color.ptr()); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (color*ambFactor).ptr()); + glFogfv(GL_FOG_COLOR, (baseFogColor*fowFactor).ptr()); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glTranslatef(v.x, v.y, v.z); + glRotatef(o->getRotation(), 0.f, 1.f, 0.f); + + objModel->updateInterpolationData(0.f, true); + modelRenderer->render(objModel); + + triangleCount+= objModel->getTriangleCount(); + pointCount+= objModel->getVertexCount(); + + glPopMatrix(); + + } + } + } + + modelRenderer->end(); + + //restore + static_cast(modelRenderer)->setDuplicateTexCoords(true); + glPopAttrib(); +} + +void Renderer::renderWater(){ + + bool closed= false; + const World *world= game->getWorld(); + const Map *map= world->getMap(); + + float waterAnim= world->getWaterEffects()->getAmin(); + + //assert + assertGl(); + + glPushAttrib(GL_TEXTURE_BIT | GL_ENABLE_BIT | GL_CURRENT_BIT); + + //water texture nit + glDisable(GL_TEXTURE_2D); + + glEnable(GL_BLEND); + if(textures3D){ + Texture3D *waterTex= world->getTileset()->getWaterTex(); + glEnable(GL_TEXTURE_3D); + glBindTexture(GL_TEXTURE_3D, static_cast(waterTex)->getHandle()); + } + else{ + glEnable(GL_COLOR_MATERIAL); + glColor4f(0.5f, 0.5f, 1.0f, 0.5f); + glBindTexture(GL_TEXTURE_3D, 0); + } + + assertGl(); + + //fog of War texture Unit + const Texture2D *fowTex= world->getMinimap()->getFowTexture(); + glActiveTexture(fowTexUnit); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, static_cast(fowTex)->getHandle()); + glActiveTexture(baseTexUnit); + + assertGl(); + + Rect2i boundingRect= visibleQuad.computeBoundingRect(); + Rect2i scaledRect= boundingRect/Map::cellScale; + scaledRect.clamp(0, 0, map->getSurfaceW()-1, map->getSurfaceH()-1); + + float waterLevel= world->getMap()->getWaterLevel(); + for(int j=scaledRect.p[0].y; jgetSurfaceCell(i, j); + SurfaceCell *tc1= map->getSurfaceCell(i, j+1); + + int thisTeamIndex= world->getThisTeamIndex(); + if(tc0->getNearSubmerged() && (tc0->isExplored(thisTeamIndex) || tc1->isExplored(thisTeamIndex))){ + glNormal3f(0.f, 1.f, 0.f); + closed= false; + + triangleCount+= 2; + pointCount+= 2; + + //vertex 1 + glMaterialfv( + GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, + computeWaterColor(waterLevel, tc1->getHeight()).ptr()); + glMultiTexCoord2fv(GL_TEXTURE1, tc1->getFowTexCoord().ptr()); + glTexCoord3f(i, 1.f, waterAnim); + glVertex3f( + static_cast(i)*Map::mapScale, + waterLevel, + static_cast(j+1)*Map::mapScale); + + //vertex 2 + glMaterialfv( + GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, + computeWaterColor(waterLevel, tc0->getHeight()).ptr()); + glMultiTexCoord2fv(GL_TEXTURE1, tc0->getFowTexCoord().ptr()); + glTexCoord3f(i, 0.f, waterAnim); + glVertex3f( + static_cast(i)*Map::mapScale, + waterLevel, + static_cast(j)*Map::mapScale); + + } + else{ + if(!closed){ + + pointCount+= 2; + + //vertex 1 + glMaterialfv( + GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, + computeWaterColor(waterLevel, tc1->getHeight()).ptr()); + glMultiTexCoord2fv(GL_TEXTURE1, tc1->getFowTexCoord().ptr()); + glTexCoord3f(i, 1.f, waterAnim); + glVertex3f( + static_cast(i)*Map::mapScale, + waterLevel, + static_cast(j+1)*Map::mapScale); + + //vertex 2 + glMaterialfv( + GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, + computeWaterColor(waterLevel, tc0->getHeight()).ptr()); + glMultiTexCoord2fv(GL_TEXTURE1, tc0->getFowTexCoord().ptr()); + glTexCoord3f(i, 0.f, waterAnim); + glVertex3f( + static_cast(i)*Map::mapScale, + waterLevel, + static_cast(j)*Map::mapScale); + + glEnd(); + glBegin(GL_TRIANGLE_STRIP); + closed= true; + } + } + } + glEnd(); + } + + //restore + glPopAttrib(); + + assertGl(); +} + +void Renderer::renderUnits(){ + Unit *unit; + const World *world= game->getWorld(); + MeshCallbackTeamColor meshCallbackTeamColor; + + //assert + assertGl(); + + glPushAttrib(GL_ENABLE_BIT | GL_FOG_BIT | GL_LIGHTING_BIT | GL_TEXTURE_BIT); + glEnable(GL_COLOR_MATERIAL); + + if(shadows==sShadowMapping){ + glActiveTexture(shadowTexUnit); + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, shadowMapHandle); + + static_cast(modelRenderer)->setDuplicateTexCoords(true); + enableProjectiveTexturing(); + } + glActiveTexture(baseTexUnit); + + modelRenderer->begin(true, true, true, &meshCallbackTeamColor); + + for(int i=0; igetFactionCount(); ++i){ + meshCallbackTeamColor.setTeamTexture(world->getFaction(i)->getTexture()); + for(int j=0; jgetFaction(i)->getUnitCount(); ++j){ + unit= world->getFaction(i)->getUnit(j); + if(world->toRenderUnit(unit, visibleQuad)) { + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + //translate + Vec3f currVec= unit->getCurrVectorFlat(); + glTranslatef(currVec.x, currVec.y, currVec.z); + + //rotate + glRotatef(unit->getRotation(), 0.f, 1.f, 0.f); + glRotatef(unit->getVerticalRotation(), 1.f, 0.f, 0.f); + + //dead alpha + float alpha= 1.0f; + const SkillType *st= unit->getCurrSkill(); + if(st->getClass()==scDie && static_cast(st)->getFade()){ + alpha= 1.0f-unit->getAnimProgress(); + glDisable(GL_COLOR_MATERIAL); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Vec4f(1.0f, 1.0f, 1.0f, alpha).ptr()); + } + else{ + glEnable(GL_COLOR_MATERIAL); + } + + //render + const Model *model= unit->getCurrentModel(); + model->updateInterpolationData(unit->getAnimProgress(), unit->isAlive()); + modelRenderer->render(model); + + triangleCount+= model->getTriangleCount(); + pointCount+= model->getVertexCount(); + + glPopMatrix(); + } + } + } + modelRenderer->end(); + + //restore + static_cast(modelRenderer)->setDuplicateTexCoords(true); + glPopAttrib(); + + //assert + assertGl(); +} + +void Renderer::renderSelectionEffects(){ + + const World *world= game->getWorld(); + const Map *map= world->getMap(); + const Selection *selection= game->getGui()->getSelection(); + + 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); + + //units + for(int i=0; igetCount(); ++i){ + + const Unit *unit= selection->getUnit(i); + + //translate + Vec3f currVec= unit->getCurrVectorFlat(); + currVec.y+= 0.3f; + + //selection circle + if(world->getThisFactionIndex()==unit->getFactionIndex()){ + glColor4f(0, unit->getHpRatio(), 0, 0.3f); + } + else{ + glColor4f(unit->getHpRatio(), 0, 0, 0.3f); + } + renderSelectionCircle(currVec, unit->getType()->getSize(), selectionCircleRadius); + + //magic circle + if(world->getThisFactionIndex()==unit->getFactionIndex() && unit->getType()->getMaxEp()>0){ + glColor4f(unit->getEpRatio()/2.f, unit->getEpRatio(), unit->getEpRatio(), 0.5f); + renderSelectionCircle(currVec, unit->getType()->getSize(), magicCircleRadius); + } + } + + //target arrow + if(selection->getCount()==1){ + const Unit *unit= selection->getUnit(0); + + //comand arrow + if(focusArrows && unit->anyCommand()){ + const CommandType *ct= unit->getCurrCommand()->getCommandType(); + if(ct->getClicks()!=cOne){ + + //arrow color + Vec3f arrowColor; + switch(ct->getClass()){ + case ccMove: + arrowColor= Vec3f(0.f, 1.f, 0.f); + break; + case ccAttack: + case ccAttackStopped: + arrowColor= Vec3f(1.f, 0.f, 0.f); + break; + default: + arrowColor= Vec3f(1.f, 1.f, 0.f); + } + + //arrow target + Vec3f arrowTarget; + Command *c= unit->getCurrCommand(); + if(c->getUnit()!=NULL){ + arrowTarget= c->getUnit()->getCurrVectorFlat(); + } + else{ + Vec2i pos= c->getPos(); + arrowTarget= Vec3f(pos.x, map->getCell(pos)->getHeight(), pos.y); + } + + renderArrow(unit->getCurrVectorFlat(), arrowTarget, arrowColor, 0.3f); + } + } + + //meeting point arrow + if(unit->getType()->getMeetingPoint()){ + Vec2i pos= unit->getMeetingPos(); + Vec3f arrowTarget= Vec3f(pos.x, map->getCell(pos)->getHeight(), pos.y); + renderArrow(unit->getCurrVectorFlat(), arrowTarget, Vec3f(0.f, 0.f, 1.f), 0.3f); + } + + } + + //render selection hightlights + for(int i=0; igetFactionCount(); ++i){ + for(int j=0; jgetFaction(i)->getUnitCount(); ++j){ + const Unit *unit= world->getFaction(i)->getUnit(j); + + if(unit->isHighlighted()){ + float highlight= unit->getHightlight(); + if(game->getWorld()->getThisFactionIndex()==unit->getFactionIndex()){ + glColor4f(0.f, 1.f, 0.f, highlight); + } + else{ + glColor4f(1.f, 0.f, 0.f, highlight); + } + + Vec3f v= unit->getCurrVectorFlat(); + v.y+= 0.3f; + renderSelectionCircle(v, unit->getType()->getSize(), selectionCircleRadius); + } + } + } + + glPopAttrib(); +} + +void Renderer::renderWaterEffects(){ + const World *world= game->getWorld(); + const WaterEffects *we= world->getWaterEffects(); + const Map *map= world->getMap(); + const CoreData &coreData= CoreData::getInstance(); + float height= map->getWaterLevel()+0.001f; + + assertGl(); + + glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glDepthMask(GL_FALSE); + glDepthFunc(GL_LEQUAL); + glEnable(GL_COLOR_MATERIAL); + + glNormal3f(0.f, 1.f, 0.f); + + //splashes + glBindTexture(GL_TEXTURE_2D, static_cast(coreData.getWaterSplashTexture())->getHandle()); + for(int i=0; igetWaterSplashCount(); ++i){ + const WaterSplash *ws= we->getWaterSplash(i); + + //render only if enabled + if(ws->getEnabled()){ + + //render only if visible + Vec2i intPos= Vec2i(static_cast(ws->getPos().x), static_cast(ws->getPos().y)); + if(map->getSurfaceCell(Map::toSurfCoords(intPos))->isVisible(world->getThisTeamIndex())){ + + float scale= ws->getAnim(); + + glColor4f(1.f, 1.f, 1.f, 1.f-ws->getAnim()); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0.f, 1.f); + glVertex3f(ws->getPos().x-scale, height, ws->getPos().y+scale); + glTexCoord2f(0.f, 0.f); + glVertex3f(ws->getPos().x-scale, height, ws->getPos().y-scale); + glTexCoord2f(1.f, 1.f); + glVertex3f(ws->getPos().x+scale, height, ws->getPos().y+scale); + glTexCoord2f(1.f, 0.f); + glVertex3f(ws->getPos().x+scale, height, ws->getPos().y-scale); + glEnd(); + } + } + } + + glPopAttrib(); + + assertGl(); +} + +void Renderer::renderMinimap(){ + const World *world= game->getWorld(); + const Minimap *minimap= world->getMinimap(); + const GameCamera *gameCamera= game->getGameCamera(); + const Pixmap2D *pixmap= minimap->getTexture()->getPixmap(); + const Metrics &metrics= Metrics::getInstance(); + + int mx= metrics.getMinimapX(); + int my= metrics.getMinimapY(); + int mw= metrics.getMinimapW(); + int mh= metrics.getMinimapH(); + + Vec2f zoom= Vec2f( + static_cast(mw)/ pixmap->getW(), + static_cast(mh)/ pixmap->getH()); + + assertGl(); + + glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_TEXTURE_BIT); + + //draw map + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + + glActiveTexture(fowTexUnit); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, static_cast(minimap->getFowTexture())->getHandle()); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE); + + glActiveTexture(baseTexUnit); + glBindTexture(GL_TEXTURE_2D, static_cast(minimap->getTexture())->getHandle()); + + glColor4f(0.5f, 0.5f, 0.5f, 0.1f); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0.0f, 1.0f); + glMultiTexCoord2f(fowTexUnit, 0.0f, 1.0f); + glVertex2i(mx, my); + glTexCoord2f(0.0f, 0.0f); + glMultiTexCoord2f(fowTexUnit, 0.0f, 0.0f); + glVertex2i(mx, my+mh); + glTexCoord2f(1.0f, 1.0f); + glMultiTexCoord2f(fowTexUnit, 1.0f, 1.0f); + glVertex2i(mx+mw, my); + glTexCoord2f(1.0f, 0.0f); + glMultiTexCoord2f(fowTexUnit, 1.0f, 0.0f); + glVertex2i(mx+mw, my+mh); + glEnd(); + + glDisable(GL_BLEND); + + glActiveTexture(fowTexUnit); + glDisable(GL_TEXTURE_2D); + glActiveTexture(baseTexUnit); + glDisable(GL_TEXTURE_2D); + + //draw units + glBegin(GL_QUADS); + for(int i=0; igetFactionCount(); ++i){ + for(int j=0; jgetFaction(i)->getUnitCount(); ++j){ + Unit *unit= world->getFaction(i)->getUnit(j); + if(world->toRenderUnit(unit)){ + Vec2i pos= unit->getPos()/Map::cellScale; + int size= unit->getType()->getSize(); + Vec3f color= world->getFaction(i)->getTexture()->getPixmap()->getPixel3f(0, 0); + glColor3fv(color.ptr()); + glVertex2f(mx + pos.x*zoom.x, my + mh - (pos.y*zoom.y)); + glVertex2f(mx + (pos.x+1)*zoom.x+size, my + mh - (pos.y*zoom.y)); + glVertex2f(mx + (pos.x+1)*zoom.x+size, my + mh - ((pos.y+size)*zoom.y)); + glVertex2f(mx + pos.x*zoom.x, my + mh - ((pos.y+size)*zoom.y)); + } + } + } + glEnd(); + + //draw camera + float wRatio= static_cast(metrics.getMinimapW()) / world->getMap()->getW(); + float hRatio= static_cast(metrics.getMinimapH()) / world->getMap()->getH(); + + int x= static_cast(gameCamera->getPos().x * wRatio); + int y= static_cast(gameCamera->getPos().z * hRatio); + + float ang= degToRad(gameCamera->getHAng()); + + glEnable(GL_BLEND); + + glBegin(GL_TRIANGLES); + glColor4f(1.f, 1.f, 1.f, 1.f); + glVertex2i(mx+x, my+mh-y); + + glColor4f(1.f, 1.f, 1.f, 0.0f); + glVertex2i( + mx + x + static_cast(20*sin(ang-pi/5)), + my + mh - (y-static_cast(20*cos(ang-pi/5)))); + + glColor4f(1.f, 1.f, 1.f, 0.0f); + glVertex2i( + mx + x + static_cast(20*sin(ang+pi/5)), + my + mh - (y-static_cast(20*cos(ang+pi/5)))); + + glEnd(); + glPopAttrib(); + + assertGl(); +} + +void Renderer::renderDisplay(){ + + CoreData &coreData= CoreData::getInstance(); + const Metrics &metrics= Metrics::getInstance(); + const Display *display= game->getGui()->getDisplay(); + + glPushAttrib(GL_ENABLE_BIT); + + //infoString + renderTextShadow( + display->getInfoText().c_str(), + coreData.getDisplayFont(), + metrics.getDisplayX(), + metrics.getDisplayY()+Display::infoStringY); + + //title + renderTextShadow( + display->getTitle().c_str(), + coreData.getDisplayFont(), + metrics.getDisplayX()+40, + metrics.getDisplayY() + metrics.getDisplayH() - 20); + + glColor3f(0.0f, 0.0f, 0.0f); + + //text + renderTextShadow( + display->getText().c_str(), + coreData.getDisplayFont(), + metrics.getDisplayX() -1, + metrics.getDisplayY() + metrics.getDisplayH() - 56); + + //progress Bar + if(display->getProgressBar()!=-1){ + renderProgressBar( + display->getProgressBar(), + metrics.getDisplayX(), + metrics.getDisplayY() + metrics.getDisplayH()-50, + coreData.getMenuFontSmall()); + } + + //up images + glEnable(GL_TEXTURE_2D); + + glColor3f(1.f, 1.f, 1.f); + for(int i=0; igetUpImage(i)!=NULL){ + renderQuad( + metrics.getDisplayX()+display->computeUpX(i), + metrics.getDisplayY()+display->computeUpY(i), + Display::imageSize, Display::imageSize, display->getUpImage(i)); + } + } + + //down images + for(int i=0; igetDownImage(i)!=NULL){ + if(display->getDownLighted(i)){ + glColor3f(1.f, 1.f, 1.f); + } + else{ + glColor3f(0.3f, 0.3f, 0.3f); + } + + int x= metrics.getDisplayX()+display->computeDownX(i); + int y= metrics.getDisplayY()+display->computeDownY(i); + int size= Display::imageSize; + + if(display->getDownSelectedPos()==i){ + x-= 3; + y-= 3; + size+= 6; + } + + renderQuad(x, y, size, size, display->getDownImage(i)); + } + } + + //selection + int downPos= display->getDownSelectedPos(); + if(downPos!=Display::invalidPos){ + const Texture2D *texture= display->getDownImage(downPos); + if(texture!=NULL){ + int x= metrics.getDisplayX()+display->computeDownX(downPos)-3; + int y= metrics.getDisplayY()+display->computeDownY(downPos)-3; + int size= Display::imageSize+6; + renderQuad(x, y, size, size, display->getDownImage(downPos)); + } + } + + glPopAttrib(); +} + +void Renderer::renderMenuBackground(const MenuBackground *menuBackground){ + + assertGl(); + + Vec3f cameraPosition= menuBackground->getCamera()->getPosition(); + + glPushAttrib(GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT); + + //clear + Vec4f fogColor= Vec4f(0.4f, 0.4f, 0.4f, 1.f) * menuBackground->getFade(); + glClearColor(fogColor.x, fogColor.y, fogColor.z, 1.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glFogfv(GL_FOG_COLOR, fogColor.ptr()); + + //light + Vec4f lightPos= Vec4f(10.f, 10.f, 10.f, 1.f)* menuBackground->getFade(); + Vec4f diffLight= Vec4f(0.9f, 0.9f, 0.9f, 1.f)* menuBackground->getFade(); + Vec4f ambLight= Vec4f(0.3f, 0.3f, 0.3f, 1.f)* menuBackground->getFade(); + Vec4f specLight= Vec4f(0.1f, 0.1f, 0.1f, 1.f)* menuBackground->getFade(); + + glEnable(GL_LIGHT0); + glLightfv(GL_LIGHT0, GL_POSITION, lightPos.ptr()); + glLightfv(GL_LIGHT0, GL_DIFFUSE, diffLight.ptr()); + glLightfv(GL_LIGHT0, GL_AMBIENT, ambLight.ptr()); + glLightfv(GL_LIGHT0, GL_SPECULAR, specLight.ptr()); + + //main model + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.5f); + modelRenderer->begin(true, true, true); + modelRenderer->render(menuBackground->getMainModel()); + modelRenderer->end(); + glDisable(GL_ALPHA_TEST); + + //characters + float dist= menuBackground->getAboutPosition().dist(cameraPosition); + float minDist= 3.f; + if(distbegin(true, true, false); + + for(int i=0; igetCharacterModel(i)->updateInterpolationData(menuBackground->getAnim(), true); + modelRenderer->render(menuBackground->getCharacterModel(i)); + glPopMatrix(); + } + modelRenderer->end(); + } + + //water + if(menuBackground->getWater()){ + + //water surface + const int waterTesselation= 10; + const int waterSize= 250; + const int waterQuadSize= 2*waterSize/waterTesselation; + const float waterHeight= menuBackground->getWaterHeight(); + + glEnable(GL_BLEND); + + glNormal3f(0.f, 1.f, 0.f); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Vec4f(1.f, 1.f, 1.f, 1.f).ptr()); + GLuint waterHandle= static_cast(menuBackground->getWaterTexture())->getHandle(); + glBindTexture(GL_TEXTURE_2D, waterHandle); + for(int i=1; igetRain()){ + const float maxRaindropAlpha= 0.5f; + + glEnable(GL_BLEND); + glDisable(GL_LIGHTING); + glDisable(GL_ALPHA_TEST); + glDepthMask(GL_FALSE); + + //splashes + CoreData &coreData= CoreData::getInstance(); + glBindTexture(GL_TEXTURE_2D, static_cast(coreData.getWaterSplashTexture())->getHandle()); + for(int i=0; igetRaindropPos(i); + float scale= menuBackground->getRaindropState(i); + float alpha= maxRaindropAlpha-scale*maxRaindropAlpha; + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + glColor4f(1.f, 1.f, 1.f, alpha); + glTranslatef(pos.x, waterHeight+0.01f, pos.y); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0.f, 1.f); + glVertex3f(-scale, 0, scale); + glTexCoord2f(0.f, 0.f); + glVertex3f(-scale, 0, -scale); + glTexCoord2f(1.f, 1.f); + glVertex3f(scale, 0, scale); + glTexCoord2f(1.f, 0.f); + glVertex3f(scale, 0, -scale); + glEnd(); + + glPopMatrix(); + } + } + } + + glPopAttrib(); + + assertGl(); +} + +// ==================== computing ==================== + +bool Renderer::computePosition(const Vec2i &screenPos, Vec2i &worldPos){ + + assertGl(); + const Map* map= game->getWorld()->getMap(); + const Metrics &metrics= Metrics::getInstance(); + float depth= 0.0f; + GLdouble modelviewMatrix[16]; + GLdouble projectionMatrix[16]; + GLint viewport[4]= {0, 0, metrics.getScreenW(), metrics.getScreenH()}; + GLdouble worldX; + GLdouble worldY; + GLdouble worldZ; + GLint screenX= (screenPos.x * metrics.getScreenW() / metrics.getVirtualW()); + GLint screenY= (screenPos.y * metrics.getScreenH() / metrics.getVirtualH()); + + //get the depth in the cursor pixel + glReadPixels(screenX, screenY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth); + + //load matrices + loadProjectionMatrix(); + loadGameCameraMatrix(); + + //get matrices + glGetDoublev(GL_MODELVIEW_MATRIX, modelviewMatrix); + glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix); + + //get the world coordinates + gluUnProject( + screenX, screenY, depth, + modelviewMatrix, projectionMatrix, viewport, + &worldX, &worldY, &worldZ); + + //conver coords to int + worldPos= Vec2i(static_cast(worldX+0.5f), static_cast(worldZ+0.5f)); + + //clamp coords to map size + return map->isInside(worldPos); +} + +void Renderer::computeSelected(Selection::UnitContainer &units, const Vec2i &posDown, const Vec2i &posUp){ + + //declarations + GLuint selectBuffer[Gui::maxSelBuff]; + const Metrics &metrics= Metrics::getInstance(); + + //compute center and dimensions of selection rectangle + int x= (posDown.x+posUp.x) / 2; + int y= (posDown.y+posUp.y) / 2; + int w= abs(posDown.x-posUp.x); + int h= abs(posDown.y-posUp.y); + if(w<1) w=1; + if(h<1) h=1; + + //setup matrices + glSelectBuffer(Gui::maxSelBuff, selectBuffer); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + GLint view[]= {0, 0, metrics.getVirtualW(), metrics.getVirtualH()}; + glRenderMode(GL_SELECT); + glLoadIdentity(); + gluPickMatrix(x, y, w, h, view); + gluPerspective(perspFov, metrics.getAspectRatio(), perspNearPlane, perspFarPlane); + loadGameCameraMatrix(); + + //render units + renderUnitsFast(); + + //pop matrices + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + //select units + int selCount= glRenderMode(GL_RENDER); + for(int i=1; i<=selCount; ++i){ + int factionIndex= selectBuffer[i*5-2]; + int unitIndex= selectBuffer[i*5-1]; + const World *world= game->getWorld(); + if(factionIndexgetFactionCount() && unitIndexgetFaction(factionIndex)->getUnitCount()){ + Unit *unit= world->getFaction(factionIndex)->getUnit(unitIndex); + if(unit->isAlive()){ + units.push_back(unit); + } + } + } +} + + +// ==================== shadows ==================== + +void Renderer::renderShadowsToTexture(){ + + if(shadows==sProjected || shadows==sShadowMapping){ + + shadowMapFrame= (shadowMapFrame + 1) % (shadowFrameSkip + 1); + + if(shadowMapFrame==0){ + + assertGl(); + + glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT | GL_POLYGON_BIT); + + if(shadows==sShadowMapping){ + glClear(GL_DEPTH_BUFFER_BIT); + } + else{ + float color= 1.0f-shadowAlpha; + glColor3f(color, color, color); + glClearColor(1.f, 1.f, 1.f, 1.f); + glDisable(GL_DEPTH_TEST); + glClear(GL_COLOR_BUFFER_BIT); + } + + //clear color buffer + // + //set viewport, we leave one texel always in white to avoid problems + glViewport(1, 1, shadowTextureSize-2, shadowTextureSize-2); + + if(nearestLightPos.w==0.f){ + //directional light + + //light pos + const TimeFlow *tf= game->getWorld()->getTimeFlow(); + float ang= tf->isDay()? computeSunAngle(tf->getTime()): computeMoonAngle(tf->getTime()); + ang= radToDeg(ang); + + //push and set projection + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + if(game->getGameCamera()->getState()==GameCamera::sGame){ + glOrtho(-35, 5, -15, 15, -1000, 1000); + } + else{ + glOrtho(-30, 30, -20, 20, -1000, 1000); + } + + //push and set modelview + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glRotatef(15, 0, 1, 0); + + glRotatef(ang, 1, 0, 0); + glRotatef(90, 0, 1, 0); + Vec3f pos= game->getGameCamera()->getPos(); + + glTranslatef(static_cast(-pos.x), 0, static_cast(-pos.z)); + + } + else{ + //non directional light + + //push projection + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + gluPerspective(150, 1.f, perspNearPlane, perspFarPlane); + + //push modelview + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glRotatef(-90, -1, 0, 0); + glTranslatef(-nearestLightPos.x, -nearestLightPos.y-2, -nearestLightPos.z); + } + + if(shadows==sShadowMapping){ + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(1.0f, 0.001f); + } + + //render 3d + renderUnitsFast(); + renderObjectsFast(); + + //read color buffer + glBindTexture(GL_TEXTURE_2D, shadowMapHandle); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, shadowTextureSize, shadowTextureSize); + + //get elemental matrices + Matrix4f matrix1; + matrix1[0]= 0.5f; matrix1[4]= 0.f; matrix1[8]= 0.f; matrix1[12]= 0.5f; + matrix1[1]= 0.f; matrix1[5]= 0.5f; matrix1[9]= 0.f; matrix1[13]= 0.5f; + matrix1[2]= 0.f; matrix1[6]= 0.f; matrix1[10]= 0.5f; matrix1[14]= 0.5f; + matrix1[3]= 0.f; matrix1[7]= 0.f; matrix1[11]= 0.f; matrix1[15]= 1.f; + + Matrix4f matrix2; + glGetFloatv(GL_PROJECTION_MATRIX, matrix2.ptr()); + + Matrix4f matrix3; + glGetFloatv(GL_MODELVIEW_MATRIX, matrix3.ptr()); + + //pop both matrices + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + + //compute texture matrix + glLoadMatrixf(matrix1.ptr()); + glMultMatrixf(matrix2.ptr()); + glMultMatrixf(matrix3.ptr()); + glGetFloatv(GL_TRANSPOSE_PROJECTION_MATRIX_ARB, shadowMapMatrix.ptr()); + + //pop + glPopMatrix(); + + glPopAttrib(); + + assertGl(); + } + } +} + + +// ==================== gl wrap ==================== + +string Renderer::getGlInfo(){ + string infoStr; + Lang &lang= Lang::getInstance(); + + infoStr+= lang.get("OpenGlInfo")+":\n"; + infoStr+= " "+lang.get("OpenGlVersion")+": "; + infoStr+= string(getGlVersion())+"\n"; + infoStr+= " "+lang.get("OpenGlRenderer")+": "; + infoStr+= string(getGlRenderer())+"\n"; + infoStr+= " "+lang.get("OpenGlVendor")+": "; + infoStr+= string(getGlVendor())+"\n"; + infoStr+= " "+lang.get("OpenGlMaxLights")+": "; + infoStr+= intToStr(getGlMaxLights())+"\n"; + infoStr+= " "+lang.get("OpenGlMaxTextureSize")+": "; + infoStr+= intToStr(getGlMaxTextureSize())+"\n"; + infoStr+= " "+lang.get("OpenGlMaxTextureUnits")+": "; + infoStr+= intToStr(getGlMaxTextureUnits())+"\n"; + infoStr+= " "+lang.get("OpenGlModelviewStack")+": "; + infoStr+= intToStr(getGlModelviewMatrixStackDepth())+"\n"; + infoStr+= " "+lang.get("OpenGlProjectionStack")+": "; + infoStr+= intToStr(getGlProjectionMatrixStackDepth())+"\n"; + + return infoStr; +} + +string Renderer::getGlMoreInfo(){ + string infoStr; + Lang &lang= Lang::getInstance(); + + //gl extensions + infoStr+= lang.get("OpenGlExtensions")+":\n "; + + string extensions= getGlExtensions(); + int charCount= 0; + for(int i=0; i120 && extensions[i]==' '){ + infoStr+= "\n "; + charCount= 0; + } + ++charCount; + } + + //platform extensions + infoStr+= "\n\n"; + infoStr+= lang.get("OpenGlPlatformExtensions")+":\n "; + + charCount= 0; + string platformExtensions= getGlPlatformExtensions(); + for(int i=0; i120 && platformExtensions[i]==' '){ + infoStr+= "\n "; + charCount= 0; + } + ++charCount; + } + + return infoStr; +} + +void Renderer::autoConfig(){ + + Config &config= Config::getInstance(); + bool nvidiaCard= toLower(getGlVendor()).find("nvidia")!=string::npos; + bool atiCard= toLower(getGlVendor()).find("ati")!=string::npos; + bool shadowExtensions = isGlExtensionSupported("GL_ARB_shadow") && isGlExtensionSupported("GL_ARB_shadow_ambient"); + + //3D textures + config.setBool("Textures3D", isGlExtensionSupported("GL_EXT_texture3D")); + + //shadows + string shadows; + if(getGlMaxTextureUnits()>=3){ + if(nvidiaCard && shadowExtensions){ + shadows= shadowsToStr(sShadowMapping); + } + else{ + shadows= shadowsToStr(sProjected); + } + } + else{ + shadows=shadowsToStr(sDisabled); + } + config.setString("Shadows", shadows); + + //lights + config.setInt("MaxLights", atiCard? 1: 4); + + //filter + config.setString("Filter", "Bilinear"); +} + +void Renderer::clearBuffers(){ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void Renderer::clearZBuffer(){ + glClear(GL_DEPTH_BUFFER_BIT); +} + +void Renderer::loadConfig(){ + Config &config= Config::getInstance(); + + //cache most used config params + maxLights= config.getInt("MaxLights"); + photoMode= config.getBool("PhotoMode"); + focusArrows= config.getBool("FocusArrows"); + textures3D= config.getBool("Textures3D"); + + //load shadows + shadows= strToShadows(config.getString("Shadows")); + if(shadows==sProjected || shadows==sShadowMapping){ + shadowTextureSize= config.getInt("ShadowTextureSize"); + shadowFrameSkip= config.getInt("ShadowFrameSkip"); + shadowAlpha= config.getFloat("ShadowAlpha"); + } + + //load filter settings + Texture2D::Filter textureFilter= strToTextureFilter(config.getString("Filter")); + int maxAnisotropy= config.getInt("FilterMaxAnisotropy"); + for(int i=0; isetFilter(textureFilter); + textureManager[i]->setMaxAnisotropy(maxAnisotropy); + } +} + +void Renderer::saveScreen(const string &path){ + + const Metrics &sm= Metrics::getInstance(); + + Pixmap2D pixmap(sm.getScreenW(), sm.getScreenH(), 3); + + glReadPixels(0, 0, pixmap.getW(), pixmap.getH(), GL_RGB, GL_UNSIGNED_BYTE, pixmap.getPixels()); + pixmap.saveTga(path); +} + +// ==================== PRIVATE ==================== + +float Renderer::computeSunAngle(float time){ + + float dayTime= TimeFlow::dusk-TimeFlow::dawn; + float fTime= (time-TimeFlow::dawn)/dayTime; + return clamp(fTime*pi, pi/8.f, 7.f*pi/8.f); +} + +float Renderer::computeMoonAngle(float time){ + float nightTime= 24-(TimeFlow::dusk-TimeFlow::dawn); + + if(timegetWorld()->getTileset(); + Vec3f color; + + const float transition= 2; + const float dayStart= TimeFlow::dawn; + const float dayEnd= TimeFlow::dusk-transition; + const float nightStart= TimeFlow::dusk; + const float nightEnd= TimeFlow::dawn-transition; + + if(time>dayStart && timegetSunLightColor(); + } + else if(time>nightStart || timegetMoonLightColor(); + } + else if(time>=dayEnd && time<=nightStart){ + color= tileset->getSunLightColor().lerp((time-dayEnd)/transition, tileset->getMoonLightColor()); + } + else if(time>=nightEnd && time<=dayStart){ + color= tileset->getMoonLightColor().lerp((time-nightEnd)/transition, tileset->getSunLightColor()); + } + else{ + assert(false); + color= tileset->getSunLightColor(); + } + return color; +} + +Vec4f Renderer::computeWaterColor(float waterLevel, float cellHeight){ + const float waterFactor= 1.5f; + return Vec4f(1.f, 1.f, 1.f, clamp((waterLevel-cellHeight)*waterFactor, 0.f, 1.f)); +} + +// ==================== fast render ==================== + +//render units for selection purposes +void Renderer::renderUnitsFast(){ + const World *world= game->getWorld(); + + assertGl(); + + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + + modelRenderer->begin(false, false, false); + glInitNames(); + for(int i=0; igetFactionCount(); ++i){ + glPushName(i); + for(int j=0; jgetFaction(i)->getUnitCount(); ++j){ + glPushName(j); + Unit *unit= world->getFaction(i)->getUnit(j); + if(world->toRenderUnit(unit, visibleQuad)) { + glMatrixMode(GL_MODELVIEW); + + //debuxar modelo + glPushMatrix(); + + //translate + Vec3f currVec= unit->getCurrVectorFlat(); + glTranslatef(currVec.x, currVec.y, currVec.z); + + //rotate + glRotatef(unit->getRotation(), 0.f, 1.f, 0.f); + + //render + const Model *model= unit->getCurrentModel(); + model->updateInterpolationVertices(unit->getAnimProgress(), unit->isAlive()); + modelRenderer->render(model); + + glPopMatrix(); + + } + glPopName(); + } + glPopName(); + } + modelRenderer->end(); + + glPopAttrib(); +} + +//render objects for selection purposes +void Renderer::renderObjectsFast(){ + const World *world= game->getWorld(); + const Map *map= world->getMap(); + + assertGl(); + + glPushAttrib(GL_ENABLE_BIT| GL_TEXTURE_BIT); + glDisable(GL_LIGHTING); + + glAlphaFunc(GL_GREATER, 0.5f); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + //set color to the texture alpha + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + + //set alpha to the texture alpha + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + modelRenderer->begin(false, true, false); + int thisTeamIndex= world->getThisTeamIndex(); + + PosQuadIterator pqi(map, visibleQuad, Map::cellScale); + while(pqi.next()){ + const Vec2i pos= pqi.getPos(); + + if(map->isInside(pos)){ + + SurfaceCell *sc= map->getSurfaceCell(Map::toSurfCoords(pos)); + Object *o= sc->getObject(); + if(sc->isExplored(thisTeamIndex) && o!=NULL){ + + const Model *objModel= sc->getObject()->getModel(); + Vec3f v= o->getPos(); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glTranslatef(v.x, v.y, v.z); + glRotatef(o->getRotation(), 0.f, 1.f, 0.f); + + modelRenderer->render(objModel); + + glPopMatrix(); + + } + } + } + + modelRenderer->end(); + + glPopAttrib(); + + assertGl(); +} + +// ==================== gl caps ==================== + +void Renderer::checkGlCaps(){ + + //opengl 1.3 + if(!isGlVersionSupported(1, 3, 0)){ + string message; + + message += "Your system supports OpenGL version \""; + message += getGlVersion() + string("\"\n"); + message += "Glest needs at least version 1.3 to work\n"; + message += "You may solve this problem by installing your latest video card drivers"; + + throw runtime_error(message.c_str()); + } + + //opengl 1.4 or extension + if(!isGlVersionSupported(1, 4, 0)){ + checkExtension("GL_ARB_texture_env_crossbar", "Glest"); + } +} + +void Renderer::checkGlOptionalCaps(){ + + //shadows + if(shadows==sProjected || shadows==sShadowMapping){ + if(getGlMaxTextureUnits()<3){ + throw runtime_error("Your system doesn't support 3 texture units, required for shadows"); + } + } + + //shadow mapping + if(shadows==sShadowMapping){ + checkExtension("GL_ARB_shadow", "Shadow Mapping"); + checkExtension("GL_ARB_shadow_ambient", "Shadow Mapping"); + } +} + +void Renderer::checkExtension(const string &extension, const string &msg){ + if(!isGlExtensionSupported(extension.c_str())){ + string str= "OpenGL extension not supported: " + extension + ", required for " + msg; + throw runtime_error(str); + } +} + +// ==================== init 3d lists ==================== + +void Renderer::init3dList(){ + + const Metrics &metrics= Metrics::getInstance(); + + assertGl(); + + list3d= glGenLists(1); + glNewList(list3d, GL_COMPILE_AND_EXECUTE); + //need to execute, because if not gluPerspective takes no effect and gluLoadMatrix is wrong + + //misc + glViewport(0, 0, metrics.getScreenW(), metrics.getScreenH()); + glClearColor(fowColor.x, fowColor.y, fowColor.z, fowColor.w); + glFrontFace(GL_CW); + glEnable(GL_CULL_FACE); + loadProjectionMatrix(); + + //texture state + glActiveTexture(shadowTexUnit); + glDisable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glActiveTexture(fowTexUnit); + glDisable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glActiveTexture(baseTexUnit); + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + //material state + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, defSpecularColor.ptr()); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, defAmbientColor.ptr()); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, defDiffuseColor.ptr()); + glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); + glColor4fv(defColor.ptr()); + + //blend state + glDisable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + //alpha test state + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.f); + + //depth test state + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glDepthFunc(GL_LESS); + + //lighting state + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + + //matrix mode + glMatrixMode(GL_MODELVIEW); + + //stencil test + glDisable(GL_STENCIL_TEST); + + //fog + const Tileset *tileset= game->getWorld()->getTileset(); + if(tileset->getFog()){ + glEnable(GL_FOG); + if(tileset->getFogMode()==fmExp){ + glFogi(GL_FOG_MODE, GL_EXP); + } + else{ + glFogi(GL_FOG_MODE, GL_EXP2); + } + + glFogf(GL_FOG_DENSITY, tileset->getFogDensity()); + glFogfv(GL_FOG_COLOR, tileset->getFogColor().ptr()); + } + + glEndList(); + + //assert + assertGl(); + +} + +void Renderer::init2dList(){ + + const Metrics &metrics= Metrics::getInstance(); + + //this list sets the state for the 2d rendering + list2d= glGenLists(1); + glNewList(list2d, GL_COMPILE); + + //projection + glViewport(0, 0, metrics.getScreenW(), metrics.getScreenH()); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, metrics.getVirtualW(), 0, metrics.getVirtualH(), 0, 1); + + //modelview + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + //disable everything + glDisable(GL_BLEND); + glDisable(GL_LIGHTING); + glDisable(GL_ALPHA_TEST); + glDisable(GL_DEPTH_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_FOG); + glDisable(GL_CULL_FACE); + glFrontFace(GL_CCW); + glActiveTexture(baseTexUnit); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glDisable(GL_TEXTURE_2D); + + //blend func + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + //color + glColor4f(1.f, 1.f, 1.f, 1.f); + + glEndList(); + + assertGl(); +} + +void Renderer::init3dListMenu(MainMenu *mm){ + assertGl(); + + const Metrics &metrics= Metrics::getInstance(); + const MenuBackground *mb= mm->getMenuBackground(); + + list3dMenu= glGenLists(1); + glNewList(list3dMenu, GL_COMPILE); + + //misc + glViewport(0, 0, metrics.getScreenW(), metrics.getScreenH()); + glClearColor(0.4f, 0.4f, 0.4f, 1.f); + glFrontFace(GL_CW); + glEnable(GL_CULL_FACE); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(perspFov, metrics.getAspectRatio(), perspNearPlane, 1000); + + //texture state + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + //material state + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, defSpecularColor.ptr()); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, defAmbientColor.ptr()); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, defDiffuseColor.ptr()); + glColor4fv(defColor.ptr()); + glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); + + //blend state + glDisable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + //alpha test state + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.f); + + //depth test state + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glDepthFunc(GL_LESS); + + //lighting state + glEnable(GL_LIGHTING); + + //matrix mode + glMatrixMode(GL_MODELVIEW); + + //stencil test + glDisable(GL_STENCIL_TEST); + + //fog + if(mb->getFog()){ + glEnable(GL_FOG); + glFogi(GL_FOG_MODE, GL_EXP2); + glFogf(GL_FOG_DENSITY, mb->getFogDensity()); + } + + glEndList(); + + //assert + assertGl(); +} + + +// ==================== misc ==================== + +void Renderer::loadProjectionMatrix(){ + GLdouble clipping; + const Metrics &metrics= Metrics::getInstance(); + + assertGl(); + + clipping= photoMode ? perspFarPlane*100 : perspFarPlane; + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(perspFov, metrics.getAspectRatio(), perspNearPlane, clipping); + + assertGl(); +} + +void Renderer::enableProjectiveTexturing(){ + glTexGenfv(GL_S, GL_EYE_PLANE, &shadowMapMatrix[0]); + glTexGenfv(GL_T, GL_EYE_PLANE, &shadowMapMatrix[4]); + glTexGenfv(GL_R, GL_EYE_PLANE, &shadowMapMatrix[8]); + glTexGenfv(GL_Q, GL_EYE_PLANE, &shadowMapMatrix[12]); + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + glEnable(GL_TEXTURE_GEN_R); + glEnable(GL_TEXTURE_GEN_Q); +} + +// ==================== private aux drawing ==================== + +void Renderer::renderSelectionCircle(Vec3f v, int size, float radius){ + GLUquadricObj *disc; + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + glTranslatef(v.x, v.y, v.z); + glRotatef(90.f, 1.f, 0.f, 0.f); + disc= gluNewQuadric(); + gluQuadricDrawStyle(disc, GLU_FILL); + gluCylinder(disc, radius*(size-0.2f), radius*size, 0.2f, 30, 1); + gluDeleteQuadric(disc); + + glPopMatrix(); +} + +void Renderer::renderArrow(const Vec3f &pos1, const Vec3f &pos2, const Vec3f &color, float width){ + const int tesselation= 3; + const float arrowEndSize= 0.4f; + const float maxlen= 25; + const float blendDelay= 5.f; + + Vec3f dir= Vec3f(pos2-pos1); + float len= dir.length(); + + if(len>maxlen){ + return; + } + float alphaFactor= clamp((maxlen-len)/blendDelay, 0.f, 1.f); + + dir.normalize(); + Vec3f normal= dir.cross(Vec3f(0, 1, 0)); + + Vec3f pos2Left= pos2 + normal*(width-0.05f) - dir*arrowEndSize*width; + Vec3f pos2Right= pos2 - normal*(width-0.05f) - dir*arrowEndSize*width; + Vec3f pos1Left= pos1 + normal*(width+0.05f); + Vec3f pos1Right= pos1 - normal*(width+0.05f); + + //arrow body + glBegin(GL_TRIANGLE_STRIP); + for(int i=0; i<=tesselation; ++i){ + float t= static_cast(i)/tesselation; + Vec3f a= pos1Left.lerp(t, pos2Left); + Vec3f b= pos1Right.lerp(t, pos2Right); + Vec4f c= Vec4f(color, t*0.25f*alphaFactor); + + glColor4fv(c.ptr()); + glVertex3fv(a.ptr()); + glVertex3fv(b.ptr()); + } + glEnd(); + + //arrow end + glBegin(GL_TRIANGLES); + glVertex3fv((pos2Left + normal*(arrowEndSize-0.1f)).ptr()); + glVertex3fv((pos2Right - normal*(arrowEndSize-0.1f)).ptr()); + glVertex3fv((pos2 + dir*(arrowEndSize-0.1f)).ptr()); + glEnd(); +} + +void Renderer::renderProgressBar(int size, int x, int y, Font2D *font){ + + //bar + glBegin(GL_QUADS); + glColor4fv(progressBarFront2.ptr()); + glVertex2i(x, y); + glVertex2i(x, y+10); + glColor4fv(progressBarFront1.ptr()); + glVertex2i(x+size, y+10); + glVertex2i(x+size, y); + glEnd(); + + //transp bar + glEnable(GL_BLEND); + glBegin(GL_QUADS); + glColor4fv(progressBarBack2.ptr()); + glVertex2i(x+size, y); + glVertex2i(x+size, y+10); + glColor4fv(progressBarBack1.ptr()); + glVertex2i(x+maxProgressBar, y+10); + glVertex2i(x+maxProgressBar, y); + glEnd(); + glDisable(GL_BLEND); + + //text + glColor3fv(defColor.ptr()); + textRenderer->begin(font); + textRenderer->render(intToStr(static_cast(size))+"%", x+maxProgressBar/2, y, true); + textRenderer->end(); +} + + +void Renderer::renderTile(const Vec2i &pos){ + + const Map *map= game->getWorld()->getMap(); + Vec2i scaledPos= pos * Map::cellScale; + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glTranslatef(-0.5f, 0.f, -0.5f); + + glInitNames(); + for(int i=0; i(renderPos.x), + map->getCell(renderPos.x, renderPos.y)->getHeight(), + static_cast(renderPos.y)); + glVertex3f( + static_cast(renderPos.x), + map->getCell(renderPos.x, renderPos.y+1)->getHeight(), + static_cast(renderPos.y+1)); + glVertex3f( + static_cast(renderPos.x+1), + map->getCell(renderPos.x+1, renderPos.y)->getHeight(), + static_cast(renderPos.y)); + glVertex3f( + static_cast(renderPos.x+1), + map->getCell(renderPos.x+1, renderPos.y+1)->getHeight(), + static_cast(renderPos.y+1)); + glEnd(); + + glPopName(); + glPopName(); + } + } + + glPopMatrix(); +} + +void Renderer::renderQuad(int x, int y, int w, int h, const Texture2D *texture){ + glBindTexture(GL_TEXTURE_2D, static_cast(texture)->getHandle()); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2i(0, 1); + glVertex2i(x, y+h); + glTexCoord2i(0, 0); + glVertex2i(x, y); + glTexCoord2i(1, 1); + glVertex2i(x+w, y+h); + glTexCoord2i(1, 0); + glVertex2i(x+w, y); + glEnd(); +} + +Renderer::Shadows Renderer::strToShadows(const string &s){ + if(s=="Projected"){ + return sProjected; + } + else if(s=="ShadowMapping"){ + return sShadowMapping; + } + return sDisabled; +} + +string Renderer::shadowsToStr(Shadows shadows){ + switch(shadows){ + case sDisabled: + return "Disabled"; + case sProjected: + return "Projected"; + case sShadowMapping: + return "ShadowMapping"; + default: + assert(false); + return ""; + } +} + +Texture2D::Filter Renderer::strToTextureFilter(const string &s){ + if(s=="Bilinear"){ + return Texture2D::fBilinear; + } + else if(s=="Trilinear"){ + return Texture2D::fTrilinear; + } + + throw runtime_error("Error converting from string to FilterType, found: "+s); +} + +}}//end namespace diff --git a/source/glest_game/graphics/renderer.h b/source/glest_game/graphics/renderer.h new file mode 100644 index 00000000..a464cbd4 --- /dev/null +++ b/source/glest_game/graphics/renderer.h @@ -0,0 +1,307 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_RENDERER_H_ +#define _GLEST_GAME_RENDERER_H_ + +#include "vec.h" +#include "math_util.h" +#include "model.h" +#include "particle.h" +#include "pixmap.h" +#include "font.h" +#include "matrix.h" +#include "selection.h" +#include "components.h" +#include "texture.h" +#include "model_manager.h" +#include "graphics_factory_gl.h" +#include "font_manager.h" +#include "camera.h" + +namespace Glest{ namespace Game{ + +using Shared::Graphics::Texture2D; +using Shared::Graphics::Texture3D; +using Shared::Graphics::ModelRenderer; +using Shared::Graphics::TextRenderer2D; +using Shared::Graphics::ParticleRenderer; +using Shared::Graphics::ParticleManager; +using Shared::Graphics::ModelManager; +using Shared::Graphics::TextureManager; +using Shared::Graphics::FontManager; +using Shared::Graphics::Font2D; +using Shared::Graphics::Matrix4f; +using Shared::Graphics::Vec2i; +using Shared::Graphics::Quad2i; +using Shared::Graphics::Vec3f; +using Shared::Graphics::Model; +using Shared::Graphics::ParticleSystem; +using Shared::Graphics::Pixmap2D; +using Shared::Graphics::Camera; + +//non shared classes +class Config; +class Game; +class MainMenu; +class Console; +class MenuBackground; +class ChatManager; + +enum ResourceScope{ + rsGlobal, + rsMenu, + rsGame, + + rsCount +}; + +// =========================================================== +// class Renderer +// +/// OpenGL renderer, uses the shared library +// =========================================================== + +class Renderer{ +public: + //progress bar + static const int maxProgressBar; + static const Vec4f progressBarBack1; + static const Vec4f progressBarBack2; + static const Vec4f progressBarFront1; + static const Vec4f progressBarFront2; + + //sun and moon + static const float sunDist; + static const float moonDist; + static const float lightAmbFactor; + + //mouse + static const int maxMouse2dAnim; + + //texture units + static const GLenum baseTexUnit; + static const GLenum fowTexUnit; + static const GLenum shadowTexUnit; + + //selection + static const float selectionCircleRadius; + static const float magicCircleRadius; + + //perspective values + static const float perspFov; + static const float perspNearPlane; + static const float perspFarPlane; + + //default values + static const float ambFactor; + static const Vec4f defSpecularColor; + static const Vec4f defDiffuseColor; + static const Vec4f defAmbientColor; + static const Vec4f defColor; + static const Vec4f fowColor; + + //light + static const float maxLightDist; + +public: + enum Shadows{ + sDisabled, + sProjected, + sShadowMapping, + + sCount + }; + +private: + //config + int maxLights; + bool photoMode; + int shadowTextureSize; + int shadowFrameSkip; + float shadowAlpha; + bool focusArrows; + bool textures3D; + Shadows shadows; + + //game + const Game *game; + + //misc + int triangleCount; + int pointCount; + Quad2i visibleQuad; + Vec4f nearestLightPos; + + //renderers + ModelRenderer *modelRenderer; + TextRenderer2D *textRenderer; + ParticleRenderer *particleRenderer; + + //texture managers + ModelManager *modelManager[rsCount]; + TextureManager *textureManager[rsCount]; + FontManager *fontManager[rsCount]; + ParticleManager *particleManager[rsCount]; + + //state lists + GLuint list3d; + GLuint list2d; + GLuint list3dMenu; + + //shadows + GLuint shadowMapHandle; + Matrix4f shadowMapMatrix; + int shadowMapFrame; + + //water + float waterAnim; + +private: + Renderer(); + ~Renderer(); + +public: + static Renderer &getInstance(); + + //init + void init(); + void initGame(Game *game); + void initMenu(MainMenu *mm); + void reset3d(); + void reset2d(); + void reset3dMenu(); + + //end + void end(); + void endMenu(); + void endGame(); + + //get + int getTriangleCount() const {return triangleCount;} + int getPointCount() const {return pointCount;} + + //misc + void reloadResources(); + + //engine interface + Model *newModel(ResourceScope rs); + Texture2D *newTexture2D(ResourceScope rs); + Texture3D *newTexture3D(ResourceScope rs); + Font2D *newFont(ResourceScope rs); + TextRenderer2D *getTextRenderer() const {return textRenderer;} + void manageParticleSystem(ParticleSystem *particleSystem, ResourceScope rs); + void updateParticleManager(ResourceScope rs); + void renderParticleManager(ResourceScope rs); + void swapBuffers(); + + //lights and camera + void setupLighting(); + void loadGameCameraMatrix(); + void loadCameraMatrix(const Camera *camera); + void computeVisibleQuad(); + + //basic rendering + void renderMouse2d(int mouseX, int mouseY, int anim, float fade= 0.f); + void renderMouse3d(); + void renderBackground(const Texture2D *texture); + void renderTextureQuad(int x, int y, int w, int h, const Texture2D *texture, float alpha=1.f); + void renderConsole(const Console *console); + void renderChatManager(const ChatManager *chatManager); + void renderResourceStatus(); + void renderSelectionQuad(); + void renderText(const string &text, const Font2D *font, float alpha, int x, int y, bool centered= false); + void renderText(const string &text, const Font2D *font, const Vec3f &color, int x, int y, bool centered= false); + void renderTextShadow(const string &text, const Font2D *font, int x, int y, bool centered= false); + + //components + void renderLabel(const GraphicLabel *label); + void renderButton(const GraphicButton *button); + void renderListBox(const GraphicListBox *listBox); + void renderMessageBox(const GraphicMessageBox *listBox); + + //complex rendering + void renderSurface(); + void renderObjects(); + void renderWater(); + void renderUnits(); + void renderSelectionEffects(); + void renderWaterEffects(); + void renderMinimap(); + void renderDisplay(); + void renderMenuBackground(const MenuBackground *menuBackground); + + //computing + bool computePosition(const Vec2i &screenPos, Vec2i &worldPos); + void computeSelected(Selection::UnitContainer &units, const Vec2i &posDown, const Vec2i &posUp); + + //gl wrap + string getGlInfo(); + string getGlMoreInfo(); + void autoConfig(); + + //clear + void clearBuffers(); + void clearZBuffer(); + + //shadows + void renderShadowsToTexture(); + + //misc + void loadConfig(); + void saveScreen(const string &path); + Quad2i getVisibleQuad() const {return visibleQuad;} + + //static + static Shadows strToShadows(const string &s); + static string shadowsToStr(Shadows shadows); + +private: + //private misc + float computeSunAngle(float time); + float computeMoonAngle(float time); + Vec4f computeSunPos(float time); + Vec4f computeMoonPos(float time); + Vec3f computeLightColor(float time); + Vec4f computeWaterColor(float waterLevel, float cellHeight); + void checkExtension(const string &extension, const string &msg); + + //selection render + void renderObjectsFast(); + void renderUnitsFast(); + + //gl requirements + void checkGlCaps(); + void checkGlOptionalCaps(); + + //gl init + void init3dList(); + void init2dList(); + void init3dListMenu(MainMenu *mm); + + //misc + void loadProjectionMatrix(); + void enableProjectiveTexturing(); + + //private aux drawing + void renderSelectionCircle(Vec3f v, int size, float radius); + void renderArrow(const Vec3f &pos1, const Vec3f &pos2, const Vec3f &color, float width); + void renderProgressBar(int size, int x, int y, Font2D *font); + void renderTile(const Vec2i &pos); + void renderQuad(int x, int y, int w, int h, const Texture2D *texture); + + //static + static Texture2D::Filter strToTextureFilter(const string &s); +}; + +}} //end namespace + +#endif diff --git a/source/glest_game/gui/display.cpp b/source/glest_game/gui/display.cpp new file mode 100644 index 00000000..440b0b57 --- /dev/null +++ b/source/glest_game/gui/display.cpp @@ -0,0 +1,83 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "display.h" + +#include "metrics.h" +#include "command_type.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Display +// ===================================================== + +Display::Display(){ + clear(); +} + +//misc +void Display::clear(){ + for(int i=0; iimageSize*cellSideCount){ + return invalidPos; + } + + int cellX= x/imageSize; + int cellY= (y/imageSize) % cellSideCount; + int index= (cellSideCount-cellY-1)*cellSideCount+cellX;; + + if(index<0 || index>=downCellCount || downImages[index]==NULL){ + index= invalidPos; + } + + return index; +} + +int Display::computeDownX(int index) const{ + return (index % cellSideCount) * imageSize; +} + +int Display::computeDownY(int index) const{ + return Display::downY - (index/cellSideCount)*imageSize - imageSize; +} + +int Display::computeUpX(int index) const{ + return (index % cellSideCount) * imageSize; +} + +int Display::computeUpY(int index) const{ + return Metrics::getInstance().getDisplayH() - (index/cellSideCount)*imageSize - imageSize; +} + +}}//end namespace diff --git a/source/glest_game/gui/display.h b/source/glest_game/gui/display.h new file mode 100644 index 00000000..5fb51386 --- /dev/null +++ b/source/glest_game/gui/display.h @@ -0,0 +1,96 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_DISPLAY_H_ +#define _GLEST_GAME_DISPLAY_H_ + +#include + +#include "texture.h" +#include "util.h" +#include "command_type.h" +#include "game_util.h" + +using std::string; + +using Shared::Graphics::Texture2D; +using Shared::Graphics::Vec3f; +using Shared::Util::replaceBy; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Display +// +/// Display for unit commands, and unit selection +// ===================================================== + +class Display{ +public: + static const int cellSideCount= 4; + static const int upCellCount= cellSideCount*cellSideCount; + static const int downCellCount= cellSideCount*cellSideCount; + static const int imageSize= 32; + static const int invalidPos= -1; + static const int downY = imageSize*9; + static const int infoStringY= imageSize*4; + +private: + string title; + string text; + string infoText; + const Texture2D *upImages[upCellCount]; + const Texture2D *downImages[downCellCount]; + bool downLighted[downCellCount]; + const CommandType *commandTypes[downCellCount]; + CommandClass commandClasses[downCellCount]; + int progressBar; + int downSelectedPos; + +public: + Display(); + + //get + string getTitle() const {return title;} + string getText() const {return text;} + string getInfoText() const {return infoText;} + const Texture2D *getUpImage(int index) const {return upImages[index];} + const Texture2D *getDownImage(int index) const {return downImages[index];} + bool getDownLighted(int index) const {return downLighted[index];} + const CommandType *getCommandType(int i) {return commandTypes[i];} + CommandClass getCommandClass(int i) {return commandClasses[i];} + int getProgressBar() const {return progressBar;} + int getDownSelectedPos() const {return downSelectedPos;} + + //set + void setTitle(const string title) {this->title= formatString(title);} + void setText(const string &text) {this->text= formatString(text);} + void setInfoText(const string infoText) {this->infoText= formatString(infoText);} + void setUpImage(int i, const Texture2D *image) {upImages[i]= image;} + void setDownImage(int i, const Texture2D *image) {downImages[i]= image;} + void setCommandType(int i, const CommandType *ct) {commandTypes[i]= ct;} + void setCommandClass(int i, const CommandClass cc) {commandClasses[i]= cc;} + void setDownLighted(int i, bool lighted) {downLighted[i]= lighted;} + void setProgressBar(int i) {progressBar= i;} + void setDownSelectedPos(int i) {downSelectedPos= i;} + + //misc + void clear(); + int computeDownIndex(int x, int y); + int computeDownX(int index) const; + int computeDownY(int index) const; + int computeUpX(int index) const; + int computeUpY(int index) const; +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/gui/gui.cpp b/source/glest_game/gui/gui.cpp new file mode 100644 index 00000000..c5dda9a3 --- /dev/null +++ b/source/glest_game/gui/gui.cpp @@ -0,0 +1,849 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "gui.h" + +#include +#include + +#include "world.h" +#include "renderer.h" +#include "game.h" +#include "upgrade.h" +#include "unit.h" +#include "metrics.h" +#include "display.h" +#include "platform_util.h" +#include "sound_renderer.h" +#include "util.h" +#include "faction.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Mouse3d +// ===================================================== + +const float Mouse3d::fadeSpeed= 1.f/50.f; + +Mouse3d::Mouse3d(){ + enabled= false; + rot= 0; + fade= 0.f; +} + +void Mouse3d::enable(){ + enabled= true; + fade= 0.f; +} + +void Mouse3d::update(){ + if(enabled){ + rot= (rot + 3) % 360; + fade+= fadeSpeed; + if(fade>1.f) fade= 1.f; + } +} + +// =============================== +// class SelectionQuad +// =============================== + +SelectionQuad::SelectionQuad(){ + enabled= false; + posDown= Vec2i(0); + posUp= Vec2i(0); +} + +void SelectionQuad::setPosDown(const Vec2i &posDown){ + enabled= true; + this->posDown= posDown; + this->posUp= posDown; +} + +void SelectionQuad::setPosUp(const Vec2i &posUp){ + this->posUp= posUp; +} + +void SelectionQuad::disable(){ + enabled= false; +} + +// ===================================================== +// class Gui +// ===================================================== + +//constructor +Gui::Gui(){ + posObjWorld= Vec2i(54, 14); + computeSelection= false; + validPosObjWorld= false; + activeCommandType= NULL; + activeCommandClass= ccStop; + selectingBuilding= false; + selectingPos= false; + selectingMeetingPoint= false; + activePos= invalidPos; +} + +void Gui::init(Game *game){ + this->commander= game->getCommander(); + this->gameCamera= game->getGameCamera(); + this->console= game->getConsole(); + this->world= game->getWorld(); + selection.init(this, world->getThisFactionIndex()); +} + +void Gui::end(){ + selection.clear(); +} + +// ==================== get ==================== + +const UnitType *Gui::getBuilding() const{ + assert(selectingBuilding); + return choosenBuildingType; +} + +// ==================== is ==================== + +bool Gui::isPlacingBuilding() const{ + return isSelectingPos() && activeCommandType!=NULL && activeCommandType->getClass()==ccBuild; +} + +// ==================== set ==================== + +void Gui::invalidatePosObjWorld(){ + validPosObjWorld= false; +} + +void Gui::setComputeSelectionFlag(){ + computeSelection= true; +} + + +// ==================== reset state ==================== + +void Gui::resetState(){ + selectingBuilding= false; + selectingPos= false; + selectingMeetingPoint= false; + activePos= invalidPos; + activeCommandClass= ccStop; + activeCommandType= NULL; +} + +// ==================== events ==================== + +void Gui::update(){ + setComputeSelectionFlag(); + mouse3d.update(); +} + +void Gui::tick(){ + computeDisplay(); +} + +//in display coords +bool Gui::mouseValid(int x, int y){ + return computePosDisplay(x, y) != invalidPos; +} + +void Gui::mouseDownLeftDisplay(int x, int y){ + if(!selectingPos && !selectingMeetingPoint){ + int posDisplay= computePosDisplay(x, y); + if(posDisplay!= invalidPos){ + if(selection.isComandable()){ + if(selectingBuilding){ + mouseDownDisplayUnitBuild(posDisplay); + } + else{ + mouseDownDisplayUnitSkills(posDisplay); + } + } + else{ + resetState(); + } + } + computeDisplay(); + } +} + +void Gui::mouseMoveDisplay(int x, int y){ + computeInfoString(computePosDisplay(x, y)); +} + +void Gui::mouseDownLeftGraphics(int x, int y){ + if(selectingPos){ + //give standard orders + giveTwoClickOrders(x, y); + resetState(); + } + //set meeting point + else if(selectingMeetingPoint){ + if(selection.isComandable()){ + Vec2i targetPos; + if(Renderer::getInstance().computePosition(Vec2i(x, y), targetPos)){ + commander->trySetMeetingPoint(selection.getFrontUnit(), targetPos); + } + } + resetState(); + } + else{ + selectionQuad.setPosDown(Vec2i(x, y)); + computeSelected(false); + } + computeDisplay(); +} + +void Gui::mouseDownRightGraphics(int x, int y){ + + if(selectingPos || selectingMeetingPoint){ + resetState(); + } + else if(selection.isComandable()){ + giveDefaultOrders(x, y); + } + computeDisplay(); +} + +void Gui::mouseUpLeftGraphics(int x, int y){ + if(!selectingPos && !selectingMeetingPoint){ + if(selectionQuad.isEnabled()){ + selectionQuad.setPosUp(Vec2i(x, y)); + if(selection.isComandable() && random.randRange(0, 1)==0){ + SoundRenderer::getInstance().playFx( + selection.getFrontUnit()->getType()->getSelectionSound(), + selection.getFrontUnit()->getCurrVector(), + gameCamera->getPos()); + } + selectionQuad.disable(); + } + } +} + +void Gui::mouseMoveGraphics(int x, int y){ + + //compute selection + if(selectionQuad.isEnabled()){ + selectionQuad.setPosUp(Vec2i(x, y)); + if(computeSelection){ + computeSelection= false; + computeSelected(false); + } + } + + //compute position for building + if(isPlacingBuilding()){ + validPosObjWorld= Renderer::getInstance().computePosition(Vec2i(x,y), posObjWorld); + } + + display.setInfoText(""); +} + +void Gui::mouseDoubleClickLeftGraphics(int x, int y){ + if(!selectingPos && !selectingMeetingPoint){ + selectionQuad.setPosDown(Vec2i(x, y)); + computeSelected(true); + computeDisplay(); + } +} + +void Gui::groupKey(int groupIndex){ + if(isKeyDown(vkControl)){ + selection.assignGroup(groupIndex); + } + else{ + selection.recallGroup(groupIndex); + } +} + +void Gui::hotKey(char key){ + if(key==' '){ + centerCameraOnSelection(); + } + else if(key=='I'){ + selectInterestingUnit(iutIdleHarvester); + } + else if(key=='B'){ + selectInterestingUnit(iutBuiltBuilding); + } + else if(key=='R'){ + selectInterestingUnit(iutProducer); + } + else if(key=='D'){ + selectInterestingUnit(iutDamaged); + } + else if(key=='T'){ + selectInterestingUnit(iutStore); + } + else if(key=='A'){ + clickCommonCommand(ccAttack); + } + else if(key=='S'){ + clickCommonCommand(ccStop); + } + else if(key=='M'){ + clickCommonCommand(ccMove); + } +} + +void Gui::onSelectionChanged(){ + resetState(); + computeDisplay(); +} + +// ================= PRIVATE ================= + +void Gui::giveOneClickOrders(){ + CommandResult result; + if(selection.isUniform()){ + result= commander->tryGiveCommand(&selection, activeCommandType); + } + else{ + result= commander->tryGiveCommand(&selection, activeCommandClass); + } + addOrdersResultToConsole(activeCommandClass, result); + activeCommandType= NULL; + activeCommandClass= ccStop; +} + +void Gui::giveDefaultOrders(int x, int y){ + + //compute target + const Unit *targetUnit= NULL; + Vec2i targetPos; + if(!computeTarget(Vec2i(x, y), targetPos, targetUnit)){ + console->addStdMessage("InvalidPosition"); + return; + } + + //give order + CommandResult result= commander->tryGiveCommand(&selection, targetPos, targetUnit); + + //graphical result + addOrdersResultToConsole(activeCommandClass, result); + if(result == crSuccess || result == crSomeFailed){ + mouse3d.enable(); + + if(random.randRange(0, 1)==0){ + SoundRenderer::getInstance().playFx( + selection.getFrontUnit()->getType()->getCommandSound(), + selection.getFrontUnit()->getCurrVector(), + gameCamera->getPos()); + } + } + + //reset + resetState(); +} + +void Gui::giveTwoClickOrders(int x, int y){ + + CommandResult result; + + //compute target + const Unit *targetUnit= NULL; + Vec2i targetPos; + if(!computeTarget(Vec2i(x, y), targetPos, targetUnit)){ + console->addStdMessage("InvalidPosition"); + return; + } + + //give orders to the units of this faction + if(!selectingBuilding){ + if(selection.isUniform()){ + result= commander->tryGiveCommand(&selection, activeCommandType, targetPos, targetUnit); + } + else{ + result= commander->tryGiveCommand(&selection, activeCommandClass, targetPos, targetUnit); + } + } + else{ + //selecting building + result= commander->tryGiveCommand( selection.getFrontUnit(), activeCommandType, posObjWorld, choosenBuildingType ); + } + + //graphical result + addOrdersResultToConsole(activeCommandClass, result); + if(result == crSuccess || result == crSomeFailed){ + mouse3d.enable(); + + if(random.randRange(0, 1)==0){ + SoundRenderer::getInstance().playFx( + selection.getFrontUnit()->getType()->getCommandSound(), + selection.getFrontUnit()->getCurrVector(), + gameCamera->getPos()); + } + } +} + +void Gui::centerCameraOnSelection(){ + if(!selection.isEmpty()){ + Vec3f refPos= selection.getRefPos(); + gameCamera->centerXZ(refPos.x, refPos.z); + } +} + +void Gui::selectInterestingUnit(InterestingUnitType iut){ + const Faction* thisFaction= world->getThisFaction(); + const Unit* previousUnit= NULL; + bool previousFound= true; + + //start at the next harvester + if(selection.getCount()==1){ + const Unit* refUnit= selection.getFrontUnit(); + + if(refUnit->isInteresting(iut)){ + previousUnit= refUnit; + previousFound= false; + } + } + + //clear selection + selection.clear(); + + //search + for(int i= 0; igetUnitCount(); ++i){ + Unit* unit= thisFaction->getUnit(i); + + if(previousFound){ + if(unit->isInteresting(iut)){ + selection.select(unit); + break; + } + } + else{ + if(unit==previousUnit){ + previousFound= true; + } + } + } + + //search again if we have a previous + if(selection.isEmpty() && previousUnit!=NULL && previousFound==true){ + for(int i= 0; igetUnitCount(); ++i){ + Unit* unit= thisFaction->getUnit(i); + + if(unit->isInteresting(iut)){ + selection.select(unit); + break; + } + } + } +} + +void Gui::clickCommonCommand(CommandClass commandClass){ + for(int i= 0; igetClass()==commandClass) || display.getCommandClass(i)==commandClass){ + mouseDownDisplayUnitSkills(i); + break; + } + } +} + +void Gui::mouseDownDisplayUnitSkills(int posDisplay){ + if(!selection.isEmpty()){ + if(posDisplay != cancelPos){ + if(posDisplay!=meetingPointPos){ + const Unit *unit= selection.getFrontUnit(); + + //uniform selection + if(selection.isUniform()){ + if(unit->getFaction()->reqsOk(display.getCommandType(posDisplay))){ + activeCommandType= display.getCommandType(posDisplay); + activeCommandClass= activeCommandType->getClass(); + } + else{ + posDisplay= invalidPos; + activeCommandType= NULL; + activeCommandClass= ccStop; + return; + } + } + + //non uniform selection + else{ + activeCommandType= NULL; + activeCommandClass= display.getCommandClass(posDisplay); + } + + //give orders depending on command type + if(!selection.isEmpty()){ + const CommandType *ct= selection.getUnit(0)->getType()->getFirstCtOfClass(activeCommandClass); + if(activeCommandType!=NULL && activeCommandType->getClass()==ccBuild){ + assert(selection.isUniform()); + selectingBuilding= true; + } + else if(ct->getClicks()==cOne){ + invalidatePosObjWorld(); + giveOneClickOrders(); + } + else{ + selectingPos= true; + activePos= posDisplay; + } + } + } + else{ + activePos= posDisplay; + selectingMeetingPoint= true; + } + } + else{ + commander->tryCancelCommand(&selection); + } + } +} + +void Gui::mouseDownDisplayUnitBuild(int posDisplay){ + int factionIndex= world->getThisFactionIndex(); + + if(posDisplay==cancelPos){ + resetState(); + } + else{ + if(activeCommandType!=NULL && activeCommandType->getClass()==ccBuild){ + const BuildCommandType *bct= static_cast(activeCommandType); + const UnitType *ut= bct->getBuilding(posDisplay); + if(world->getFaction(factionIndex)->reqsOk(ut)){ + choosenBuildingType= ut; + assert(choosenBuildingType!=NULL); + selectingPos= true;; + activePos= posDisplay; + } + } + } +} + +void Gui::computeInfoString(int posDisplay){ + + Lang &lang= Lang::getInstance(); + + display.setInfoText(""); + if(posDisplay!=invalidPos && selection.isComandable()){ + if(!selectingBuilding){ + if(posDisplay==cancelPos){ + display.setInfoText(lang.get("Cancel")); + } + else if(posDisplay==meetingPointPos){ + display.setInfoText(lang.get("MeetingPoint")); + } + else{ + //uniform selection + if(selection.isUniform()){ + const Unit *unit= selection.getFrontUnit(); + const CommandType *ct= display.getCommandType(posDisplay); + if(ct!=NULL){ + if(unit->getFaction()->reqsOk(ct)){ + display.setInfoText(ct->getDesc(unit->getTotalUpgrade())); + } + else{ + if(ct->getClass()==ccUpgrade){ + const UpgradeCommandType *uct= static_cast(ct); + if(unit->getFaction()->getUpgradeManager()->isUpgrading(uct->getProducedUpgrade())){ + display.setInfoText(lang.get("Upgrading")); + } + else if(unit->getFaction()->getUpgradeManager()->isUpgraded(uct->getProducedUpgrade())){ + display.setInfoText(lang.get("AlreadyUpgraded")); + } + else{ + display.setInfoText(ct->getReqDesc()); + } + } + else{ + display.setInfoText(ct->getReqDesc()); + } + } + } + } + + //non uniform selection + else{ + const UnitType *ut= selection.getFrontUnit()->getType(); + CommandClass cc= display.getCommandClass(posDisplay); + if(cc!=ccNull){ + display.setInfoText(lang.get("CommonCommand") + ": " + ut->getFirstCtOfClass(cc)->toString()); + } + } + } + } + else{ + if(posDisplay==cancelPos){ + display.setInfoText(lang.get("Return")); + } + else{ + if(activeCommandType!=NULL && activeCommandType->getClass()==ccBuild){ + const BuildCommandType *bct= static_cast(activeCommandType); + display.setInfoText(bct->getBuilding(posDisplay)->getReqDesc()); + } + } + } + } +} + +void Gui::computeDisplay(){ + + //init + display.clear(); + + // ================ PART 1 ================ + + //title, text and progress bar + if(selection.getCount()==1){ + display.setTitle(selection.getFrontUnit()->getFullName()); + display.setText(selection.getFrontUnit()->getDesc()); + display.setProgressBar(selection.getFrontUnit()->getProductionPercent()); + } + + //portraits + for(int i=0; igetType()->getImage()); + } + + // ================ PART 2 ================ + + if(selectingPos || selectingMeetingPoint){ + display.setDownSelectedPos(activePos); + } + + if(selection.isComandable()){ + if(!selectingBuilding){ + + //cancel button + const Unit *u= selection.getFrontUnit(); + const UnitType *ut= u->getType(); + if(selection.isCancelable()){ + display.setDownImage(cancelPos, ut->getCancelImage()); + display.setDownLighted(cancelPos, true); + } + + //meeting point + if(selection.isMeetable()){ + display.setDownImage(meetingPointPos, ut->getMeetingPointImage()); + display.setDownLighted(meetingPointPos, true); + } + + if(selection.isUniform()){ + //uniform selection + if(u->isBuilt()){ + int morphPos= 8; + for(int i=0; igetCommandTypeCount(); ++i){ + int displayPos= i; + const CommandType *ct= ut->getCommandType(i); + if(ct->getClass()==ccMorph){ + displayPos= morphPos++; + } + display.setDownImage(displayPos, ct->getImage()); + display.setCommandType(displayPos, ct); + display.setDownLighted(displayPos, u->getFaction()->reqsOk(ct)); + } + } + } + + else{ + //non uniform selection + int lastCommand= 0; + for(int i=0; i(i); + if(isSharedCommandClass(cc) && cc!=ccBuild){ + display.setDownLighted(lastCommand, true); + display.setDownImage(lastCommand, ut->getFirstCtOfClass(cc)->getImage()); + display.setCommandClass(lastCommand, cc); + lastCommand++; + } + } + } + } + else{ + + //selecting building + const Unit *unit= selection.getFrontUnit(); + if(activeCommandType!=NULL && activeCommandType->getClass()==ccBuild){ + const BuildCommandType* bct= static_cast(activeCommandType); + for(int i=0; igetBuildingCount(); ++i){ + display.setDownImage(i, bct->getBuilding(i)->getImage()); + display.setDownLighted(i, unit->getFaction()->reqsOk(bct->getBuilding(i))); + } + display.setDownImage(cancelPos, selection.getFrontUnit()->getType()->getCancelImage()); + display.setDownLighted(cancelPos, true); + } + } + } +} + +int Gui::computePosDisplay(int x, int y){ + int posDisplay= display.computeDownIndex(x, y); + + if(posDisplay<0 || posDisplay>=Display::downCellCount){ + posDisplay= invalidPos; + } + else if(selection.isComandable()){ + if(posDisplay!=cancelPos){ + if(posDisplay!=meetingPointPos){ + if(!selectingBuilding){ + //standard selection + if(display.getCommandClass(posDisplay)==ccNull && display.getCommandType(posDisplay)==NULL){ + posDisplay= invalidPos; + } + } + else{ + //building selection + if(activeCommandType!=NULL && activeCommandType->getClass()==ccBuild){ + const BuildCommandType *bct= static_cast(activeCommandType); + if(posDisplay>=bct->getBuildingCount()){ + posDisplay= invalidPos; + } + } + } + } + else{ + //check meeting point + if(!selection.isMeetable()){ + posDisplay= invalidPos; + } + } + } + else{ + //check cancel button + if(!selection.isCancelable()){ + posDisplay= invalidPos; + } + } + } + else{ + posDisplay= invalidPos; + } + + return posDisplay; +} + +void Gui::addOrdersResultToConsole(CommandClass cc, CommandResult result){ + + switch(result){ + case crSuccess: + break; + case crFailReqs: + switch(cc){ + case ccBuild: + console->addStdMessage("BuildingNoReqs"); + break; + case ccProduce: + console->addStdMessage("UnitNoReqs"); + break; + case ccUpgrade: + console->addStdMessage("UpgradeNoReqs"); + break; + default: + break; + } + break; + case crFailRes: + switch(cc){ + case ccBuild: + console->addStdMessage("BuildingNoRes"); + break; + case ccProduce: + console->addStdMessage("UnitNoRes"); + break; + case ccUpgrade: + console->addStdMessage("UpgradeNoRes"); + break; + default: + break; + } + break; + + case crFailUndefined: + console->addStdMessage("InvalidOrder"); + break; + + case crSomeFailed: + console->addStdMessage("SomeOrdersFailed"); + break; + } +} + +bool Gui::isSharedCommandClass(CommandClass commandClass){ + for(int i=0; igetType()->getFirstCtOfClass(commandClass); + if(ct==NULL || !unit->getFaction()->reqsOk(ct)) + return false; + } + return true; +} + +void Gui::computeSelected(bool doubleClick){ + Selection::UnitContainer units; + Renderer::getInstance().computeSelected(units, selectionQuad.getPosDown(), selectionQuad.getPosUp()); + selectingBuilding= false; + activeCommandType= NULL; + + //select all units of the same type if double click + if(doubleClick && units.size()==1){ + const Unit *refUnit= units.front(); + int factionIndex= refUnit->getFactionIndex(); + for(int i=0; igetFaction(factionIndex)->getUnitCount(); ++i){ + Unit *unit= world->getFaction(factionIndex)->getUnit(i); + if(unit->getPos().dist(refUnit->getPos())getType()==refUnit->getType()) + { + units.push_back(unit); + } + } + } + + bool shiftDown= isKeyDown(vkShift); + bool controlDown= isKeyDown(vkControl); + + if(!shiftDown && !controlDown){ + selection.clear(); + } + + if(!controlDown){ + selection.select(units); + } + else{ + selection.unSelect(units); + } +} + +bool Gui::computeTarget(const Vec2i &screenPos, Vec2i &targetPos, const Unit *&targetUnit){ + Selection::UnitContainer uc; + Renderer &renderer= Renderer::getInstance(); + renderer.computeSelected(uc, screenPos, screenPos); + validPosObjWorld= false; + + if(!uc.empty()){ + targetUnit= uc.front(); + targetPos= targetUnit->getPos(); + return true; + } + else{ + targetUnit= NULL; + if(renderer.computePosition(screenPos, targetPos)){ + validPosObjWorld= true; + posObjWorld= targetPos; + return true; + } + else{ + return false; + } + } +} + +}}//end namespace diff --git a/source/glest_game/gui/gui.h b/source/glest_game/gui/gui.h new file mode 100644 index 00000000..a20cb727 --- /dev/null +++ b/source/glest_game/gui/gui.h @@ -0,0 +1,201 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_GUI_H_ +#define _GLEST_GAME_GUI_H_ + +#include "resource.h" +#include "command_type.h" +#include "display.h" +#include "commander.h" +#include "console.h" +#include "selection.h" +#include "random.h" + +using Shared::Util::Random; + +namespace Glest{ namespace Game{ + +class Unit; +class World; +class CommandType; +class GameCamera; +class Game; + +enum DisplayState{ + dsEmpty, + dsUnitSkills, + dsUnitBuild, + dsEnemy +}; + +// ===================================================== +// class Mouse3d +// ===================================================== + +class Mouse3d{ +public: + static const float fadeSpeed; + +private: + bool enabled; + int rot; + float fade; + +public: + Mouse3d(); + + void enable(); + void update(); + + bool isEnabled() const {return enabled;} + float getFade() const {return fade;} + int getRot() const {return rot;} +}; + +// ===================================================== +// class SelectionQuad +// ===================================================== + +class SelectionQuad{ +private: + Vec2i posDown; + Vec2i posUp; + bool enabled; + +public: + SelectionQuad(); + + bool isEnabled() const {return enabled;} + Vec2i getPosDown() const {return posDown;} + Vec2i getPosUp() const {return posUp;} + + void setPosDown(const Vec2i &posDown); + void setPosUp(const Vec2i &posUp); + void disable(); +}; + +// ===================================================== +// class Gui +// +/// In game GUI +// ===================================================== + +class Gui{ +public: + static const int maxSelBuff= 128*5; + static const int upgradeDisplayIndex= 8; + static const int cancelPos= 15; + static const int meetingPointPos= 14; + static const int imageCount= 16; + static const int invalidPos= -1; + static const int doubleClickSelectionRadius= 20; + +private: + //External objects + Random random; + const Commander *commander; + const World *world; + GameCamera *gameCamera; + Console *console; + + //Positions + Vec2i posObjWorld; //world coords + bool validPosObjWorld; + bool computeSelection; + + //display + const UnitType *choosenBuildingType; + const CommandType *activeCommandType; + CommandClass activeCommandClass; + int activePos; + + //composite + Display display; + Mouse3d mouse3d; + Selection selection; + SelectionQuad selectionQuad; + + //states + bool selectingBuilding; + bool selectingPos; + bool selectingMeetingPoint; + +public: + Gui(); + void init(Game *game); + void end(); + + //get + Vec2i getPosObjWorld() const {return posObjWorld;} + const UnitType *getBuilding() const; + + const Mouse3d *getMouse3d() const {return &mouse3d;} + const Display *getDisplay() const {return &display;} + const Selection *getSelection() const {return &selection;} + const SelectionQuad *getSelectionQuad() const {return &selectionQuad;} + bool isSelected(const Unit *unit) const {return selection.hasUnit(unit);} + + bool isValidPosObjWorld() const {return validPosObjWorld;} + bool isSelecting() const {return selectionQuad.isEnabled();} + bool isSelectingPos() const {return selectingPos;} + bool isSelectingBuilding() const {return selectingBuilding;} + bool isPlacingBuilding() const; + + //set + void invalidatePosObjWorld(); + void setComputeSelectionFlag(); + + //events + void update(); + void tick(); + bool mouseValid(int x, int y); + void mouseDownLeftDisplay(int x, int y); + void mouseMoveDisplay(int x, int y); + void mouseDownLeftGraphics(int x, int y); + void mouseDownRightGraphics(int x, int y); + void mouseUpLeftGraphics(int x, int y); + void mouseMoveGraphics(int x, int y); + void mouseDoubleClickLeftGraphics(int x, int y); + void groupKey(int groupIndex); + void hotKey(char key); + + //misc + void onSelectionChanged(); + +private: + + //orders + void giveDefaultOrders(int x, int y); + void giveOneClickOrders(); + void giveTwoClickOrders(int x, int y); + + //hotkeys + void centerCameraOnSelection(); + void selectInterestingUnit(InterestingUnitType iut); + void clickCommonCommand(CommandClass commandClass); + + //misc + int computePosDisplay(int x, int y); + void computeDisplay(); + void resetState(); + void mouseDownDisplayUnitSkills(int posDisplay); + void mouseDownDisplayUnitBuild(int posDisplay); + void computeInfoString(int posDisplay); + void addOrdersResultToConsole(CommandClass cc, CommandResult rr); + bool isSharedCommandClass(CommandClass commandClass); + void computeSelected(bool doubleCkick); + bool computeTarget(const Vec2i &screenPos, Vec2i &targetPos, const Unit *&targetUnit); +}; + +}} //end namespace + +#endif diff --git a/source/glest_game/gui/selection.cpp b/source/glest_game/gui/selection.cpp new file mode 100644 index 00000000..f111885f --- /dev/null +++ b/source/glest_game/gui/selection.cpp @@ -0,0 +1,204 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "selection.h" + +#include + +#include "unit_type.h" +#include "gui.h" +#include "leak_dumper.h" + +using namespace std; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Selection +// ===================================================== + +void Selection::init(Gui *gui, int factionIndex){ + this->factionIndex= factionIndex; + this->gui= gui; +} + +Selection::~Selection(){ + clear(); +} + +void Selection::select(Unit *unit){ + + //check size + if(selectedUnits.size()>=maxUnits){ + return; + } + + //check if already selected + for(int i=0; iisDead()){ + return; + } + + //check if multisel + if(!unit->getType()->getMultiSelect() && !isEmpty()){ + return; + } + + //check if enemy + if(unit->getFactionIndex()!=factionIndex && !isEmpty()){ + return; + } + + //check existing enemy + if(selectedUnits.size()==1 && selectedUnits.front()->getFactionIndex()!=factionIndex){ + clear(); + } + + //check existing multisel + if(selectedUnits.size()==1 && !selectedUnits.front()->getType()->getMultiSelect()){ + clear(); + } + + unit->addObserver(this); + selectedUnits.push_back(unit); + gui->onSelectionChanged(); +} + +void Selection::select(const UnitContainer &units){ + + //add units to gui + for(UnitIterator it= units.begin(); it!=units.end(); ++it){ + select(*it); + } +} + +void Selection::unSelect(const UnitContainer &units){ + + //add units to gui + for(UnitIterator it= units.begin(); it!=units.end(); ++it){ + for(int i=0; ionSelectionChanged(); +} + +void Selection::clear(){ + //clear list + selectedUnits.clear(); +} + +bool Selection::isUniform() const{ + if(selectedUnits.empty()){ + return true; + } + + const UnitType *ut= selectedUnits.front()->getType(); + + for(int i=0; igetType()!=ut){ + return false; + } + } + return true; +} + +bool Selection::isEnemy() const{ + return selectedUnits.size()==1 && selectedUnits.front()->getFactionIndex()!=factionIndex; +} + +bool Selection::isComandable() const{ + return + !isEmpty() && + !isEnemy() && + !(selectedUnits.size()==1 && !selectedUnits.front()->isOperative()); +} + +bool Selection::isCancelable() const{ + return + selectedUnits.size()>1 || + (selectedUnits.size()==1 && selectedUnits[0]->anyCommand()); +} + +bool Selection::isMeetable() const{ + return + isUniform() && + isComandable() && + selectedUnits.front()->getType()->getMeetingPoint(); +} + +Vec3f Selection::getRefPos() const{ + return getFrontUnit()->getCurrVector(); +} + +bool Selection::hasUnit(const Unit* unit) const{ + return find(selectedUnits.begin(), selectedUnits.end(), unit)!=selectedUnits.end(); +} + +void Selection::assignGroup(int groupIndex){ + //clear group + groups[groupIndex].clear(); + + //assign new group + for(int i=0; ionSelectionChanged(); + + } +} + +}}//end namespace diff --git a/source/glest_game/gui/selection.h b/source/glest_game/gui/selection.h new file mode 100644 index 00000000..d6dae228 --- /dev/null +++ b/source/glest_game/gui/selection.h @@ -0,0 +1,76 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_SELECTION_ +#define _GLEST_GAME_SELECTION_ + +#include "unit.h" +#include + +using std::vector; + +namespace Glest{ namespace Game{ + +class Gui; + +// ===================================================== +// class Selection +// +/// List of selected units and groups +// ===================================================== + +class Selection: public UnitObserver{ +public: + typedef vector UnitContainer; + typedef UnitContainer::const_iterator UnitIterator; + +public: + static const int maxGroups= 10; + static const int maxUnits= 16; + +private: + int factionIndex; + UnitContainer selectedUnits; + UnitContainer groups[maxGroups]; + Gui *gui; + +public: + void init(Gui *gui, int factionIndex); + virtual ~Selection(); + + void select(Unit *unit); + void select(const UnitContainer &units); + void unSelect(int unitIndex); + void unSelect(const UnitContainer &units); + void clear(); + + bool isEmpty() const {return selectedUnits.empty();} + bool isUniform() const; + bool isEnemy() const; + bool isComandable() const; + bool isCancelable() const; + bool isMeetable() const; + int getCount() const {return selectedUnits.size();} + const Unit *getUnit(int i) const {return selectedUnits[i];} + const Unit *getFrontUnit() const {return selectedUnits.front();} + Vec3f getRefPos() const; + bool hasUnit(const Unit* unit) const; + + void assignGroup(int groupIndex); + void recallGroup(int groupIndex); + + + virtual void unitEvent(UnitObserver::Event event, const Unit *unit); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/main/battle_end.cpp b/source/glest_game/main/battle_end.cpp new file mode 100644 index 00000000..4cde18a2 --- /dev/null +++ b/source/glest_game/main/battle_end.cpp @@ -0,0 +1,150 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "battle_end.h" + +#include "main_menu.h" +#include "program.h" +#include "core_data.h" +#include "lang.h" +#include "util.h" +#include "renderer.h" +#include "main_menu.h" +#include "sound_renderer.h" +#include "components.h" +#include "metrics.h" +#include "stats.h" +#include "auto_test.h" + +#include "leak_dumper.h" + +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class BattleEnd +// ===================================================== + +BattleEnd::BattleEnd(Program *program, const Stats *stats): +ProgramState(program){ + this->stats= *stats; +} + +BattleEnd::~BattleEnd(){ + SoundRenderer::getInstance().playMusic(CoreData::getInstance().getMenuMusic()); +} + +void BattleEnd::update(){ + if(Config::getInstance().getBool("AutoTest")){ + AutoTest::getInstance().updateBattleEnd(program); + } +} + +void BattleEnd::render(){ + Renderer &renderer= Renderer::getInstance(); + TextRenderer2D *textRenderer= renderer.getTextRenderer(); + Lang &lang= Lang::getInstance(); + + renderer.clearBuffers(); + renderer.reset2d(); + renderer.renderBackground(CoreData::getInstance().getBackgroundTexture()); + + textRenderer->begin(CoreData::getInstance().getMenuFontBig()); + + int lm= 80; + int bm= 100; + + for(int i=0; irender((lang.get("Player")+" "+intToStr(i+1)).c_str(), textX, bm+400); + textRenderer->render(stats.getVictory(i)? lang.get("Victory").c_str(): lang.get("Defeat").c_str(), textX, bm+360); + textRenderer->render(controlString, textX, bm+320); + textRenderer->render(stats.getFactionTypeName(i), textX, bm+280); + textRenderer->render(intToStr(team).c_str(), textX, bm+240); + textRenderer->render(intToStr(kills).c_str(), textX, bm+200); + textRenderer->render(intToStr(deaths).c_str(), textX, bm+160); + textRenderer->render(intToStr(unitsProduced).c_str(), textX, bm+120); + textRenderer->render(intToStr(resourcesHarvested).c_str(), textX, bm+80); + textRenderer->render(intToStr(score).c_str(), textX, bm+20); + } + + textRenderer->render(lang.get("Result"), lm+50, bm+360); + textRenderer->render(lang.get("Control"), lm+50, bm+320); + textRenderer->render(lang.get("Faction"), lm+50, bm+280); + textRenderer->render(lang.get("Team"), lm+50, bm+240); + textRenderer->render(lang.get("Kills"), lm+50, bm+200); + textRenderer->render(lang.get("Deaths"), lm+50, bm+160); + textRenderer->render(lang.get("UnitsProduced"), lm+50, bm+120); + textRenderer->render(lang.get("ResourcesHarvested"), lm+50, bm+80); + textRenderer->render(lang.get("Score"), lm+50, bm+20); + + textRenderer->end(); + + textRenderer->begin(CoreData::getInstance().getMenuFontVeryBig()); + + string header = stats.getDescription() + " - "; + + if(stats.getVictory(stats.getThisFactionIndex())){ + header += lang.get("Victory"); + } + else{ + header += lang.get("Defeat"); + } + + textRenderer->render(header, lm+250, bm+550); + + textRenderer->end(); + renderer.swapBuffers(); +} + +void BattleEnd::keyDown(char key){ + program->setState(new MainMenu(program)); +} + +void BattleEnd::mouseDownLeft(int x, int y){ + program->setState(new MainMenu(program)); +} + +}}//end namespace diff --git a/source/glest_game/main/battle_end.h b/source/glest_game/main/battle_end.h new file mode 100644 index 00000000..1aaacd25 --- /dev/null +++ b/source/glest_game/main/battle_end.h @@ -0,0 +1,41 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_BATTLEEND_H_ +#define _GLEST_GAME_BATTLEEND_H_ + +#include "program.h" +#include "stats.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class BattleEnd +// +/// ProgramState representing the end of the game +// ===================================================== + +class BattleEnd: public ProgramState{ +private: + Stats stats; + +public: + BattleEnd(Program *program, const Stats *stats); + ~BattleEnd(); + virtual void update(); + virtual void render(); + virtual void keyDown(char key); + virtual void mouseDownLeft(int x, int y); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/main/intro.cpp b/source/glest_game/main/intro.cpp new file mode 100644 index 00000000..7a3fc243 --- /dev/null +++ b/source/glest_game/main/intro.cpp @@ -0,0 +1,133 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== +#include "intro.h" + +#include "main_menu.h" +#include "util.h" +#include "game_util.h" +#include "config.h" +#include "program.h" +#include "renderer.h" +#include "sound_renderer.h" +#include "core_data.h" +#include "metrics.h" +#include "auto_test.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Text +// ===================================================== + +Text::Text(const string &text, const Vec2i &pos, int time, const Font2D *font){ + this->text= text; + this->pos= pos; + this->time= time; + this->texture= NULL; + this->font= font; +} + +Text::Text(const Texture2D *texture, const Vec2i &pos, const Vec2i &size, int time){ + this->pos= pos; + this->size= size; + this->time= time; + this->texture= texture; + this->font= NULL; +} + +// ===================================================== +// class Intro +// ===================================================== + +const int Intro::introTime= 24000; +const int Intro::appearTime= 2500; +const int Intro::showTime= 2500; +const int Intro::disapearTime= 2500; + +Intro::Intro(Program *program): + ProgramState(program) +{ + CoreData &coreData= CoreData::getInstance(); + const Metrics &metrics= Metrics::getInstance(); + int w= metrics.getVirtualW(); + int h= metrics.getVirtualH(); + timer=0; + + texts.push_back(Text(coreData.getLogoTexture(), Vec2i(w/2-128, h/2-64), Vec2i(256, 128), 4000)); + texts.push_back(Text(glestVersionString, Vec2i(w/2+64, h/2-32), 4000, coreData.getMenuFontNormal())); + texts.push_back(Text("www.glest.org", Vec2i(w/2, h/2), 12000, coreData.getMenuFontVeryBig())); + SoundRenderer &soundRenderer= SoundRenderer::getInstance(); + soundRenderer.playMusic(CoreData::getInstance().getIntroMusic()); +} + +void Intro::update(){ + timer++; + if(timer>introTime*GameConstants::updateFps/1000){ + program->setState(new MainMenu(program)); + } + + if(Config::getInstance().getBool("AutoTest")){ + AutoTest::getInstance().updateIntro(program); + } +} + +void Intro::render(){ + Renderer &renderer= Renderer::getInstance(); + int difTime; + + renderer.reset2d(); + renderer.clearBuffers(); + for(int i=0; igetTime(); + + if(difTime>0 && difTime0 && difTime(difTime)/appearTime; + } + else if(difTime>0 && difTime(difTime-appearTime-showTime)/disapearTime; + } + if(!text->getText().empty()){ + renderer.renderText( + text->getText(), text->getFont(), alpha, + text->getPos().x, text->getPos().y, true); + } + if(text->getTexture()!=NULL){ + renderer.renderTextureQuad( + text->getPos().x, text->getPos().y, + text->getSize().x, text->getSize().y, + text->getTexture(), alpha); + } + } + } + renderer.swapBuffers(); +} + +void Intro::keyDown(char key){ + mouseUpLeft(0, 0); +} + +void Intro::mouseUpLeft(int x, int y){ + SoundRenderer &soundRenderer= SoundRenderer::getInstance(); + soundRenderer.stopMusic(CoreData::getInstance().getIntroMusic()); + soundRenderer.playMusic(CoreData::getInstance().getMenuMusic()); + program->setState(new MainMenu(program)); +} + +}}//end namespace diff --git a/source/glest_game/main/intro.h b/source/glest_game/main/intro.h new file mode 100644 index 00000000..dc4a6ecb --- /dev/null +++ b/source/glest_game/main/intro.h @@ -0,0 +1,84 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_INTRO_H_ +#define _GLEST_GAME_INTRO_H_ + +#include + +#include "program.h" +#include "font.h" +#include "vec.h" +#include "texture.h" + +using std::vector; + +using Shared::Graphics::Vec2i; +using Shared::Graphics::Vec2f; +using Shared::Graphics::Vec3f; +using Shared::Graphics::Font2D; +using Shared::Graphics::Texture2D; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Text +// ===================================================== + +class Text{ +private: + string text; + Vec2i pos; + Vec2i size; + int time; + const Font2D *font; + const Texture2D *texture; + +public: + Text(const string &text, const Vec2i &pos, int time, const Font2D *font); + Text(const Texture2D *texture, const Vec2i &pos, const Vec2i &size, int time); + + const string &getText() const {return text;} + const Font2D *getFont() const {return font;} + const Vec2i &getPos() const {return pos;} + const Vec2i &getSize() const {return size;} + int getTime() const {return time;} + const Texture2D *getTexture() const {return texture;} +}; + +// ===================================================== +// class Intro +// +/// ProgramState representing the intro +// ===================================================== + +class Intro: public ProgramState{ +private: + static const int introTime; + static const int appearTime; + static const int showTime; + static const int disapearTime; + +private: + vector texts; + int timer; + +public: + Intro(Program *program); + virtual void update(); + virtual void render(); + virtual void keyDown(char key); + virtual void mouseUpLeft(int x, int y); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/main/main.cpp b/source/glest_game/main/main.cpp new file mode 100644 index 00000000..8da0d1a2 --- /dev/null +++ b/source/glest_game/main/main.cpp @@ -0,0 +1,159 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "main.h" + +#include +#include + +#include "game.h" +#include "main_menu.h" +#include "program.h" +#include "config.h" +#include "metrics.h" +#include "game_util.h" +#include "platform_util.h" +#include "platform_main.h" +#include "leak_dumper.h" + +using namespace std; +using namespace Shared::Platform; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class ExceptionHandler +// ===================================================== + +class ExceptionHandler: public PlatformExceptionHandler{ +public: + virtual void handle(){ + message("An error ocurred and Glest will close.\nPlease report this bug to "+mailString+", attaching the generated "+getCrashDumpFileName()+" file."); + } +}; + +// ===================================================== +// class MainWindow +// ===================================================== + +MainWindow::MainWindow(Program *program){ + this->program= program; +} + +MainWindow::~MainWindow(){ + delete program; +} + +void MainWindow::eventMouseDown(int x, int y, MouseButton mouseButton){ + switch(mouseButton){ + case mbLeft: + program->mouseDownLeft(x, getH() - y); + break; + case mbRight: + program->mouseDownRight(x, getH() - y); + break; + default: + break; + } +} + +void MainWindow::eventMouseUp(int x, int y, MouseButton mouseButton){ + if(mouseButton==mbLeft){ + program->mouseUpLeft(x, getH() - y); + } +} + +void MainWindow::eventMouseDoubleClick(int x, int y, MouseButton mouseButton){ + if(mouseButton == mbLeft){ + program->mouseDoubleClickLeft(x, getH() - y); + } +} + +void MainWindow::eventMouseMove(int x, int y, const MouseState *ms){ + program->mouseMove(x, getH() - y, ms); +} + +void MainWindow::eventKeyDown(char key){ + program->keyDown(key); +} + +void MainWindow::eventKeyUp(char key){ + program->keyUp(key); +} + +void MainWindow::eventKeyPress(char c){ + program->keyPress(c); +} + +void MainWindow::eventActivate(bool active){ + if(!active){ + //minimize(); + } +} + +void MainWindow::eventResize(SizeState sizeState){ + program->resize(sizeState); +} + +void MainWindow::eventClose(){ + delete program; + program= NULL; +} + +// ===================================================== +// Main +// ===================================================== + +int glestMain(int argc, char** argv){ + + MainWindow *mainWindow= NULL; + Program *program= NULL; + ExceptionHandler exceptionHandler; + exceptionHandler.install( getCrashDumpFileName() ); + + try{ + Config &config = Config::getInstance(); + + showCursor(config.getBool("Windowed")); + + program= new Program(); + mainWindow= new MainWindow(program); + + //parse command line + if(argc==2 && string(argv[1])=="-server"){ + program->initServer(mainWindow); + } + else if(argc==3 && string(argv[1])=="-client"){ + program->initClient(mainWindow, Ip(argv[2])); + } + else{ + program->initNormal(mainWindow); + } + + //main loop + while(Window::handleEvent()){ + program->loop(); + } + } + catch(const exception &e){ + restoreVideoMode(); + exceptionMessage(e); + } + + delete mainWindow; + + return 0; +} + +}}//end namespace + +MAIN_FUNCTION(Glest::Game::glestMain) diff --git a/source/glest_game/main/main.h b/source/glest_game/main/main.h new file mode 100644 index 00000000..d087ecce --- /dev/null +++ b/source/glest_game/main/main.h @@ -0,0 +1,55 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_MAIN_H_ +#define _GLEST_GAME_MAIN_H_ + +#include + +#include "program.h" +#include "window_gl.h" + +using Shared::Platform::MouseButton; +using Shared::Platform::MouseState; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class MainWindow +// +/// Main program window +// ===================================================== + +class MainWindow: public WindowGl{ +private: + Program* program; + +public: + MainWindow(Program *program); + ~MainWindow(); + + void setProgram(Program *program) {this->program= program;} + + virtual void eventMouseDown(int x, int y, MouseButton mouseButton); + virtual void eventMouseUp(int x, int y, MouseButton mouseButton); + virtual void eventMouseDoubleClick(int x, int y, MouseButton mouseButton); + virtual void eventMouseMove(int x, int y, const MouseState *mouseState); + virtual void eventKeyDown(char key); + virtual void eventKeyUp(char key); + virtual void eventKeyPress(char c); + virtual void eventActivate(bool active); + virtual void eventResize(SizeState sizeState); + virtual void eventClose(); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/main/program.cpp b/source/glest_game/main/program.cpp new file mode 100644 index 00000000..2678a6f7 --- /dev/null +++ b/source/glest_game/main/program.cpp @@ -0,0 +1,255 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "program.h" + +#include "sound.h" +#include "renderer.h" +#include "config.h" +#include "game.h" +#include "main_menu.h" +#include "intro.h" +#include "world.h" +#include "main.h" +#include "sound_renderer.h" +#include "logger.h" +#include "profiler.h" +#include "core_data.h" +#include "metrics.h" +#include "network_manager.h" +#include "menu_state_custom_game.h" +#include "menu_state_join_game.h" +#include "leak_dumper.h" + +using namespace Shared::Util; +using namespace Shared::Graphics; +using namespace Shared::Graphics::Gl; + +// ===================================================== +// class Program +// ===================================================== + +namespace Glest{ namespace Game{ + +const int Program::maxTimes= 10; + +// ===================== PUBLIC ======================== + +Program::Program(){ + programState= NULL; +} + +void Program::initNormal(WindowGl *window){ + init(window); + setState(new Intro(this)); +} + +void Program::initServer(WindowGl *window){ + MainMenu* mainMenu= NULL; + + init(window); + mainMenu= new MainMenu(this); + setState(mainMenu); + mainMenu->setState(new MenuStateCustomGame(this, mainMenu, true)); +} + +void Program::initClient(WindowGl *window, const Ip &serverIp){ + MainMenu* mainMenu= NULL; + + init(window); + mainMenu= new MainMenu(this); + setState(mainMenu); + mainMenu->setState(new MenuStateJoinGame(this, mainMenu, true, serverIp)); +} + +Program::~Program(){ + delete programState; + + Renderer::getInstance().end(); + + //restore video mode + restoreDisplaySettings(); +} + +void Program::mouseDownLeft(int x, int y){ + const Metrics &metrics= Metrics::getInstance(); + programState->mouseDownLeft(metrics.toVirtualX(x), metrics.toVirtualY(y)); +} + +void Program::mouseUpLeft(int x, int y){ + const Metrics &metrics= Metrics::getInstance(); + programState->mouseUpLeft(metrics.toVirtualX(x), metrics.toVirtualY(y)); +} + +void Program::mouseDownRight(int x, int y){ + const Metrics &metrics= Metrics::getInstance(); + programState->mouseDownRight(metrics.toVirtualX(x), metrics.toVirtualY(y)); +} + +void Program::mouseDoubleClickLeft(int x, int y){ + const Metrics &metrics= Metrics::getInstance(); + programState->mouseDoubleClickLeft(metrics.toVirtualX(x), metrics.toVirtualY(y)); +} + +void Program::mouseMove(int x, int y, const MouseState *ms){ + const Metrics &metrics= Metrics::getInstance(); + programState->mouseMove(metrics.toVirtualX(x), metrics.toVirtualY(y), ms); +} + +void Program::keyDown(char key){ + //delegate event + programState->keyDown(key); +} + +void Program::keyUp(char key){ + programState->keyUp(key); +} + +void Program::keyPress(char c){ + programState->keyPress(c); +} + +void Program::loop(){ + + //render + programState->render(); + + //update camera + while(updateCameraTimer.isTime()){ + programState->updateCamera(); + } + + //update world + while(updateTimer.isTime()){ + GraphicComponent::update(); + programState->update(); + SoundRenderer::getInstance().update(); + NetworkManager::getInstance().update(); + } + + //fps timer + while(fpsTimer.isTime()){ + programState->tick(); + } +} + +void Program::resize(SizeState sizeState){ + + switch(sizeState){ + case ssMinimized: + //restoreVideoMode(); + break; + case ssMaximized: + case ssRestored: + //setDisplaySettings(); + //renderer.reloadResources(); + break; + } +} + +// ==================== misc ==================== + +void Program::setState(ProgramState *programState){ + + delete this->programState; + + this->programState= programState; + programState->load(); + programState->init(); + + updateTimer.reset(); + updateCameraTimer.reset(); + fpsTimer.reset(); +} + +void Program::exit(){ + window->destroy(); +} + +// ==================== PRIVATE ==================== + +void Program::init(WindowGl *window){ + + this->window= window; + Config &config= Config::getInstance(); + + //set video mode + setDisplaySettings(); + + //window + window->setText("Glest"); + window->setStyle(config.getBool("Windowed")? wsWindowedFixed: wsFullscreen); + window->setPos(0, 0); + window->setSize(config.getInt("ScreenWidth"), config.getInt("ScreenHeight")); + window->create(); + + //timers + fpsTimer.init(1, maxTimes); + updateTimer.init(GameConstants::updateFps, maxTimes); + updateCameraTimer.init(GameConstants::cameraFps, maxTimes); + + //log start + Logger &logger= Logger::getInstance(); + logger.setFile("glest.log"); + logger.clear(); + + //lang + Lang &lang= Lang::getInstance(); + lang.loadStrings(config.getString("Lang")); + + //render + Renderer &renderer= Renderer::getInstance(); + + window->initGl(config.getInt("ColorBits"), config.getInt("DepthBits"), config.getInt("StencilBits")); + window->makeCurrentGl(); + + //coreData, needs renderer, but must load before renderer init + CoreData &coreData= CoreData::getInstance(); + coreData.load(); + + //init renderer (load global textures) + renderer.init(); + + //sound + SoundRenderer &soundRenderer= SoundRenderer::getInstance(); + soundRenderer.init(window); +} + +void Program::setDisplaySettings(){ + + Config &config= Config::getInstance(); + + if(!config.getBool("Windowed")){ + + int freq= config.getInt("RefreshFrequency"); + int colorBits= config.getInt("ColorBits"); + int screenWidth= config.getInt("ScreenWidth"); + int screenHeight= config.getInt("ScreenHeight"); + + if(!(changeVideoMode(screenWidth, screenHeight, colorBits, freq) || + changeVideoMode(screenWidth, screenHeight, colorBits, 0))) + { + throw runtime_error( + "Error setting video mode: " + + intToStr(screenWidth) + "x" + intToStr(screenHeight) + "x" + intToStr(colorBits)); + } + } +} + +void Program::restoreDisplaySettings(){ + Config &config= Config::getInstance(); + + if(!config.getBool("Windowed")){ + restoreVideoMode(); + } +} + +}}//end namespace diff --git a/source/glest_game/main/program.h b/source/glest_game/main/program.h new file mode 100644 index 00000000..3e4d9c50 --- /dev/null +++ b/source/glest_game/main/program.h @@ -0,0 +1,113 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_PROGRAM_H_ +#define _GLEST_GAME_PROGRAM_H_ + +#include "context.h" +#include "platform_util.h" +#include "window_gl.h" +#include "socket.h" + +using Shared::Graphics::Context; +using Shared::Platform::WindowGl; +using Shared::Platform::SizeState; +using Shared::Platform::MouseState; +using Shared::Platform::PerformanceTimer; +using Shared::Platform::Ip; + +namespace Glest{ namespace Game{ + +class Program; +class MainWindow; + +// ===================================================== +// class ProgramState +// +/// Base class for all program states: +/// Intro, MainMenu, Game, BattleEnd (State Design pattern) +// ===================================================== + +class ProgramState{ +protected: + Program *program; + +public: + ProgramState(Program *program) {this->program= program;} + virtual ~ProgramState(){}; + + virtual void render()=0; + virtual void update(){}; + virtual void updateCamera(){}; + virtual void tick(){}; + virtual void init(){}; + virtual void load(){}; + virtual void end(){}; + virtual void mouseDownLeft(int x, int y){}; + virtual void mouseUpLeft(int x, int y){}; + virtual void mouseDownRight(int x, int y){}; + virtual void mouseDoubleClickLeft(int x, int y){}; + virtual void mouseMove(int x, int y, const MouseState *mouseState){}; + virtual void keyDown(char key){}; + virtual void keyUp(char key){}; + virtual void keyPress(char c){}; +}; + +// =============================== +// class Program +// =============================== + +class Program{ +private: + static const int maxTimes; + +private: + ProgramState *programState; + + PerformanceTimer fpsTimer; + PerformanceTimer updateTimer; + PerformanceTimer updateCameraTimer; + + WindowGl *window; + +public: + Program(); + ~Program(); + + void initNormal(WindowGl *window); + void initServer(WindowGl *window); + void initClient(WindowGl *window, const Ip &serverIp); + + //main + void mouseDownLeft(int x, int y); + void mouseUpLeft(int x, int y); + void mouseDownRight(int x, int y); + void mouseDoubleClickLeft(int x, int y); + void mouseMove(int x, int y, const MouseState *mouseState); + void keyDown(char key); + void keyUp(char key); + void keyPress(char c); + void loop(); + void resize(SizeState sizeState); + + //misc + void setState(ProgramState *programState); + void exit(); + +private: + void init(WindowGl *window); + void setDisplaySettings(); + void restoreDisplaySettings(); +}; + +}} //end namespace + +#endif diff --git a/source/glest_game/menu/main_menu.cpp b/source/glest_game/menu/main_menu.cpp new file mode 100644 index 00000000..06e6f980 --- /dev/null +++ b/source/glest_game/menu/main_menu.cpp @@ -0,0 +1,194 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "main_menu.h" + +#include "renderer.h" +#include "sound.h" +#include "config.h" +#include "program.h" +#include "game_util.h" +#include "game.h" +#include "platform_util.h" +#include "sound_renderer.h" +#include "core_data.h" +#include "faction.h" +#include "metrics.h" +#include "network_manager.h" +#include "network_message.h" +#include "socket.h" +#include "menu_state_root.h" + +#include "leak_dumper.h" + +using namespace Shared::Sound; +using namespace Shared::Platform; +using namespace Shared::Util; +using namespace Shared::Graphics; +using namespace Shared::Xml; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class MainMenu +// ===================================================== + +// ===================== PUBLIC ======================== + +MainMenu::MainMenu(Program *program): + ProgramState(program) +{ + mouseX=100; + mouseY=100; + + state= NULL; + this->program= program; + + fps= 0; + lastFps= 0; + + setState(new MenuStateRoot(program, this)); +} + +MainMenu::~MainMenu(){ + delete state; + Renderer::getInstance().endMenu(); + SoundRenderer &soundRenderer= SoundRenderer::getInstance(); + soundRenderer.stopAllSounds(); +} + +void MainMenu::init(){ + Renderer::getInstance().initMenu(this); +} + +//asynchronus render update +void MainMenu::render(){ + + Config &config= Config::getInstance(); + Renderer &renderer= Renderer::getInstance(); + CoreData &coreData= CoreData::getInstance(); + + fps++; + + renderer.clearBuffers(); + + //3d + renderer.reset3dMenu(); + renderer.clearZBuffer(); + renderer.loadCameraMatrix(menuBackground.getCamera()); + renderer.renderMenuBackground(&menuBackground); + renderer.renderParticleManager(rsMenu); + + //2d + renderer.reset2d(); + state->render(); + renderer.renderMouse2d(mouseX, mouseY, mouse2dAnim); + + if(config.getBool("DebugMode")){ + renderer.renderText( + "FPS: " + intToStr(lastFps), + coreData.getMenuFontNormal(), Vec3f(1.f), 10, 10, false); + } + + renderer.swapBuffers(); +} + +//syncronus update +void MainMenu::update(){ + Renderer::getInstance().updateParticleManager(rsMenu); + mouse2dAnim= (mouse2dAnim +1) % Renderer::maxMouse2dAnim; + menuBackground.update(); + state->update(); +} + +void MainMenu::tick(){ + lastFps= fps; + fps= 0; +} + +//event magangement: mouse click +void MainMenu::mouseMove(int x, int y, const MouseState *ms){ + mouseX= x; mouseY= y; + state->mouseMove(x, y, ms); +} + +//returns if exiting +void MainMenu::mouseDownLeft(int x, int y){ + state->mouseClick(x, y, mbLeft); +} + +void MainMenu::mouseDownRight(int x, int y){ + state->mouseClick(x, y, mbRight); +} + +void MainMenu::keyDown(char key){ + state->keyDown(key); +} + +void MainMenu::keyPress(char c){ + state->keyPress(c); +} + +void MainMenu::setState(MenuState *state){ + delete this->state; + this->state= state; + GraphicComponent::resetFade(); + + menuBackground.setTargetCamera(state->getCamera()); +} + + +// ===================================================== +// class MenuState +// ===================================================== + +MenuState::MenuState(Program *program, MainMenu *mainMenu, const string &stateName){ + this->program= program; + this->mainMenu= mainMenu; + + //camera + XmlTree xmlTree; + xmlTree.load("data/core/menu/menu.xml"); + const XmlNode *menuNode= xmlTree.getRootNode(); + const XmlNode *cameraNode= menuNode->getChild("camera"); + + //position + const XmlNode *positionNode= cameraNode->getChild(stateName + "-position"); + Vec3f startPosition; + startPosition.x= positionNode->getAttribute("x")->getFloatValue(); + startPosition.y= positionNode->getAttribute("y")->getFloatValue(); + startPosition.z= positionNode->getAttribute("z")->getFloatValue(); + camera.setPosition(startPosition); + + //rotation + const XmlNode *rotationNode= cameraNode->getChild(stateName + "-rotation"); + Vec3f startRotation; + startRotation.x= rotationNode->getAttribute("x")->getFloatValue(); + startRotation.y= rotationNode->getAttribute("y")->getFloatValue(); + startRotation.z= rotationNode->getAttribute("z")->getFloatValue(); + camera.setOrientation(Quaternion(EulerAngles( + degToRad(startRotation.x), + degToRad(startRotation.y), + degToRad(startRotation.z)))); +} + + + + + + + + + + + + +}}//end namespace diff --git a/source/glest_game/menu/main_menu.h b/source/glest_game/menu/main_menu.h new file mode 100644 index 00000000..3599dedc --- /dev/null +++ b/source/glest_game/menu/main_menu.h @@ -0,0 +1,121 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_MAINMENU_H_ +#define _GLEST_GAME_MAINMENU_H_ + +#include "lang.h" +#include "console.h" +#include "vec.h" +#include "world.h" +#include "program.h" +#include "components.h" +#include "menu_background.h" +#include "game_settings.h" + +namespace Glest{ namespace Game{ + +//misc consts +struct MapInfo{ + Vec2i size; + int players; + string desc; +}; + +struct ScenarioInfo +{ + int difficulty; + ControlType factionControls[GameConstants::maxPlayers]; + int teams[GameConstants::maxPlayers]; + string factionTypeNames[GameConstants::maxPlayers]; + + string mapName; + string tilesetName; + string techTreeName; + + bool defaultUnits; + bool defaultResources; + bool defaultVictoryConditions; + + string desc; +}; + +class MenuState; + +// ===================================================== +// class MainMenu +// +/// Main menu ProgramState +// ===================================================== + +class MainMenu: public ProgramState{ +private: + //up + Program *program; + + //shared + GameSettings gameSettings; + MenuBackground menuBackground; + + MenuState *state; + + //shared + int mouseX, mouseY; + int mouse2dAnim; + int fps, lastFps; + +public: + MainMenu(Program *program); + ~MainMenu(); + + MenuBackground *getMenuBackground() {return &menuBackground;} + + virtual void render(); + virtual void update(); + virtual void tick(); + virtual void init(); + virtual void mouseMove(int x, int y, const MouseState *mouseState); + virtual void mouseDownLeft(int x, int y); + virtual void mouseDownRight(int x, int y); + virtual void keyDown(char key); + virtual void keyPress(char key); + + void setState(MenuState *state); +}; + + +// =============================== +// class MenuState +// =============================== + +class MenuState{ +protected: + Program *program; + + MainMenu *mainMenu; + Camera camera; + +public: + MenuState(Program *program, MainMenu *mainMenu, const string &stateName); + virtual ~MenuState(){}; + virtual void mouseClick(int x, int y, MouseButton mouseButton)=0; + virtual void mouseMove(int x, int y, const MouseState *mouseState)=0; + virtual void render()=0; + virtual void update(){}; + virtual void keyDown(char key){}; + virtual void keyPress(char c){}; + + const Camera *getCamera() const {return &camera;} +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/menu/menu_background.cpp b/source/glest_game/menu/menu_background.cpp new file mode 100644 index 00000000..fe1ff4f8 --- /dev/null +++ b/source/glest_game/menu/menu_background.cpp @@ -0,0 +1,180 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "menu_background.h" + +#include + +#include "renderer.h" +#include "core_data.h" +#include "config.h" +#include "xml_parser.h" +#include "util.h" +#include "game_constants.h" +#include "leak_dumper.h" + +using namespace Shared::Util; +using namespace Shared::Xml; +using namespace Shared::Graphics; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class MenuBackground +// ===================================================== + +MenuBackground::MenuBackground(){ + + Renderer &renderer= Renderer::getInstance(); + + //load data + + XmlTree xmlTree; + xmlTree.load("data/core/menu/menu.xml"); + const XmlNode *menuNode= xmlTree.getRootNode(); + + //water + const XmlNode *waterNode= menuNode->getChild("water"); + water= waterNode->getAttribute("value")->getBoolValue(); + if(water){ + waterHeight= waterNode->getAttribute("height")->getFloatValue(); + + //water texture + waterTexture= renderer.newTexture2D(rsMenu); + waterTexture->getPixmap()->init(4); + waterTexture->getPixmap()->load("data/core/menu/textures/water.tga"); + } + + //fog + const XmlNode *fogNode= menuNode->getChild("fog"); + fog= fogNode->getAttribute("value")->getBoolValue(); + if(fog){ + fogDensity= fogNode->getAttribute("density")->getFloatValue(); + } + + //rain + rain= menuNode->getChild("rain")->getAttribute("value")->getBoolValue(); + if(rain){ + RainParticleSystem *rps= new RainParticleSystem(); + rps->setSpeed(12.f/GameConstants::updateFps); + rps->setEmissionRate(25); + rps->setWind(-90.f, 4.f/GameConstants::updateFps); + rps->setPos(Vec3f(0.f, 25.f, 0.f)); + rps->setColor(Vec4f(1.f, 1.f, 1.f, 0.2f)); + rps->setRadius(30.f); + renderer.manageParticleSystem(rps, rsMenu); + + for(int i=0; igetChild("camera"); + + //position + const XmlNode *positionNode= cameraNode->getChild("start-position"); + Vec3f startPosition; + startPosition.x= positionNode->getAttribute("x")->getFloatValue(); + startPosition.y= positionNode->getAttribute("y")->getFloatValue(); + startPosition.z= positionNode->getAttribute("z")->getFloatValue(); + camera.setPosition(startPosition); + + //rotation + const XmlNode *rotationNode= cameraNode->getChild("start-rotation"); + Vec3f startRotation; + startRotation.x= rotationNode->getAttribute("x")->getFloatValue(); + startRotation.y= rotationNode->getAttribute("y")->getFloatValue(); + startRotation.z= rotationNode->getAttribute("z")->getFloatValue(); + camera.setOrientation(Quaternion(EulerAngles( + degToRad(startRotation.x), + degToRad(startRotation.y), + degToRad(startRotation.z)))); + + //load main model + mainModel= renderer.newModel(rsMenu); + mainModel->load("data/core/menu/main_model/menu_main.g3d"); + + //models + for(int i=0; i<5; ++i){ + characterModels[i]= renderer.newModel(rsMenu); + characterModels[i]->load("data/core/menu/about_models/character"+intToStr(i)+".g3d"); + } + + //about position + positionNode= cameraNode->getChild("about-position"); + aboutPosition.x= positionNode->getAttribute("x")->getFloatValue(); + aboutPosition.y= positionNode->getAttribute("y")->getFloatValue(); + aboutPosition.z= positionNode->getAttribute("z")->getFloatValue(); + rotationNode= cameraNode->getChild("about-rotation"); + + targetCamera= NULL; + t= 0.f; + fade= 0.f; + anim= 0.f; +} + +void MenuBackground::setTargetCamera(const Camera *targetCamera){ + this->targetCamera= targetCamera; + this->lastCamera= camera; + t= 0.f; +} + +void MenuBackground::update(){ + + //rain drops + for(int i=0; i=1.f){ + raindropStates[i]= 0.f; + raindropPos[i]= computeRaindropPos(); + } + } + + if(targetCamera!=NULL){ + t+= ((0.01f+(1.f-t)/10.f)/20.f)*(60.f/GameConstants::updateFps); + + //interpolate position + camera.setPosition(lastCamera.getPosition().lerp(t, targetCamera->getPosition())); + + //interpolate orientation + Quaternion q= lastCamera.getOrientation().lerp(t, targetCamera->getOrientation()); + camera.setOrientation(q); + + if(t>=1.f){ + targetCamera= NULL; + t= 0.f; + } + } + + //fade + if(fade<=1.f){ + fade+= 0.6f/GameConstants::updateFps; + if(fade>1.f){ + fade= 1.f; + } + } + + //animation + anim+=(0.6f/GameConstants::updateFps)/5+random.randRange(0.f, (0.6f/GameConstants::updateFps)/5.f); + if(anim>1.f){ + anim= 0.f; + } +} + +Vec2f MenuBackground::computeRaindropPos(){ + float f= static_cast(meshSize); + return Vec2f(random.randRange(-f, f), random.randRange(-f, f)); +} + +}}//end namespace + diff --git a/source/glest_game/menu/menu_background.h b/source/glest_game/menu/menu_background.h new file mode 100644 index 00000000..4ae4d5df --- /dev/null +++ b/source/glest_game/menu/menu_background.h @@ -0,0 +1,104 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_MENUBACKGROUND_H_ +#define _GLEST_GAME_MENUBACKGROUND_H_ + +#include "particle.h" +#include "camera.h" +#include "vec.h" +#include "texture.h" +#include "model.h" +#include "random.h" + +using Shared::Graphics::RainParticleSystem; +using Shared::Graphics::FireParticleSystem; +using Shared::Graphics::Camera; +using Shared::Graphics::Vec3f; +using Shared::Graphics::Vec2f; +using Shared::Graphics::Texture2D; +using Shared::Graphics::Model; +using Shared::Util::Random; + +namespace Glest{ namespace Game{ + +// =========================================================== +// class MenuBackground +// +/// Holds the data to display the 3D environment +/// in the MenuState +// =========================================================== + +class MenuBackground{ +public: + static const int meshSize= 32; + static const int raindropCount= 1000; + static const int characterCount= 5; + +private: + Model *mainModel; + + //water + bool water; + float waterHeight; + Texture2D *waterTexture; + + //fog + bool fog; + float fogDensity; + + //rain + bool rain; + Vec2f raindropPos[raindropCount]; + float raindropStates[raindropCount]; + + //camera + Camera camera; + Camera lastCamera; + const Camera *targetCamera; + float t; + + //misc + Random random; + Model *characterModels[characterCount]; + float anim; + float fade; + Vec3f aboutPosition; + +public: + MenuBackground(); + + bool getWater() const {return water;} + float getWaterHeight() const {return waterHeight;} + bool getFog() const {return fog;} + float getFogDensity() const {return fogDensity;} + bool getRain() const {return rain;} + Texture2D *getWaterTexture() const {return waterTexture;} + const Camera *getCamera() const {return &camera;} + const Model *getCharacterModel(int i) const {return characterModels[i];} + const Model *getMainModel() const {return mainModel;} + float getFade() const {return fade;} + Vec2f getRaindropPos(int i) const {return raindropPos[i];} + float getRaindropState(int i) const {return raindropStates[i];} + float getAnim() const {return anim;} + const Vec3f &getAboutPosition() const {return aboutPosition;} + + void setTargetCamera(const Camera *targetCamera); + void update(); + +private: + Vec2f computeRaindropPos(); +}; + +}} //end namespace + +#endif + diff --git a/source/glest_game/menu/menu_state_about.cpp b/source/glest_game/menu/menu_state_about.cpp new file mode 100644 index 00000000..0553db7b --- /dev/null +++ b/source/glest_game/menu/menu_state_about.cpp @@ -0,0 +1,92 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "menu_state_about.h" + +#include "renderer.h" +#include "menu_state_root.h" +#include "sound_renderer.h" +#include "core_data.h" +#include "menu_state_options.h" + +#include "leak_dumper.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class MenuStateAbout +// ===================================================== + +MenuStateAbout::MenuStateAbout(Program *program, MainMenu *mainMenu): + MenuState(program, mainMenu, "about") +{ + Lang &lang= Lang::getInstance(); + + //init + buttonReturn.init(460, 100, 125); + buttonReturn.setText(lang.get("Return")); + + for(int i= 0; isetState(new MenuStateRoot(program, mainMenu)); + } + +} + +void MenuStateAbout::mouseMove(int x, int y, const MouseState *ms){ + buttonReturn.mouseMove(x, y); +} + +void MenuStateAbout::render(){ + Renderer &renderer= Renderer::getInstance(); + + renderer.renderButton(&buttonReturn); + for(int i= 0; i results, teamItems, controlItems; + + //create + buttonReturn.init(350, 200, 125); + buttonPlayNow.init(525, 200, 125); + + //map listBox + findAll("maps/*.gbm", results, true); + if(results.size()==0){ + throw runtime_error("There is no maps"); + } + mapFiles= results; + for(int i= 0; isetState(new MenuStateNewGame(program, mainMenu)); + } + else if(buttonPlayNow.mouseClick(x,y)){ + GameSettings gameSettings; + + closeUnusedSlots(); + soundRenderer.playFx(coreData.getClickSoundC()); + loadGameSettings(&gameSettings); + serverInterface->launchGame(&gameSettings); + program->setState(new Game(program, &gameSettings)); + } + else if(listBoxMap.mouseClick(x, y)){ + loadMapInfo(Map::getMapPath(mapFiles[listBoxMap.getSelectedItemIndex()]), &mapInfo); + labelMapInfo.setText(mapInfo.desc); + updateControlers(); + } + else if(listBoxTileset.mouseClick(x, y)){ + } + else if(listBoxTechTree.mouseClick(x, y)){ + reloadFactions(); + } + else{ + for(int i=0; i(listBoxControls[j].getSelectedItemIndex()); + if(ct==ctHuman){ + if(humanIndex1==-1){ + humanIndex1= j; + } + else{ + humanIndex2= j; + } + } + } + + //no human + if(humanIndex1==-1 && humanIndex2==-1){ + listBoxControls[i].setSelectedItemIndex(ctHuman); + } + + //2 humans + if(humanIndex1!=-1 && humanIndex2!=-1){ + listBoxControls[humanIndex1==i? humanIndex2: humanIndex1].setSelectedItemIndex(ctClosed); + } + updateNetworkSlots(); + } + else if(listBoxFactions[i].mouseClick(x, y)){ + } + else if(listBoxTeams[i].mouseClick(x, y)){ + } + } + } +} + +void MenuStateCustomGame::mouseMove(int x, int y, const MouseState *ms){ + + buttonReturn.mouseMove(x, y); + buttonPlayNow.mouseMove(x, y); + + for(int i=0; igetSlot(i); + + assert(connectionSlot!=NULL); + + if(connectionSlot->isConnected()){ + labelNetStatus[i].setText(connectionSlot->getName()); + } + else + { + labelNetStatus[i].setText(lang.get("NotConnected")); + } + } + else{ + labelNetStatus[i].setText(""); + } + } +} + +void MenuStateCustomGame::loadGameSettings(GameSettings *gameSettings){ + + int factionCount= 0; + + gameSettings->setDescription(formatString(mapFiles[listBoxMap.getSelectedItemIndex()])); + gameSettings->setMap(mapFiles[listBoxMap.getSelectedItemIndex()]); + gameSettings->setTileset(tilesetFiles[listBoxTileset.getSelectedItemIndex()]); + gameSettings->setTech(techTreeFiles[listBoxTechTree.getSelectedItemIndex()]); + gameSettings->setDefaultUnits(true); + gameSettings->setDefaultResources(true); + gameSettings->setDefaultVictoryConditions(true); + + for(int i=0; i(listBoxControls[i].getSelectedItemIndex()); + if(ct!=ctClosed){ + if(ct==ctHuman){ + gameSettings->setThisFactionIndex(factionCount); + } + gameSettings->setFactionControl(factionCount, ct); + gameSettings->setTeam(factionCount, listBoxTeams[i].getSelectedItemIndex()); + gameSettings->setStartLocationIndex(factionCount, i); + gameSettings->setFactionTypeName(factionCount, factionFiles[listBoxFactions[i].getSelectedItemIndex()]); + factionCount++; + } + } + gameSettings->setFactionCount(factionCount); +} + +// ============ PRIVATE =========================== + +void MenuStateCustomGame::loadMapInfo(string file, MapInfo *mapInfo){ + + struct MapFileHeader{ + int32 version; + int32 maxPlayers; + int32 width; + int32 height; + int32 altFactor; + int32 waterLevel; + int8 title[128]; + }; + + Lang &lang= Lang::getInstance(); + + try{ + FILE *f= fopen(file.c_str(), "rb"); + if(f==NULL) + throw runtime_error("Can't open file"); + + MapFileHeader header; + fread(&header, sizeof(MapFileHeader), 1, f); + + mapInfo->size.x= header.width; + mapInfo->size.y= header.height; + mapInfo->players= header.maxPlayers; + + mapInfo->desc= lang.get("MaxPlayers")+": "+intToStr(mapInfo->players)+"\n"; + mapInfo->desc+=lang.get("Size")+": "+intToStr(mapInfo->size.x) + " x " + intToStr(mapInfo->size.y); + + fclose(f); + } + catch(exception e){ + throw runtime_error("Error loading map file: "+file+'\n'+e.what()); + } + +} + +void MenuStateCustomGame::reloadFactions(){ + + vector results; + + findAll("techs/"+techTreeFiles[listBoxTechTree.getSelectedItemIndex()]+"/factions/*.", results); + if(results.size()==0){ + throw runtime_error("There is no factions for this tech tree"); + } + factionFiles= results; + for(int i= 0; igetSlot(i)->isConnected()){ + listBoxControls[i].setSelectedItemIndex(ctClosed); + } + } + } + updateNetworkSlots(); +} + +void MenuStateCustomGame::updateNetworkSlots(){ + ServerInterface* serverInterface= NetworkManager::getInstance().getServerInterface(); + + for(int i= 0; igetSlot(i)==NULL && listBoxControls[i].getSelectedItemIndex()==ctNetwork){ + serverInterface->addSlot(i); + } + if(serverInterface->getSlot(i) != NULL && listBoxControls[i].getSelectedItemIndex()!=ctNetwork){ + serverInterface->removeSlot(i); + } + } +} + +}}//end namespace diff --git a/source/glest_game/menu/menu_state_custom_game.h b/source/glest_game/menu/menu_state_custom_game.h new file mode 100644 index 00000000..e48d72a0 --- /dev/null +++ b/source/glest_game/menu/menu_state_custom_game.h @@ -0,0 +1,67 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_MENUSTATECUSTOMGAME_H_ +#define _GLEST_GAME_MENUSTATECUSTOMGAME_H_ + +#include "main_menu.h" + +namespace Glest{ namespace Game{ + +// =============================== +// class MenuStateCustomGame +// =============================== + +class MenuStateCustomGame: public MenuState{ +private: + GraphicButton buttonReturn; + GraphicButton buttonPlayNow; + GraphicLabel labelControl; + GraphicLabel labelFaction; + GraphicLabel labelTeam; + GraphicLabel labelMap; + GraphicLabel labelTechTree; + GraphicLabel labelTileset; + GraphicLabel labelMapInfo; + GraphicListBox listBoxMap; + GraphicListBox listBoxTechTree; + GraphicListBox listBoxTileset; + vector mapFiles; + vector techTreeFiles; + vector tilesetFiles; + vector factionFiles; + GraphicLabel labelPlayers[GameConstants::maxPlayers]; + GraphicListBox listBoxControls[GameConstants::maxPlayers]; + GraphicListBox listBoxFactions[GameConstants::maxPlayers]; + GraphicListBox listBoxTeams[GameConstants::maxPlayers]; + GraphicLabel labelNetStatus[GameConstants::maxPlayers]; + MapInfo mapInfo; + +public: + MenuStateCustomGame(Program *program, MainMenu *mainMenu, bool openNetworkSlots= false); + + void mouseClick(int x, int y, MouseButton mouseButton); + void mouseMove(int x, int y, const MouseState *mouseState); + void render(); + void update(); + +private: + void loadGameSettings(GameSettings *gameSettings); + void loadMapInfo(string file, MapInfo *mapInfo); + void reloadFactions(); + void updateControlers(); + void closeUnusedSlots(); + void updateNetworkSlots(); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/menu/menu_state_graphic_info.cpp b/source/glest_game/menu/menu_state_graphic_info.cpp new file mode 100644 index 00000000..fd4bb7b0 --- /dev/null +++ b/source/glest_game/menu/menu_state_graphic_info.cpp @@ -0,0 +1,68 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "menu_state_graphic_info.h" + +#include "renderer.h" +#include "sound_renderer.h" +#include "core_data.h" +#include "menu_state_options.h" + +#include "leak_dumper.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class MenuStateGraphicInfo +// ===================================================== + +MenuStateGraphicInfo::MenuStateGraphicInfo(Program *program, MainMenu *mainMenu): + MenuState(program, mainMenu, "info") +{ + buttonReturn.init(387, 100, 125); + labelInfo.init(100, 700); + labelMoreInfo.init(100, 500); + labelMoreInfo.setFont(CoreData::getInstance().getMenuFontSmall()); + + Renderer &renderer= Renderer::getInstance(); + glInfo= renderer.getGlInfo(); + glMoreInfo= renderer.getGlMoreInfo(); +} + +void MenuStateGraphicInfo::mouseClick(int x, int y, MouseButton mouseButton){ + CoreData &coreData= CoreData::getInstance(); + SoundRenderer &soundRenderer= SoundRenderer::getInstance(); + + if(buttonReturn.mouseClick(x,y)){ + soundRenderer.playFx(coreData.getClickSoundA()); + mainMenu->setState(new MenuStateOptions(program, mainMenu)); + } +} + +void MenuStateGraphicInfo::mouseMove(int x, int y, const MouseState *ms){ + buttonReturn.mouseMove(x, y); +} + +void MenuStateGraphicInfo::render(){ + + Renderer &renderer= Renderer::getInstance(); + Lang &lang= Lang::getInstance(); + + buttonReturn.setText(lang.get("Return")); + labelInfo.setText(glInfo); + labelMoreInfo.setText(glMoreInfo); + + renderer.renderButton(&buttonReturn); + renderer.renderLabel(&labelInfo); + renderer.renderLabel(&labelMoreInfo); +} + +}}//end namespace diff --git a/source/glest_game/menu/menu_state_graphic_info.h b/source/glest_game/menu/menu_state_graphic_info.h new file mode 100644 index 00000000..9b72e727 --- /dev/null +++ b/source/glest_game/menu/menu_state_graphic_info.h @@ -0,0 +1,41 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_MENUSTATEGRAPHICINFO_H_ +#define _GLEST_GAME_MENUSTATEGRAPHICINFO_H_ + +#include "main_menu.h" + +namespace Glest{ namespace Game{ + +// =============================== +// class MenuStateGraphicInfo +// =============================== + +class MenuStateGraphicInfo: public MenuState{ +private: + GraphicButton buttonReturn; + GraphicLabel labelInfo; + GraphicLabel labelMoreInfo; + + string glInfo; + string glMoreInfo; +public: + MenuStateGraphicInfo(Program *program, MainMenu *mainMenu); + + void mouseClick(int x, int y, MouseButton mouseButton); + void mouseMove(int x, int y, const MouseState *mouseState); + void render(); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/menu/menu_state_join_game.cpp b/source/glest_game/menu/menu_state_join_game.cpp new file mode 100644 index 00000000..3d480eb7 --- /dev/null +++ b/source/glest_game/menu/menu_state_join_game.cpp @@ -0,0 +1,277 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "menu_state_join_game.h" + +#include "renderer.h" +#include "sound_renderer.h" +#include "core_data.h" +#include "config.h" +#include "menu_state_root.h" +#include "metrics.h" +#include "network_manager.h" +#include "network_message.h" +#include "client_interface.h" +#include "conversion.h" +#include "game.h" +#include "socket.h" + +#include "leak_dumper.h" + +namespace Glest{ namespace Game{ + +using namespace Shared::Util; + +// =============================== +// class MenuStateJoinGame +// =============================== + +const int MenuStateJoinGame::newServerIndex= 0; +const string MenuStateJoinGame::serverFileName= "servers.ini"; + +MenuStateJoinGame::MenuStateJoinGame(Program *program, MainMenu *mainMenu, bool connect, Ip serverIp): + MenuState(program, mainMenu, "join-game") +{ + Lang &lang= Lang::getInstance(); + Config &config= Config::getInstance(); + NetworkManager &networkManager= NetworkManager::getInstance(); + + servers.load(serverFileName); + + //buttons + buttonReturn.init(325, 300, 125); + buttonReturn.setText(lang.get("Return")); + + buttonConnect.init(475, 300, 125); + buttonConnect.setText(lang.get("Connect")); + + //server type label + labelServerType.init(330, 460); + labelServerType.setText(lang.get("ServerType") + ":"); + + //server type list box + listBoxServerType.init(465, 460); + listBoxServerType.pushBackItem(lang.get("ServerTypeNew")); + listBoxServerType.pushBackItem(lang.get("ServerTypePrevious")); + + //server label + labelServer.init(330, 430); + labelServer.setText(lang.get("Server") + ": "); + + //server listbox + listBoxServers.init(465, 430); + + for(int i= 0; iisConnected()){ + //server type + if(listBoxServerType.mouseClick(x, y)){ + if(!listBoxServers.getText().empty()){ + labelServerIp.setText(servers.getString(listBoxServers.getText())+"_"); + } + } + + //server list + else if(listBoxServerType.getSelectedItemIndex()!=newServerIndex){ + if(listBoxServers.mouseClick(x, y)){ + labelServerIp.setText(servers.getString(listBoxServers.getText())+"_"); + } + } + } + + //return + if(buttonReturn.mouseClick(x, y)){ + soundRenderer.playFx(coreData.getClickSoundA()); + mainMenu->setState(new MenuStateRoot(program, mainMenu)); + } + + //connect + else if(buttonConnect.mouseClick(x, y)){ + ClientInterface* clientInterface= networkManager.getClientInterface(); + + soundRenderer.playFx(coreData.getClickSoundA()); + labelInfo.setText(""); + + if(clientInterface->isConnected()){ + clientInterface->reset(); + } + else{ + connectToServer(); + } + } +} + +void MenuStateJoinGame::mouseMove(int x, int y, const MouseState *ms){ + buttonReturn.mouseMove(x, y); + buttonConnect.mouseMove(x, y); + listBoxServerType.mouseMove(x, y); + + //hide-show options depending on the selection + if(listBoxServers.getSelectedItemIndex()==newServerIndex){ + labelServerIp.mouseMove(x, y); + } + else{ + listBoxServers.mouseMove(x, y); + } +} + +void MenuStateJoinGame::render(){ + Renderer &renderer= Renderer::getInstance(); + + renderer.renderButton(&buttonReturn); + renderer.renderLabel(&labelServer); + renderer.renderLabel(&labelServerType); + renderer.renderLabel(&labelStatus); + renderer.renderLabel(&labelInfo); + renderer.renderButton(&buttonConnect); + renderer.renderListBox(&listBoxServerType); + + if(listBoxServerType.getSelectedItemIndex()==newServerIndex){ + renderer.renderLabel(&labelServerIp); + } + else + { + renderer.renderListBox(&listBoxServers); + } +} + +void MenuStateJoinGame::update(){ + ClientInterface* clientInterface= NetworkManager::getInstance().getClientInterface(); + Lang &lang= Lang::getInstance(); + + //update status label + if(clientInterface->isConnected()){ + buttonConnect.setText(lang.get("Disconnect")); + if(!clientInterface->getServerName().empty()){ + labelStatus.setText(lang.get("ConnectedToServer") + " " + clientInterface->getServerName()); + } + else{ + labelStatus.setText(lang.get("ConnectedToServer")); + } + } + else{ + buttonConnect.setText(lang.get("Connect")); + labelStatus.setText(lang.get("NotConnected")); + labelInfo.setText(""); + } + + //process network messages + if(clientInterface->isConnected()){ + + //update lobby + clientInterface->updateLobby(); + + //intro + if(clientInterface->getIntroDone()){ + labelInfo.setText(lang.get("WaitingHost")); + servers.setString(clientInterface->getServerName(), Ip(labelServerIp.getText()).getString()); + } + + //launch + if(clientInterface->getLaunchGame()){ + servers.save(serverFileName); + program->setState(new Game(program, clientInterface->getGameSettings())); + } + } +} + +void MenuStateJoinGame::keyDown(char key){ + ClientInterface* clientInterface= NetworkManager::getInstance().getClientInterface(); + + if(!clientInterface->isConnected()){ + if(key==vkBack){ + string text= labelServerIp.getText(); + + if(text.size()>1){ + text.erase(text.end()-2); + } + + labelServerIp.setText(text); + } + } +} + +void MenuStateJoinGame::keyPress(char c){ + ClientInterface* clientInterface= NetworkManager::getInstance().getClientInterface(); + + if(!clientInterface->isConnected()){ + int maxTextSize= 16; + + if(c>='0' && c<='9'){ + + if(labelServerIp.getText().size()connect(serverIp, GameConstants::serverPort); + labelServerIp.setText(serverIp.getString()+'_'); + labelInfo.setText(""); + + //save server ip + config.setString("ServerIp", serverIp.getString()); + config.save(); +} + +}}//end namespace diff --git a/source/glest_game/menu/menu_state_join_game.h b/source/glest_game/menu/menu_state_join_game.h new file mode 100644 index 00000000..ab296898 --- /dev/null +++ b/source/glest_game/menu/menu_state_join_game.h @@ -0,0 +1,63 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_MENUSTATEJOINGAME_H_ +#define _GLEST_GAME_MENUSTATEJOINGAME_H_ + +#include "properties.h" +#include "main_menu.h" + +using Shared::Util::Properties; + +namespace Glest{ namespace Game{ + +class NetworkMessageIntro; + +// =============================== +// class MenuStateJoinGame +// =============================== + +class MenuStateJoinGame: public MenuState{ +private: + static const int newServerIndex; + static const string serverFileName; + +private: + GraphicButton buttonReturn; + GraphicButton buttonConnect; + GraphicLabel labelServer; + GraphicLabel labelServerType; + GraphicLabel labelServerIp; + GraphicLabel labelStatus; + GraphicLabel labelInfo; + GraphicListBox listBoxServerType; + GraphicListBox listBoxServers; + + bool connected; + int playerIndex; + Properties servers; + +public: + MenuStateJoinGame(Program *program, MainMenu *mainMenu, bool connect= false, Ip serverIp= Ip()); + + void mouseClick(int x, int y, MouseButton mouseButton); + void mouseMove(int x, int y, const MouseState *mouseState); + void render(); + void update(); + void keyDown(char key); + void keyPress(char c); + +private: + void connectToServer(); +}; +}}//end namespace + +#endif diff --git a/source/glest_game/menu/menu_state_new_game.cpp b/source/glest_game/menu/menu_state_new_game.cpp new file mode 100644 index 00000000..1f665b39 --- /dev/null +++ b/source/glest_game/menu/menu_state_new_game.cpp @@ -0,0 +1,98 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "menu_state_new_game.h" + +#include "renderer.h" +#include "sound_renderer.h" +#include "core_data.h" +#include "config.h" +#include "menu_state_custom_game.h" +#include "menu_state_scenario.h" +#include "menu_state_root.h" +#include "metrics.h" +#include "network_manager.h" +#include "network_message.h" +#include "auto_test.h" +#include "socket.h" + +#include "leak_dumper.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class MenuStateNewGame +// ===================================================== + +MenuStateNewGame::MenuStateNewGame(Program *program, MainMenu *mainMenu): + MenuState(program, mainMenu, "root") +{ + Lang &lang= Lang::getInstance(); + + buttonCustomGame.init(425, 350, 150); + buttonScenario.init(425, 310, 150); + buttonTutorial.init(425, 270, 150); + buttonReturn.init(425, 230, 150); + + buttonCustomGame.setText(lang.get("CustomGame")); + buttonScenario.setText(lang.get("Scenario")); + buttonTutorial.setText(lang.get("Tutorial")); + buttonReturn.setText(lang.get("Return")); + + NetworkManager::getInstance().end(); +} + +void MenuStateNewGame::mouseClick(int x, int y, MouseButton mouseButton){ + + CoreData &coreData= CoreData::getInstance(); + SoundRenderer &soundRenderer= SoundRenderer::getInstance(); + + if(buttonCustomGame.mouseClick(x, y)){ + soundRenderer.playFx(coreData.getClickSoundB()); + mainMenu->setState(new MenuStateCustomGame(program, mainMenu)); + } + else if(buttonScenario.mouseClick(x, y)){ + soundRenderer.playFx(coreData.getClickSoundB()); + mainMenu->setState(new MenuStateScenario(program, mainMenu, "scenarios")); + } + else if(buttonTutorial.mouseClick(x, y)){ + soundRenderer.playFx(coreData.getClickSoundB()); + mainMenu->setState(new MenuStateScenario(program, mainMenu, "tutorials")); + } + else if(buttonReturn.mouseClick(x, y)){ + soundRenderer.playFx(coreData.getClickSoundB()); + mainMenu->setState(new MenuStateRoot(program, mainMenu)); + } +} + +void MenuStateNewGame::mouseMove(int x, int y, const MouseState *ms){ + buttonCustomGame.mouseMove(x, y); + buttonScenario.mouseMove(x, y); + buttonTutorial.mouseMove(x, y); + buttonReturn.mouseMove(x, y); +} + +void MenuStateNewGame::render(){ + Renderer &renderer= Renderer::getInstance(); + + renderer.renderButton(&buttonCustomGame); + renderer.renderButton(&buttonScenario); + renderer.renderButton(&buttonTutorial); + renderer.renderButton(&buttonReturn); +} + +void MenuStateNewGame::update(){ + if(Config::getInstance().getBool("AutoTest")){ + AutoTest::getInstance().updateNewGame(program, mainMenu); + } +} + +}}//end namespace diff --git a/source/glest_game/menu/menu_state_new_game.h b/source/glest_game/menu/menu_state_new_game.h new file mode 100644 index 00000000..ed3d6591 --- /dev/null +++ b/source/glest_game/menu/menu_state_new_game.h @@ -0,0 +1,42 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_MENUSTATENEWGAME_H_ +#define _GLEST_GAME_MENUSTATENEWGAME_H_ + +#include "main_menu.h" + +namespace Glest{ namespace Game{ + +// =============================== +// class MenuStateNewGame +// =============================== + +class MenuStateNewGame: public MenuState{ +private: + GraphicButton buttonCustomGame; + GraphicButton buttonScenario; + GraphicButton buttonTutorial; + GraphicButton buttonReturn; + +public: + MenuStateNewGame(Program *program, MainMenu *mainMenu); + + void mouseClick(int x, int y, MouseButton mouseButton); + void mouseMove(int x, int y, const MouseState *mouseState); + void update(); + void render(); +}; + + +}}//end namespace + +#endif diff --git a/source/glest_game/menu/menu_state_options.cpp b/source/glest_game/menu/menu_state_options.cpp new file mode 100644 index 00000000..fb648798 --- /dev/null +++ b/source/glest_game/menu/menu_state_options.cpp @@ -0,0 +1,227 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "menu_state_options.h" + +#include "renderer.h" +#include "sound_renderer.h" +#include "core_data.h" +#include "config.h" +#include "menu_state_root.h" +#include "util.h" + +#include "leak_dumper.h" + +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class MenuStateOptions +// ===================================================== + +MenuStateOptions::MenuStateOptions(Program *program, MainMenu *mainMenu): + MenuState(program, mainMenu, "config") +{ + Lang &lang= Lang::getInstance(); + Config &config= Config::getInstance(); + + //create + buttonReturn.init(200, 150, 125); + buttonAutoConfig.init(375, 150, 125); + + //labels + labelVolumeFx.init(200, 530); + labelVolumeAmbient.init(200, 500); + labelVolumeMusic.init(200, 470); + + labelLang.init(200, 400); + + labelFilter.init(200, 340); + labelShadows.init(200, 310); + labelTextures3D.init(200, 280); + labelLights.init(200, 250); + + //list boxes + listBoxVolumeFx.init(350, 530, 80); + listBoxVolumeAmbient.init(350, 500, 80); + listBoxVolumeMusic.init(350, 470, 80); + listBoxMusicSelect.init(350, 440, 150); + + listBoxLang.init(350, 400, 170); + + listBoxFilter.init(350, 340, 170); + listBoxShadows.init(350, 310, 170); + listBoxTextures3D.init(350, 280, 80); + listBoxLights.init(350, 250, 80); + + //set text + buttonReturn.setText(lang.get("Return")); + buttonAutoConfig.setText(lang.get("AutoConfig")); + labelLang.setText(lang.get("Language")); + labelShadows.setText(lang.get("Shadows")); + labelFilter.setText(lang.get("Filter")); + labelTextures3D.setText(lang.get("Textures3D")); + labelLights.setText(lang.get("MaxLights")); + labelVolumeFx.setText(lang.get("FxVolume")); + labelVolumeAmbient.setText(lang.get("AmbientVolume")); + labelVolumeMusic.setText(lang.get("MusicVolume")); + + //sound + + //lang + vector langResults; + findAll("data/lang/*.lng", langResults, true); + if(langResults.empty()){ + throw runtime_error("There is no lang file"); + } + listBoxLang.setItems(langResults); + listBoxLang.setSelectedItem(config.getString("Lang")); + + //shadows + for(int i= 0; i(i)))); + } + + string str= config.getString("Shadows"); + listBoxShadows.setSelectedItemIndex(clamp(Renderer::strToShadows(str), 0, Renderer::sCount-1)); + + //filter + listBoxFilter.pushBackItem("Bilinear"); + listBoxFilter.pushBackItem("Trilinear"); + listBoxFilter.setSelectedItem(config.getString("Filter")); + + //textures 3d + listBoxTextures3D.pushBackItem(lang.get("No")); + listBoxTextures3D.pushBackItem(lang.get("Yes")); + listBoxTextures3D.setSelectedItemIndex(clamp(config.getInt("Textures3D"), 0, 1)); + + //lights + for(int i= 1; i<=8; ++i){ + listBoxLights.pushBackItem(intToStr(i)); + } + listBoxLights.setSelectedItemIndex(clamp(config.getInt("MaxLights")-1, 0, 7)); + + //sound + for(int i=0; i<=100; i+=5){ + listBoxVolumeFx.pushBackItem(intToStr(i)); + listBoxVolumeAmbient.pushBackItem(intToStr(i)); + listBoxVolumeMusic.pushBackItem(intToStr(i)); + } + listBoxVolumeFx.setSelectedItem(intToStr(config.getInt("SoundVolumeFx")/5*5)); + listBoxVolumeAmbient.setSelectedItem(intToStr(config.getInt("SoundVolumeAmbient")/5*5)); + listBoxVolumeMusic.setSelectedItem(intToStr(config.getInt("SoundVolumeMusic")/5*5)); +} + +void MenuStateOptions::mouseClick(int x, int y, MouseButton mouseButton){ + + Config &config= Config::getInstance(); + Lang &lang= Lang::getInstance(); + CoreData &coreData= CoreData::getInstance(); + SoundRenderer &soundRenderer= SoundRenderer::getInstance(); + + if(buttonReturn.mouseClick(x, y)){ + soundRenderer.playFx(coreData.getClickSoundA()); + mainMenu->setState(new MenuStateRoot(program, mainMenu)); + } + else if(buttonAutoConfig.mouseClick(x, y)){ + soundRenderer.playFx(coreData.getClickSoundA()); + Renderer::getInstance().autoConfig(); + saveConfig(); + mainMenu->setState(new MenuStateOptions(program, mainMenu)); + } + else if(listBoxLang.mouseClick(x, y)){ + config.setString("Lang", listBoxLang.getSelectedItem()); + lang.loadStrings(config.getString("Lang")); + saveConfig(); + mainMenu->setState(new MenuStateOptions(program, mainMenu)); + + } + else if(listBoxShadows.mouseClick(x, y)){ + int index= listBoxShadows.getSelectedItemIndex(); + config.setString("Shadows", Renderer::shadowsToStr(static_cast(index))); + saveConfig(); + } + else if(listBoxFilter.mouseClick(x, y)){ + config.setString("Filter", listBoxFilter.getSelectedItem()); + saveConfig(); + } + else if(listBoxTextures3D.mouseClick(x, y)){ + config.setInt("Textures3D", listBoxTextures3D.getSelectedItemIndex()); + saveConfig(); + } + else if(listBoxLights.mouseClick(x, y)){ + config.setInt("MaxLights", listBoxLights.getSelectedItemIndex()+1); + saveConfig(); + } + else if(listBoxVolumeFx.mouseClick(x, y)){ + config.setString("SoundVolumeFx", listBoxVolumeFx.getSelectedItem()); + saveConfig(); + } + else if(listBoxVolumeAmbient.mouseClick(x, y)){ + config.setString("SoundVolumeAmbient", listBoxVolumeAmbient.getSelectedItem()); + saveConfig(); + } + else if(listBoxVolumeMusic.mouseClick(x, y)){ + CoreData::getInstance().getMenuMusic()->setVolume(strToInt(listBoxVolumeMusic.getSelectedItem())/100.f); + config.setString("SoundVolumeMusic", listBoxVolumeMusic.getSelectedItem()); + saveConfig(); + } + + +} + +void MenuStateOptions::mouseMove(int x, int y, const MouseState *ms){ + buttonReturn.mouseMove(x, y); + buttonAutoConfig.mouseMove(x, y); + listBoxLang.mouseMove(x, y); + listBoxVolumeFx.mouseMove(x, y); + listBoxVolumeAmbient.mouseMove(x, y); + listBoxVolumeMusic.mouseMove(x, y); + listBoxLang.mouseMove(x, y); + listBoxFilter.mouseMove(x, y); + listBoxShadows.mouseMove(x, y); + listBoxTextures3D.mouseMove(x, y); + listBoxLights.mouseMove(x, y); +} + +void MenuStateOptions::render(){ + Renderer &renderer= Renderer::getInstance(); + + renderer.renderButton(&buttonReturn); + renderer.renderButton(&buttonAutoConfig); + renderer.renderListBox(&listBoxLang); + renderer.renderListBox(&listBoxShadows); + renderer.renderListBox(&listBoxTextures3D); + renderer.renderListBox(&listBoxLights); + renderer.renderListBox(&listBoxFilter); + renderer.renderListBox(&listBoxVolumeFx); + renderer.renderListBox(&listBoxVolumeAmbient); + renderer.renderListBox(&listBoxVolumeMusic); + renderer.renderLabel(&labelLang); + renderer.renderLabel(&labelShadows); + renderer.renderLabel(&labelTextures3D); + renderer.renderLabel(&labelLights); + renderer.renderLabel(&labelFilter); + renderer.renderLabel(&labelVolumeFx); + renderer.renderLabel(&labelVolumeAmbient); + renderer.renderLabel(&labelVolumeMusic); +} + +void MenuStateOptions::saveConfig(){ + Config &config= Config::getInstance(); + + config.save(); + Renderer::getInstance().loadConfig(); + SoundRenderer::getInstance().loadConfig(); +} + +}}//end namespace diff --git a/source/glest_game/menu/menu_state_options.h b/source/glest_game/menu/menu_state_options.h new file mode 100644 index 00000000..3f56c561 --- /dev/null +++ b/source/glest_game/menu/menu_state_options.h @@ -0,0 +1,59 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_MENUSTATEOPTIONS_H_ +#define _GLEST_GAME_MENUSTATEOPTIONS_H_ + +#include "main_menu.h" + +namespace Glest{ namespace Game{ + +// =============================== +// class MenuStateOptions +// =============================== + +class MenuStateOptions: public MenuState{ +private: + GraphicButton buttonReturn; + GraphicButton buttonAutoConfig; + + GraphicLabel labelLang; + GraphicLabel labelShadows; + GraphicLabel labelFilter; + GraphicLabel labelTextures3D; + GraphicLabel labelLights; + GraphicLabel labelVolumeFx; + GraphicLabel labelVolumeAmbient; + GraphicLabel labelVolumeMusic; + GraphicListBox listBoxLang; + GraphicListBox listBoxShadows; + GraphicListBox listBoxFilter; + GraphicListBox listBoxTextures3D; + GraphicListBox listBoxLights; + GraphicListBox listBoxVolumeFx; + GraphicListBox listBoxVolumeAmbient; + GraphicListBox listBoxVolumeMusic; + GraphicListBox listBoxMusicSelect; + +public: + MenuStateOptions(Program *program, MainMenu *mainMenu); + + void mouseClick(int x, int y, MouseButton mouseButton); + void mouseMove(int x, int y, const MouseState *mouseState); + void render(); + +private: + void saveConfig(); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/menu/menu_state_root.cpp b/source/glest_game/menu/menu_state_root.cpp new file mode 100644 index 00000000..6e57d4ad --- /dev/null +++ b/source/glest_game/menu/menu_state_root.cpp @@ -0,0 +1,116 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "menu_state_root.h" + +#include "renderer.h" +#include "sound_renderer.h" +#include "core_data.h" +#include "config.h" +#include "menu_state_new_game.h" +#include "menu_state_join_game.h" +#include "menu_state_options.h" +#include "menu_state_about.h" +#include "metrics.h" +#include "network_manager.h" +#include "network_message.h" +#include "socket.h" +#include "auto_test.h" + +#include "leak_dumper.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class MenuStateRoot +// ===================================================== + +MenuStateRoot::MenuStateRoot(Program *program, MainMenu *mainMenu): + MenuState(program, mainMenu, "root") +{ + Lang &lang= Lang::getInstance(); + + buttonNewGame.init(425, 350, 150); + buttonJoinGame.init(425, 310, 150); + buttonOptions.init(425, 270, 150); + buttonAbout.init(425, 230, 150); + buttonExit.init(425, 190, 150); + labelVersion.init(525, 420); + + buttonNewGame.setText(lang.get("NewGame")); + buttonJoinGame.setText(lang.get("JoinGame")); + buttonOptions.setText(lang.get("Options")); + buttonAbout.setText(lang.get("About")); + buttonExit.setText(lang.get("Exit")); + labelVersion.setText(glestVersionString); +} + +void MenuStateRoot::mouseClick(int x, int y, MouseButton mouseButton){ + + CoreData &coreData= CoreData::getInstance(); + SoundRenderer &soundRenderer= SoundRenderer::getInstance(); + + if(buttonNewGame.mouseClick(x, y)){ + soundRenderer.playFx(coreData.getClickSoundB()); + mainMenu->setState(new MenuStateNewGame(program, mainMenu)); + } + else if(buttonJoinGame.mouseClick(x, y)){ + soundRenderer.playFx(coreData.getClickSoundB()); + mainMenu->setState(new MenuStateJoinGame(program, mainMenu)); + } + else if(buttonOptions.mouseClick(x, y)){ + soundRenderer.playFx(coreData.getClickSoundB()); + mainMenu->setState(new MenuStateOptions(program, mainMenu)); + } + else if(buttonAbout.mouseClick(x, y)){ + soundRenderer.playFx(coreData.getClickSoundB()); + mainMenu->setState(new MenuStateAbout(program, mainMenu)); + } + else if(buttonExit.mouseClick(x, y)){ + soundRenderer.playFx(coreData.getClickSoundA()); + program->exit(); + } +} + +void MenuStateRoot::mouseMove(int x, int y, const MouseState *ms){ + buttonNewGame.mouseMove(x, y); + buttonJoinGame.mouseMove(x, y); + buttonOptions.mouseMove(x, y); + buttonAbout.mouseMove(x, y); + buttonExit.mouseMove(x,y); +} + +void MenuStateRoot::render(){ + Renderer &renderer= Renderer::getInstance(); + CoreData &coreData= CoreData::getInstance(); + const Metrics &metrics= Metrics::getInstance(); + + int w= 300; + int h= 150; + + renderer.renderTextureQuad( + (metrics.getVirtualW()-w)/2, 475-h/2, w, h, + coreData.getLogoTexture(), GraphicComponent::getFade()); + renderer.renderButton(&buttonNewGame); + renderer.renderButton(&buttonJoinGame); + renderer.renderButton(&buttonOptions); + renderer.renderButton(&buttonAbout); + renderer.renderButton(&buttonExit); + renderer.renderLabel(&labelVersion); +} + +void MenuStateRoot::update(){ + if(Config::getInstance().getBool("AutoTest")){ + AutoTest::getInstance().updateRoot(program, mainMenu); + } +} + +}}//end namespace diff --git a/source/glest_game/menu/menu_state_root.h b/source/glest_game/menu/menu_state_root.h new file mode 100644 index 00000000..60947cdc --- /dev/null +++ b/source/glest_game/menu/menu_state_root.h @@ -0,0 +1,44 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_MENUSTATEROOT_H_ +#define _GLEST_GAME_MENUSTATEROOT_H_ + +#include "main_menu.h" + +namespace Glest{ namespace Game{ + +// =============================== +// class MenuStateRoot +// =============================== + +class MenuStateRoot: public MenuState{ +private: + GraphicButton buttonNewGame; + GraphicButton buttonJoinGame; + GraphicButton buttonOptions; + GraphicButton buttonAbout; + GraphicButton buttonExit; + GraphicLabel labelVersion; + +public: + MenuStateRoot(Program *program, MainMenu *mainMenu); + + void mouseClick(int x, int y, MouseButton mouseButton); + void mouseMove(int x, int y, const MouseState *mouseState); + void render(); + void update(); +}; + + +}}//end namespace + +#endif diff --git a/source/glest_game/menu/menu_state_scenario.cpp b/source/glest_game/menu/menu_state_scenario.cpp new file mode 100644 index 00000000..119c7f12 --- /dev/null +++ b/source/glest_game/menu/menu_state_scenario.cpp @@ -0,0 +1,248 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "menu_state_scenario.h" + +#include "renderer.h" +#include "menu_state_new_game.h" +#include "sound_renderer.h" +#include "core_data.h" +#include "menu_state_options.h" +#include "network_manager.h" +#include "config.h" +#include "auto_test.h" +#include "game.h" + +#include "leak_dumper.h" + +namespace Glest{ namespace Game{ + +using namespace Shared::Xml; + +// ===================================================== +// class MenuStateScenario +// ===================================================== + +MenuStateScenario::MenuStateScenario(Program *program, MainMenu *mainMenu, const string &dir): + MenuState(program, mainMenu, "scenario") +{ + Lang &lang= Lang::getInstance(); + NetworkManager &networkManager= NetworkManager::getInstance(); + vector results; + + this->dir = dir; + + labelInfo.init(350, 350); + labelInfo.setFont(CoreData::getInstance().getMenuFontNormal()); + + buttonReturn.init(350, 200, 125); + buttonPlayNow.init(525, 200, 125); + + listBoxScenario.init(350, 400, 190); + labelScenario.init(350, 430); + + buttonReturn.setText(lang.get("Return")); + buttonPlayNow.setText(lang.get("PlayNow")); + + labelScenario.setText(lang.get("Scenario")); + + //scenario listbox + findAll(dir+"/*.", results); + scenarioFiles= results; + if(results.size()==0){ + throw runtime_error("There are no scenarios"); + } + for(int i= 0; isetState(new MenuStateNewGame(program, mainMenu)); + } + else if(buttonPlayNow.mouseClick(x,y)){ + soundRenderer.playFx(coreData.getClickSoundC()); + launchGame(); + } + else if(listBoxScenario.mouseClick(x, y)){ + loadScenarioInfo(Scenario::getScenarioPath(dir, scenarioFiles[listBoxScenario.getSelectedItemIndex()]), &scenarioInfo); + labelInfo.setText(scenarioInfo.desc); + } +} + +void MenuStateScenario::mouseMove(int x, int y, const MouseState *ms){ + + listBoxScenario.mouseMove(x, y); + + buttonReturn.mouseMove(x, y); + buttonPlayNow.mouseMove(x, y); +} + +void MenuStateScenario::render(){ + + Renderer &renderer= Renderer::getInstance(); + + renderer.renderLabel(&labelInfo); + renderer.renderLabel(&labelScenario); + renderer.renderListBox(&listBoxScenario); + + renderer.renderButton(&buttonReturn); + renderer.renderButton(&buttonPlayNow); +} + +void MenuStateScenario::update(){ + if(Config::getInstance().getBool("AutoTest")){ + AutoTest::getInstance().updateScenario(this); + } +} + +void MenuStateScenario::launchGame(){ + GameSettings gameSettings; + loadGameSettings(&scenarioInfo, &gameSettings); + program->setState(new Game(program, &gameSettings)); +} + +void MenuStateScenario::setScenario(int i){ + listBoxScenario.setSelectedItemIndex(i); + loadScenarioInfo(Scenario::getScenarioPath(dir, scenarioFiles[listBoxScenario.getSelectedItemIndex()]), &scenarioInfo); +} + +void MenuStateScenario::loadScenarioInfo(string file, ScenarioInfo *scenarioInfo){ + + Lang &lang= Lang::getInstance(); + + XmlTree xmlTree; + xmlTree.load(file); + + const XmlNode *scenarioNode= xmlTree.getRootNode(); + const XmlNode *difficultyNode= scenarioNode->getChild("difficulty"); + scenarioInfo->difficulty = difficultyNode->getAttribute("value")->getIntValue(); + if( scenarioInfo->difficulty < dVeryEasy || scenarioInfo->difficulty > dInsane ) + { + throw std::runtime_error("Invalid difficulty"); + } + + const XmlNode *playersNode= scenarioNode->getChild("players"); + for(int i= 0; igetChild("player", i); + ControlType factionControl = strToControllerType( playerNode->getAttribute("control")->getValue() ); + string factionTypeName; + + scenarioInfo->factionControls[i] = factionControl; + + if(factionControl != ctClosed){ + int teamIndex = playerNode->getAttribute("team")->getIntValue(); + + if( teamIndex < 1 || teamIndex > GameConstants::maxPlayers ) + { + throw runtime_error("Team out of range: " + intToStr(teamIndex) ); + } + + scenarioInfo->teams[i]= playerNode->getAttribute("team")->getIntValue(); + scenarioInfo->factionTypeNames[i]= playerNode->getAttribute("faction")->getValue(); + } + + scenarioInfo->mapName = scenarioNode->getChild("map")->getAttribute("value")->getValue(); + scenarioInfo->tilesetName = scenarioNode->getChild("tileset")->getAttribute("value")->getValue(); + scenarioInfo->techTreeName = scenarioNode->getChild("tech-tree")->getAttribute("value")->getValue(); + scenarioInfo->defaultUnits = scenarioNode->getChild("default-units")->getAttribute("value")->getBoolValue(); + scenarioInfo->defaultResources = scenarioNode->getChild("default-resources")->getAttribute("value")->getBoolValue(); + scenarioInfo->defaultVictoryConditions = scenarioNode->getChild("default-victory-conditions")->getAttribute("value")->getBoolValue(); + } + + //add player info + scenarioInfo->desc= lang.get("Player") + ": "; + for(int i=0; ifactionControls[i] == ctHuman ) + { + scenarioInfo->desc+= formatString(scenarioInfo->factionTypeNames[i]); + break; + } + } + + //add misc info + string difficultyString = "Difficulty" + intToStr(scenarioInfo->difficulty); + + scenarioInfo->desc+= "\n"; + scenarioInfo->desc+= lang.get("Difficulty") + ": " + lang.get(difficultyString) +"\n"; + scenarioInfo->desc+= lang.get("Map") + ": " + formatString(scenarioInfo->mapName) + "\n"; + scenarioInfo->desc+= lang.get("Tileset") + ": " + formatString(scenarioInfo->tilesetName) + "\n"; + scenarioInfo->desc+= lang.get("TechTree") + ": " + formatString(scenarioInfo->techTreeName) + "\n"; +} + +void MenuStateScenario::loadGameSettings(const ScenarioInfo *scenarioInfo, GameSettings *gameSettings){ + + int factionCount= 0; + + gameSettings->setDescription(formatString(scenarioFiles[listBoxScenario.getSelectedItemIndex()])); + gameSettings->setMap(scenarioInfo->mapName); + gameSettings->setTileset(scenarioInfo->tilesetName); + gameSettings->setTech(scenarioInfo->techTreeName); + gameSettings->setScenario(scenarioFiles[listBoxScenario.getSelectedItemIndex()]); + gameSettings->setScenarioDir(dir); + gameSettings->setDefaultUnits(scenarioInfo->defaultUnits); + gameSettings->setDefaultResources(scenarioInfo->defaultResources); + gameSettings->setDefaultVictoryConditions(scenarioInfo->defaultVictoryConditions); + + for(int i=0; i(scenarioInfo->factionControls[i]); + if(ct!=ctClosed){ + if(ct==ctHuman){ + gameSettings->setThisFactionIndex(factionCount); + } + gameSettings->setFactionControl(factionCount, ct); + gameSettings->setTeam(factionCount, scenarioInfo->teams[i]-1); + gameSettings->setStartLocationIndex(factionCount, i); + gameSettings->setFactionTypeName(factionCount, scenarioInfo->factionTypeNames[i]); + factionCount++; + } + } + + gameSettings->setFactionCount(factionCount); +} + +ControlType MenuStateScenario::strToControllerType(const string &str){ + if(str=="closed"){ + return ctClosed; + } + else if(str=="cpu-easy"){ + return ctCpuEasy; + } + else if(str=="cpu"){ + return ctCpu; + } + else if(str=="cpu-ultra"){ + return ctCpuUltra; + } + else if(str=="cpu-mega"){ + return ctCpuMega; + } + else if(str=="human"){ + return ctHuman; + } + + throw std::runtime_error("Unknown controller type: " + str); +} + +}}//end namespace diff --git a/source/glest_game/menu/menu_state_scenario.h b/source/glest_game/menu/menu_state_scenario.h new file mode 100644 index 00000000..2e0e67f8 --- /dev/null +++ b/source/glest_game/menu/menu_state_scenario.h @@ -0,0 +1,69 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_MENUSTATESCENARIO_H_ +#define _GLEST_GAME_MENUSTATESCENARIO_H_ + +#include "main_menu.h" + +namespace Glest{ namespace Game{ + +// =============================== +// class MenuStateScenario +// =============================== + +class MenuStateScenario: public MenuState{ +private: + enum Difficulty{ + dVeryEasy, + dEasy, + dMedium, + dHard, + dVeryHard, + dInsane + }; + + GraphicButton buttonReturn; + GraphicButton buttonPlayNow; + + GraphicLabel labelInfo; + GraphicLabel labelScenario; + GraphicListBox listBoxScenario; + + vector scenarioFiles; + + ScenarioInfo scenarioInfo; + string dir; + +public: + MenuStateScenario(Program *program, MainMenu *mainMenu, const string &dir); + + void mouseClick(int x, int y, MouseButton mouseButton); + void mouseMove(int x, int y, const MouseState *mouseState); + void render(); + void update(); + + void launchGame(); + void setScenario(int i); + int getScenarioCount() const { return listBoxScenario.getItemCount(); } + +private: + void loadScenarioInfo(string file, ScenarioInfo *scenarioInfo); + void loadGameSettings(const ScenarioInfo *scenarioInfo, GameSettings *gameSettings); + Difficulty computeDifficulty(const ScenarioInfo *scenarioInfo); + ControlType strToControllerType(const string &str); + +}; + + +}}//end namespace + +#endif diff --git a/source/glest_game/network/client_interface.cpp b/source/glest_game/network/client_interface.cpp new file mode 100644 index 00000000..89d112c1 --- /dev/null +++ b/source/glest_game/network/client_interface.cpp @@ -0,0 +1,273 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "client_interface.h" + +#include +#include + +#include "platform_util.h" +#include "game_util.h" +#include "conversion.h" +#include "config.h" +#include "lang.h" +#include "leak_dumper.h" + +using namespace std; +using namespace Shared::Platform; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class ClientInterface +// ===================================================== + +const int ClientInterface::messageWaitTimeout= 10000; //10 seconds +const int ClientInterface::waitSleepTime= 50; + +ClientInterface::ClientInterface(){ + clientSocket= NULL; + launchGame= false; + introDone= false; + playerIndex= -1; +} + +ClientInterface::~ClientInterface(){ + delete clientSocket; +} + +void ClientInterface::connect(const Ip &ip, int port){ + delete clientSocket; + clientSocket= new ClientSocket(); + clientSocket->setBlock(false); + clientSocket->connect(ip, port); +} + +void ClientInterface::reset(){ + delete clientSocket; + clientSocket= NULL; +} + +void ClientInterface::update(){ + NetworkMessageCommandList networkMessageCommandList; + + //send as many commands as we can + while(!requestedCommands.empty()){ + if(networkMessageCommandList.addCommand(&requestedCommands.back())){ + requestedCommands.pop_back(); + } + else{ + break; + } + } + if(networkMessageCommandList.getCommandCount()>0){ + sendMessage(&networkMessageCommandList); + } + + //clear chat variables + chatText.clear(); + chatSender.clear(); + chatTeamIndex= -1; +} + +void ClientInterface::updateLobby(){ + NetworkMessageType networkMessageType= getNextMessageType(); + + switch(networkMessageType){ + case nmtInvalid: + break; + + case nmtIntro:{ + NetworkMessageIntro networkMessageIntro; + + if(receiveMessage(&networkMessageIntro)){ + + //check consistency + if(Config::getInstance().getBool("NetworkConsistencyChecks")){ + if(networkMessageIntro.getVersionString()!=getNetworkVersionString()){ + throw runtime_error("Server and client versions do not match (" + networkMessageIntro.getVersionString() + "). You have to use the same binaries."); + } + } + + //send intro message + NetworkMessageIntro sendNetworkMessageIntro(getNetworkVersionString(), getHostName(), -1); + + playerIndex= networkMessageIntro.getPlayerIndex(); + serverName= networkMessageIntro.getName(); + sendMessage(&sendNetworkMessageIntro); + + assert(playerIndex>=0 && playerIndexreadyWaitTimeout){ + throw runtime_error("Timeout waiting for server"); + } + } + else{ + throw runtime_error("Unexpected network message: " + intToStr(networkMessageType) ); + } + + // sleep a bit + sleep(waitSleepTime); + } + + //check checksum + if(Config::getInstance().getBool("NetworkConsistencyChecks")){ + if(networkMessageReady.getChecksum()!=checksum->getSum()){ + throw runtime_error("Checksum error, you don't have the same data as the server"); + } + } + + //delay the start a bit, so clients have nore room to get messages + sleep(GameConstants::networkExtraLatency); +} + +void ClientInterface::sendTextMessage(const string &text, int teamIndex){ + NetworkMessageText networkMessageText(text, getHostName(), teamIndex); + sendMessage(&networkMessageText); +} + +string ClientInterface::getNetworkStatus() const{ + return Lang::getInstance().get("Server") + ": " + serverName; +} + +void ClientInterface::waitForMessage(){ + Chrono chrono; + + chrono.start(); + + while(getNextMessageType()==nmtInvalid){ + + if(!isConnected()){ + throw runtime_error("Disconnected"); + } + + if(chrono.getMillis()>messageWaitTimeout){ + throw runtime_error("Timeout waiting for message"); + } + + sleep(waitSleepTime); + } +} + +}}//end namespace diff --git a/source/glest_game/network/client_interface.h b/source/glest_game/network/client_interface.h new file mode 100644 index 00000000..5c16b752 --- /dev/null +++ b/source/glest_game/network/client_interface.h @@ -0,0 +1,81 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_CLIENTINTERFACE_H_ +#define _GLEST_GAME_CLIENTINTERFACE_H_ + +#include + +#include "network_interface.h" +#include "game_settings.h" + +#include "socket.h" + +using Shared::Platform::Ip; +using Shared::Platform::ClientSocket; +using std::vector; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class ClientInterface +// ===================================================== + +class ClientInterface: public GameNetworkInterface{ +private: + static const int messageWaitTimeout; + static const int waitSleepTime; + +private: + ClientSocket *clientSocket; + GameSettings gameSettings; + string serverName; + bool introDone; + bool launchGame; + int playerIndex; + +public: + ClientInterface(); + virtual ~ClientInterface(); + + virtual Socket* getSocket() {return clientSocket;} + virtual const Socket* getSocket() const {return clientSocket;} + + //message processing + virtual void update(); + virtual void updateLobby(); + virtual void updateKeyframe(int frameCount); + virtual void waitUntilReady(Checksum* checksum); + + // message sending + virtual void sendTextMessage(const string &text, int teamIndex); + virtual void quitGame(){} + + //misc + virtual string getNetworkStatus() const; + + //accessors + string getServerName() const {return serverName;} + bool getLaunchGame() const {return launchGame;} + bool getIntroDone() const {return introDone;} + int getPlayerIndex() const {return playerIndex;} + const GameSettings *getGameSettings() {return &gameSettings;} + + void connect(const Ip &ip, int port); + void reset(); + +private: + void waitForMessage(); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/network/connection_slot.cpp b/source/glest_game/network/connection_slot.cpp new file mode 100644 index 00000000..8d015f79 --- /dev/null +++ b/source/glest_game/network/connection_slot.cpp @@ -0,0 +1,99 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "connection_slot.h" + +#include + +#include "conversion.h" +#include "game_util.h" +#include "config.h" +#include "server_interface.h" +#include "network_message.h" +#include "leak_dumper.h" + +using namespace std; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class ClientConnection +// ===================================================== + +ConnectionSlot::ConnectionSlot(ServerInterface* serverInterface, int playerIndex){ + this->serverInterface= serverInterface; + this->playerIndex= playerIndex; + socket= NULL; + ready= false; +} + +ConnectionSlot::~ConnectionSlot(){ + close(); +} + +void ConnectionSlot::update(){ + if(socket==NULL){ + socket= serverInterface->getServerSocket()->accept(); + + //send intro message when connected + if(socket!=NULL){ + NetworkMessageIntro networkMessageIntro(getNetworkVersionString(), socket->getHostName(), playerIndex); + sendMessage(&networkMessageIntro); + } + } + else{ + if(socket->isConnected()){ + NetworkMessageType networkMessageType= getNextMessageType(); + + //process incoming commands + switch(networkMessageType){ + + case nmtInvalid: + case nmtText: + break; + + //command list + case nmtCommandList:{ + NetworkMessageCommandList networkMessageCommandList; + if(receiveMessage(&networkMessageCommandList)){ + for(int i= 0; irequestCommand(networkMessageCommandList.getCommand(i)); + } + } + } + break; + + //process intro messages + case nmtIntro:{ + NetworkMessageIntro networkMessageIntro; + if(receiveMessage(&networkMessageIntro)){ + name= networkMessageIntro.getName(); + } + } + break; + + default: + throw runtime_error("Unexpected message in connection slot: " + intToStr(networkMessageType)); + } + } + else{ + close(); + } + } +} + +void ConnectionSlot::close(){ + delete socket; + socket= NULL; +} + +}}//end namespace diff --git a/source/glest_game/network/connection_slot.h b/source/glest_game/network/connection_slot.h new file mode 100644 index 00000000..d2069b55 --- /dev/null +++ b/source/glest_game/network/connection_slot.h @@ -0,0 +1,61 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_CONNECTIONSLOT_H_ +#define _GLEST_GAME_CONNECTIONSLOT_H_ + +#include + +#include "socket.h" + +#include "network_interface.h" + +using Shared::Platform::ServerSocket; +using Shared::Platform::Socket; +using std::vector; + +namespace Glest{ namespace Game{ + +class ServerInterface; + +// ===================================================== +// class ConnectionSlot +// ===================================================== + +class ConnectionSlot: public NetworkInterface{ +private: + ServerInterface* serverInterface; + Socket* socket; + int playerIndex; + string name; + bool ready; + +public: + ConnectionSlot(ServerInterface* serverInterface, int playerIndex); + ~ConnectionSlot(); + + virtual void update(); + + void setReady() {ready= true;} + const string &getName() const {return name;} + bool isReady() const {return ready;} + +protected: + virtual Socket* getSocket() {return socket;} + virtual Socket* getSocket() const {return socket;} + +private: + void close(); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/network/network_interface.cpp b/source/glest_game/network/network_interface.cpp new file mode 100644 index 00000000..397dd7c2 --- /dev/null +++ b/source/glest_game/network/network_interface.cpp @@ -0,0 +1,77 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "network_interface.h" + +#include +#include + +#include "types.h" +#include "conversion.h" +#include "platform_util.h" + +#include "leak_dumper.h" + +using namespace Shared::Platform; +using namespace Shared::Util; +using namespace std; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class NetworkInterface +// ===================================================== + +const int NetworkInterface::readyWaitTimeout= 60000; //1 minute + + +void NetworkInterface::sendMessage(const NetworkMessage* networkMessage){ + Socket* socket= getSocket(); + + networkMessage->send(socket); +} + +NetworkMessageType NetworkInterface::getNextMessageType(){ + Socket* socket= getSocket(); + int8 messageType= nmtInvalid; + + //peek message type + if(socket->getDataToRead()>=sizeof(messageType)){ + socket->peek(&messageType, sizeof(messageType)); + } + + //sanity check new message type + if(messageType<0 || messageType>=nmtCount){ + throw runtime_error("Invalid message type: " + intToStr(messageType)); + } + + return static_cast(messageType); +} + +bool NetworkInterface::receiveMessage(NetworkMessage* networkMessage){ + Socket* socket= getSocket(); + + return networkMessage->receive(socket); +} + +bool NetworkInterface::isConnected(){ + return getSocket()!=NULL && getSocket()->isConnected(); +} + +// ===================================================== +// class GameNetworkInterface +// ===================================================== + +GameNetworkInterface::GameNetworkInterface(){ + quit= false; +} + +}}//end namespace diff --git a/source/glest_game/network/network_interface.h b/source/glest_game/network/network_interface.h new file mode 100644 index 00000000..dde0bc5c --- /dev/null +++ b/source/glest_game/network/network_interface.h @@ -0,0 +1,100 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_NETWORKINTERFACE_H_ +#define _GLEST_GAME_NETWORKINTERFACE_H_ + +#include +#include + +#include "checksum.h" +#include "network_message.h" +#include "network_types.h" + +using std::string; +using std::vector; +using Shared::Util::Checksum; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class NetworkInterface +// ===================================================== + +class NetworkInterface{ +public: + static const int readyWaitTimeout; + +public: + virtual ~NetworkInterface(){} + + virtual Socket* getSocket()= 0; + virtual const Socket* getSocket() const= 0; + + string getIp() const {return getSocket()->getIp();} + string getHostName() const {return getSocket()->getHostName();} + + void sendMessage(const NetworkMessage* networkMessage); + NetworkMessageType getNextMessageType(); + bool receiveMessage(NetworkMessage* networkMessage); + + bool isConnected(); +}; + +// ===================================================== +// class GameNetworkInterface +// +// Adds functions common to servers and clients +// but not connection slots +// ===================================================== + +class GameNetworkInterface: public NetworkInterface{ +private: + typedef vector Commands; + +protected: + Commands requestedCommands; //commands requested by the user + Commands pendingCommands; //commands ready to be given + bool quit; + string chatText; + string chatSender; + int chatTeamIndex; + +public: + GameNetworkInterface(); + + //message processimg + virtual void update()= 0; + virtual void updateLobby()= 0; + virtual void updateKeyframe(int frameCount)= 0; + virtual void waitUntilReady(Checksum* checksum)= 0; + + //message sending + virtual void sendTextMessage(const string &text, int teamIndex)= 0; + virtual void quitGame()=0; + + //misc + virtual string getNetworkStatus() const= 0; + + //access functions + void requestCommand(const NetworkCommand *networkCommand) {requestedCommands.push_back(*networkCommand);} + int getPendingCommandCount() const {return pendingCommands.size();} + const NetworkCommand* getPendingCommand(int i) const {return &pendingCommands[i];} + void clearPendingCommands() {pendingCommands.clear();} + bool getQuit() const {return quit;} + const string getChatText() const {return chatText;} + const string getChatSender() const {return chatSender;} + int getChatTeamIndex() const {return chatTeamIndex;} +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/network/network_manager.cpp b/source/glest_game/network/network_manager.cpp new file mode 100644 index 00000000..a44d3d4c --- /dev/null +++ b/source/glest_game/network/network_manager.cpp @@ -0,0 +1,78 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "network_manager.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class NetworkManager +// ===================================================== + +NetworkManager &NetworkManager::getInstance(){ + static NetworkManager networkManager; + return networkManager; +} + +NetworkManager::NetworkManager(){ + gameNetworkInterface= NULL; + networkRole= nrIdle; +} + +void NetworkManager::init(NetworkRole networkRole) +{ + assert(gameNetworkInterface==NULL); + + this->networkRole = networkRole; + + if(networkRole==nrServer){ + gameNetworkInterface = new ServerInterface(); + } + else + { + gameNetworkInterface = new ClientInterface(); + } +} + +void NetworkManager::end(){ + delete gameNetworkInterface; + gameNetworkInterface= NULL; + networkRole= nrIdle; +} + +void NetworkManager::update(){ + if(gameNetworkInterface!=NULL){ + gameNetworkInterface->update(); + } +} + +bool NetworkManager::isNetworkGame(){ + return networkRole==nrClient || getServerInterface()->getConnectedSlotCount()>0; +} + +GameNetworkInterface* NetworkManager::getGameNetworkInterface(){ + assert(gameNetworkInterface!=NULL); + return gameNetworkInterface; +} + +ServerInterface* NetworkManager::getServerInterface(){ + assert(gameNetworkInterface!=NULL); + assert(networkRole==nrServer); + return static_cast(gameNetworkInterface); +} + +ClientInterface* NetworkManager::getClientInterface(){ + assert(gameNetworkInterface!=NULL); + assert(networkRole==nrClient); + return static_cast(gameNetworkInterface); +} + +}}//end namespace diff --git a/source/glest_game/network/network_manager.h b/source/glest_game/network/network_manager.h new file mode 100644 index 00000000..5f485bf3 --- /dev/null +++ b/source/glest_game/network/network_manager.h @@ -0,0 +1,57 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_NETWORKMANAGER_H_ +#define _GLEST_GAME_NETWORKMANAGER_H_ + +#include + +#include "socket.h" +#include "checksum.h" +#include "server_interface.h" +#include "client_interface.h" + +using Shared::Util::Checksum; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class NetworkManager +// ===================================================== + +enum NetworkRole{ + nrServer, + nrClient, + nrIdle +}; + +class NetworkManager{ +private: + GameNetworkInterface* gameNetworkInterface; + NetworkRole networkRole; + +public: + static NetworkManager &getInstance(); + + NetworkManager(); + void init(NetworkRole networkRole); + void end(); + void update(); + + bool isNetworkGame(); + GameNetworkInterface* getGameNetworkInterface(); + ServerInterface* getServerInterface(); + ClientInterface* getClientInterface(); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/network/network_message.cpp b/source/glest_game/network/network_message.cpp new file mode 100644 index 00000000..657e975f --- /dev/null +++ b/source/glest_game/network/network_message.cpp @@ -0,0 +1,218 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "network_message.h" + +#include +#include + +#include "types.h" +#include "util.h" +#include "game_settings.h" + +#include "leak_dumper.h" + +using namespace Shared::Platform; +using namespace Shared::Util; +using namespace std; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class NetworkMessage +// ===================================================== + +bool NetworkMessage::receive(Socket* socket, void* data, int dataSize){ + if(socket->getDataToRead()>=dataSize){ + if(socket->receive(data, dataSize)!=dataSize){ + throw runtime_error("Error receiving NetworkMessage"); + } + return true; + } + return false; +} + +void NetworkMessage::send(Socket* socket, const void* data, int dataSize) const{ + if(socket->send(data, dataSize)!=dataSize){ + throw runtime_error("Error sending NetworkMessage"); + } +} + +// ===================================================== +// class NetworkMessageIntro +// ===================================================== + +NetworkMessageIntro::NetworkMessageIntro(){ + data.messageType= -1; + data.playerIndex= -1; +} + +NetworkMessageIntro::NetworkMessageIntro(const string &versionString, const string &name, int playerIndex){ + data.messageType=nmtIntro; + data.versionString= versionString; + data.name= name; + data.playerIndex= static_cast(playerIndex); +} + +bool NetworkMessageIntro::receive(Socket* socket){ + return NetworkMessage::receive(socket, &data, sizeof(data)); +} + +void NetworkMessageIntro::send(Socket* socket) const{ + assert(data.messageType==nmtIntro); + NetworkMessage::send(socket, &data, sizeof(data)); +} + +// ===================================================== +// class NetworkMessageReady +// ===================================================== + +NetworkMessageReady::NetworkMessageReady(){ + data.messageType= nmtReady; +} + +NetworkMessageReady::NetworkMessageReady(int32 checksum){ + data.messageType= nmtReady; + data.checksum= checksum; +} + +bool NetworkMessageReady::receive(Socket* socket){ + return NetworkMessage::receive(socket, &data, sizeof(data)); +} + +void NetworkMessageReady::send(Socket* socket) const{ + assert(data.messageType==nmtReady); + NetworkMessage::send(socket, &data, sizeof(data)); +} + +// ===================================================== +// class NetworkMessageLaunch +// ===================================================== + +NetworkMessageLaunch::NetworkMessageLaunch(){ + data.messageType=-1; +} + +NetworkMessageLaunch::NetworkMessageLaunch(const GameSettings *gameSettings){ + data.messageType=nmtLaunch; + + data.description= gameSettings->getDescription(); + data.map= gameSettings->getMap(); + data.tileset= gameSettings->getTileset(); + data.tech= gameSettings->getTech(); + data.factionCount= gameSettings->getFactionCount(); + data.thisFactionIndex= gameSettings->getThisFactionIndex(); + data.defaultResources= gameSettings->getDefaultResources(); + data.defaultUnits= gameSettings->getDefaultUnits(); + data.defaultVictoryConditions= gameSettings->getDefaultVictoryConditions(); + + for(int i= 0; igetFactionTypeName(i); + data.factionControls[i]= gameSettings->getFactionControl(i); + data.teams[i]= gameSettings->getTeam(i); + data.startLocationIndex[i]= gameSettings->getStartLocationIndex(i); + } +} + +void NetworkMessageLaunch::buildGameSettings(GameSettings *gameSettings) const{ + gameSettings->setDescription(data.description.getString()); + gameSettings->setMap(data.map.getString()); + gameSettings->setTileset(data.tileset.getString()); + gameSettings->setTech(data.tech.getString()); + gameSettings->setFactionCount(data.factionCount); + gameSettings->setThisFactionIndex(data.thisFactionIndex); + gameSettings->setDefaultResources(data.defaultResources); + gameSettings->setDefaultUnits(data.defaultUnits); + gameSettings->setDefaultVictoryConditions(data.defaultVictoryConditions); + + for(int i= 0; isetFactionTypeName(i, data.factionTypeNames[i].getString()); + gameSettings->setFactionControl(i, static_cast(data.factionControls[i])); + gameSettings->setTeam(i, data.teams[i]); + gameSettings->setStartLocationIndex(i, data.startLocationIndex[i]); + } +} + +bool NetworkMessageLaunch::receive(Socket* socket){ + return NetworkMessage::receive(socket, &data, sizeof(data)); +} + +void NetworkMessageLaunch::send(Socket* socket) const{ + assert(data.messageType==nmtLaunch); + NetworkMessage::send(socket, &data, sizeof(data)); +} + +// ===================================================== +// class NetworkMessageLaunch +// ===================================================== + +NetworkMessageCommandList::NetworkMessageCommandList(int32 frameCount){ + data.messageType= nmtCommandList; + data.frameCount= frameCount; + data.commandCount= 0; +} + +bool NetworkMessageCommandList::addCommand(const NetworkCommand* networkCommand){ + if(data.commandCount(data.commandCount)]= *networkCommand; + data.commandCount++; + return true; + } + return false; +} + +bool NetworkMessageCommandList::receive(Socket* socket){ + return NetworkMessage::receive(socket, &data, sizeof(data)); +} + +void NetworkMessageCommandList::send(Socket* socket) const{ + assert(data.messageType==nmtCommandList); + NetworkMessage::send(socket, &data, sizeof(data)); +} + +// ===================================================== +// class NetworkMessageText +// ===================================================== + +NetworkMessageText::NetworkMessageText(const string &text, const string &sender, int teamIndex){ + data.messageType= nmtText; + data.text= text; + data.sender= sender; + data.teamIndex= teamIndex; +} + +bool NetworkMessageText::receive(Socket* socket){ + return NetworkMessage::receive(socket, &data, sizeof(data)); +} + +void NetworkMessageText::send(Socket* socket) const{ + assert(data.messageType==nmtText); + NetworkMessage::send(socket, &data, sizeof(data)); +} + +// ===================================================== +// class NetworkMessageQuit +// ===================================================== + +NetworkMessageQuit::NetworkMessageQuit(){ + data.messageType= nmtQuit; +} + +bool NetworkMessageQuit::receive(Socket* socket){ + return NetworkMessage::receive(socket, &data, sizeof(data)); +} + +void NetworkMessageQuit::send(Socket* socket) const{ + assert(data.messageType==nmtQuit); + NetworkMessage::send(socket, &data, sizeof(data)); +} + +}}//end namespace diff --git a/source/glest_game/network/network_message.h b/source/glest_game/network/network_message.h new file mode 100644 index 00000000..4251b33c --- /dev/null +++ b/source/glest_game/network/network_message.h @@ -0,0 +1,252 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_NETWORKMESSAGE_H_ +#define _GLEST_GAME_NETWORKMESSAGE_H_ + +#include "socket.h" +#include "game_constants.h" +#include "network_types.h" + +using Shared::Platform::Socket; +using Shared::Platform::int8; +using Shared::Platform::int16; + +namespace Glest{ namespace Game{ + +class GameSettings; + +enum NetworkMessageType{ + nmtInvalid, + nmtIntro, + nmtPing, + nmtReady, + nmtLaunch, + nmtCommandList, + nmtText, + nmtQuit, + + nmtCount +}; + +// ===================================================== +// class NetworkMessage +// ===================================================== + +class NetworkMessage{ +public: + virtual ~NetworkMessage(){} + virtual bool receive(Socket* socket)= 0; + virtual void send(Socket* socket) const = 0; + +protected: + bool receive(Socket* socket, void* data, int dataSize); + void send(Socket* socket, const void* data, int dataSize) const; +}; + +// ===================================================== +// class NetworkMessageIntro +// +// Message sent from the server to the client +// when the client connects and vice versa +// ===================================================== + +class NetworkMessageIntro: public NetworkMessage{ +private: + static const int maxVersionStringSize= 64; + static const int maxNameSize= 16; + +private: + struct Data{ + int8 messageType; + NetworkString versionString; + NetworkString name; + int16 playerIndex; + }; + +private: + Data data; + +public: + NetworkMessageIntro(); + NetworkMessageIntro(const string &versionString, const string &name, int playerIndex); + + string getVersionString() const {return data.versionString.getString();} + string getName() const {return data.name.getString();} + int getPlayerIndex() const {return data.playerIndex;} + + virtual bool receive(Socket* socket); + virtual void send(Socket* socket) const; +}; + +// ===================================================== +// class NetworkMessageReady +// +// Message sent at the beggining of the game +// ===================================================== + +class NetworkMessageReady: public NetworkMessage{ +private: + struct Data{ + int8 messageType; + int32 checksum; + }; + +private: + Data data; + +public: + NetworkMessageReady(); + NetworkMessageReady(int32 checksum); + + int32 getChecksum() const {return data.checksum;} + + virtual bool receive(Socket* socket); + virtual void send(Socket* socket) const; +}; + +// ===================================================== +// class NetworkMessageLaunch +// +// Message sent from the server to the client +// to launch the game +// ===================================================== + +class NetworkMessageLaunch: public NetworkMessage{ +private: + static const int maxStringSize= 256; + +private: + struct Data{ + int8 messageType; + NetworkString description; + NetworkString map; + NetworkString tileset; + NetworkString tech; + NetworkString factionTypeNames[GameConstants::maxPlayers]; //faction names + + int8 factionControls[GameConstants::maxPlayers]; + + int8 thisFactionIndex; + int8 factionCount; + int8 teams[GameConstants::maxPlayers]; + int8 startLocationIndex[GameConstants::maxPlayers]; + int8 defaultResources; + int8 defaultUnits; + int8 defaultVictoryConditions; + }; + +private: + Data data; + +public: + NetworkMessageLaunch(); + NetworkMessageLaunch(const GameSettings *gameSettings); + + void buildGameSettings(GameSettings *gameSettings) const; + + virtual bool receive(Socket* socket); + virtual void send(Socket* socket) const; +}; + +// ===================================================== +// class CommandList +// +// Message to order a commands to several units +// ===================================================== + +class NetworkMessageCommandList: public NetworkMessage{ +private: + static const int maxCommandCount= 16*4; + +private: + struct Data{ + int8 messageType; + int8 commandCount; + int32 frameCount; + NetworkCommand commands[maxCommandCount]; + }; + +private: + Data data; + +public: + NetworkMessageCommandList(int32 frameCount= -1); + + bool addCommand(const NetworkCommand* networkCommand); + + void clear() {data.commandCount= 0;} + int getCommandCount() const {return data.commandCount;} + int getFrameCount() const {return data.frameCount;} + const NetworkCommand* getCommand(int i) const {return &data.commands[i];} + + virtual bool receive(Socket* socket); + virtual void send(Socket* socket) const; +}; + +// ===================================================== +// class NetworkMessageText +// +// Chat text message +// ===================================================== + +class NetworkMessageText: public NetworkMessage{ +private: + static const int maxStringSize= 64; + +private: + struct Data{ + int8 messageType; + NetworkString text; + NetworkString sender; + int8 teamIndex; + }; + +private: + Data data; + +public: + NetworkMessageText(){} + NetworkMessageText(const string &text, const string &sender, int teamIndex); + + string getText() const {return data.text.getString();} + string getSender() const {return data.sender.getString();} + int getTeamIndex() const {return data.teamIndex;} + + virtual bool receive(Socket* socket); + virtual void send(Socket* socket) const; +}; + +// ===================================================== +// class NetworkMessageQuit +// +// Message sent at the beggining of the game +// ===================================================== + +class NetworkMessageQuit: public NetworkMessage{ +private: + struct Data{ + int8 messageType; + }; + +private: + Data data; + +public: + NetworkMessageQuit(); + + virtual bool receive(Socket* socket); + virtual void send(Socket* socket) const; +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/network/network_types.cpp b/source/glest_game/network/network_types.cpp new file mode 100644 index 00000000..6e7dea2e --- /dev/null +++ b/source/glest_game/network/network_types.cpp @@ -0,0 +1,33 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "network_types.h" + +#include "leak_dumper.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class NetworkCommand +// ===================================================== + +NetworkCommand::NetworkCommand(int networkCommandType, int unitId, int commandTypeId, const Vec2i &pos, int unitTypeId, int targetId){ + this->networkCommandType= networkCommandType; + this->unitId= unitId; + this->commandTypeId= commandTypeId; + this->positionX= pos.x; + this->positionY= pos.y; + this->unitTypeId= unitTypeId; + this->targetId= targetId; +} + + +}}//end namespace diff --git a/source/glest_game/network/network_types.h b/source/glest_game/network/network_types.h new file mode 100644 index 00000000..ca5405a1 --- /dev/null +++ b/source/glest_game/network/network_types.h @@ -0,0 +1,77 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_NETWORKTYPES_H_ +#define _GLEST_GAME_NETWORKTYPES_H_ + +#include + +#include "types.h" +#include "vec.h" + +using std::string; +using Shared::Platform::int8; +using Shared::Platform::int16; +using Shared::Platform::int32; +using Shared::Graphics::Vec2i; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class NetworkString +// ===================================================== + +template +class NetworkString{ +private: + char buffer[S]; + +public: + NetworkString() {memset(buffer, 0, S);} + void operator=(const string& str) {strncpy(buffer, str.c_str(), S-1);} + string getString() const {return buffer;} +}; + +// ===================================================== +// class NetworkCommand +// ===================================================== + +enum NetworkCommandType{ + nctGiveCommand, + nctCancelCommand, + nctSetMeetingPoint +}; + +class NetworkCommand{ +private: + int16 networkCommandType; + int16 unitId; + int16 commandTypeId; + int16 positionX; + int16 positionY; + int16 unitTypeId; + int16 targetId; + +public: + NetworkCommand(){}; + NetworkCommand(int networkCommandType, int unitId, int commandTypeId= -1, const Vec2i &pos= Vec2i(0), int unitTypeId= -1, int targetId= -1); + + NetworkCommandType getNetworkCommandType() const {return static_cast(networkCommandType);} + int getUnitId() const {return unitId;} + int getCommandTypeId() const {return commandTypeId;} + Vec2i getPosition() const {return Vec2i(positionX, positionY);} + int getUnitTypeId() const {return unitTypeId;} + int getTargetId() const {return targetId;} +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/network/server_interface.cpp b/source/glest_game/network/server_interface.cpp new file mode 100644 index 00000000..6a77ce04 --- /dev/null +++ b/source/glest_game/network/server_interface.cpp @@ -0,0 +1,244 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "server_interface.h" + +#include +#include + +#include "platform_util.h" +#include "conversion.h" +#include "config.h" +#include "lang.h" + +#include "leak_dumper.h" + +using namespace std; +using namespace Shared::Platform; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class ServerInterface +// ===================================================== + +ServerInterface::ServerInterface(){ + for(int i= 0; i=0 && playerIndexupdate(); + } + } + + //process text messages + chatText.clear(); + chatSender.clear(); + chatTeamIndex= -1; + + for(int i= 0; iisConnected()){ + if(connectionSlot->getNextMessageType()==nmtText){ + NetworkMessageText networkMessageText; + if(connectionSlot->receiveMessage(&networkMessageText)){ + broadcastMessage(&networkMessageText, i); + chatText= networkMessageText.getText(); + chatSender= networkMessageText.getSender(); + chatTeamIndex= networkMessageText.getTeamIndex(); + break; + } + } + } + } + } +} + +void ServerInterface::updateKeyframe(int frameCount){ + + NetworkMessageCommandList networkMessageCommandList(frameCount); + + //build command list, remove commands from requested and add to pending + while(!requestedCommands.empty()){ + if(networkMessageCommandList.addCommand(&requestedCommands.back())){ + pendingCommands.push_back(requestedCommands.back()); + requestedCommands.pop_back(); + } + else{ + break; + } + } + + //broadcast commands + broadcastMessage(&networkMessageCommandList); +} + +void ServerInterface::waitUntilReady(Checksum* checksum){ + + Chrono chrono; + bool allReady= false; + + chrono.start(); + + //wait until we get a ready message from all clients + while(!allReady){ + + allReady= true; + for(int i= 0; iisReady()){ + NetworkMessageType networkMessageType= connectionSlot->getNextMessageType(); + NetworkMessageReady networkMessageReady; + + if(networkMessageType==nmtReady && connectionSlot->receiveMessage(&networkMessageReady)){ + connectionSlot->setReady(); + } + else if(networkMessageType!=nmtInvalid){ + throw runtime_error("Unexpected network message: " + intToStr(networkMessageType)); + } + + allReady= false; + } + } + } + + //check for timeout + if(chrono.getMillis()>readyWaitTimeout){ + throw runtime_error("Timeout waiting for clients"); + } + } + + //send ready message after, so clients start delayed + for(int i= 0; igetSum()); + ConnectionSlot* connectionSlot= slots[i]; + + if(connectionSlot!=NULL){ + connectionSlot->sendMessage(&networkMessageReady); + } + } +} + +void ServerInterface::sendTextMessage(const string &text, int teamIndex){ + NetworkMessageText networkMessageText(text, getHostName(), teamIndex); + broadcastMessage(&networkMessageText); +} + +void ServerInterface::quitGame(){ + NetworkMessageQuit networkMessageQuit; + broadcastMessage(&networkMessageQuit); +} + +string ServerInterface::getNetworkStatus() const{ + Lang &lang= Lang::getInstance(); + string str; + + for(int i= 0; iisConnected()){ + str+= connectionSlot->getName(); + } + } + else + { + str+= lang.get("NotConnected"); + } + + str+= '\n'; + } + return str; +} + +void ServerInterface::launchGame(const GameSettings* gameSettings){ + NetworkMessageLaunch networkMessageLaunch(gameSettings); + broadcastMessage(&networkMessageLaunch); +} + +void ServerInterface::broadcastMessage(const NetworkMessage* networkMessage, int excludeSlot){ + for(int i= 0; iisConnected()){ + connectionSlot->sendMessage(networkMessage); + } + else{ + removeSlot(i); + } + } + } +} + +void ServerInterface::updateListen(){ + int openSlotCount= 0; + + for(int i= 0; iisConnected()){ + ++openSlotCount; + } + } + + serverSocket.listen(openSlotCount); +} + +}}//end namespace diff --git a/source/glest_game/network/server_interface.h b/source/glest_game/network/server_interface.h new file mode 100644 index 00000000..88868f57 --- /dev/null +++ b/source/glest_game/network/server_interface.h @@ -0,0 +1,71 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_SERVERINTERFACE_H_ +#define _GLEST_GAME_SERVERINTERFACE_H_ + +#include + +#include "game_constants.h" +#include "network_interface.h" +#include "connection_slot.h" +#include "socket.h" + +using std::vector; +using Shared::Platform::ServerSocket; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class ServerInterface +// ===================================================== + +class ServerInterface: public GameNetworkInterface{ +private: + ConnectionSlot* slots[GameConstants::maxPlayers]; + ServerSocket serverSocket; + +public: + ServerInterface(); + virtual ~ServerInterface(); + + virtual Socket* getSocket() {return &serverSocket;} + virtual const Socket* getSocket() const {return &serverSocket;} + + //message processing + virtual void update(); + virtual void updateLobby(){}; + virtual void updateKeyframe(int frameCount); + virtual void waitUntilReady(Checksum* checksum); + + // message sending + virtual void sendTextMessage(const string &text, int teamIndex); + virtual void quitGame(); + + //misc + virtual string getNetworkStatus() const; + + ServerSocket* getServerSocket() {return &serverSocket;} + void addSlot(int playerIndex); + void removeSlot(int playerIndex); + ConnectionSlot* getSlot(int playerIndex); + int getConnectedSlotCount(); + + void launchGame(const GameSettings* gameSettings); + +private: + void broadcastMessage(const NetworkMessage* networkMessage, int excludeSlot= -1); + void updateListen(); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/sound/sound_container.cpp b/source/glest_game/sound/sound_container.cpp new file mode 100644 index 00000000..2c4925c7 --- /dev/null +++ b/source/glest_game/sound/sound_container.cpp @@ -0,0 +1,45 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "sound_container.h" + +#include "util.h" +#include "leak_dumper.h" + +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class SoundContainer +// ===================================================== + +SoundContainer::SoundContainer(){ + lastSound= -1; +} + +StaticSound *SoundContainer::getRandSound() const{ + switch(sounds.size()){ + case 0: + return NULL; + case 1: + return sounds[0]; + default: + int soundIndex= random.randRange(0, sounds.size()-1); + if(soundIndex==lastSound){ + soundIndex= (lastSound+1) % sounds.size(); + } + lastSound= soundIndex; + return sounds[soundIndex]; + } +} + +}}//end namespace diff --git a/source/glest_game/sound/sound_container.h b/source/glest_game/sound/sound_container.h new file mode 100644 index 00000000..fffa413f --- /dev/null +++ b/source/glest_game/sound/sound_container.h @@ -0,0 +1,53 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_SOUNDCONTAINER_H_ +#define _GLEST_GAME_SOUNDCONTAINER_H_ + +#include + +#include "sound.h" +#include "random.h" + +using std::vector; +using Shared::Util::Random; +using Shared::Sound::StaticSound; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class SoundContainer +// +/// Holds a list of sounds that are usually played at random +// ===================================================== + +class SoundContainer{ +public: + typedef vector Sounds; + +private: + Sounds sounds; + mutable Random random; + mutable int lastSound; + +public: + SoundContainer(); + + void resize(int size) {sounds.resize(size);} + StaticSound *&operator[](int i) {return sounds[i];} + + const Sounds &getSounds() const {return sounds;} + StaticSound *getRandSound() const; +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/sound/sound_renderer.cpp b/source/glest_game/sound/sound_renderer.cpp new file mode 100644 index 00000000..c75354b8 --- /dev/null +++ b/source/glest_game/sound/sound_renderer.cpp @@ -0,0 +1,122 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "sound_renderer.h" + +#include "core_data.h" +#include "config.h" +#include "sound_interface.h" +#include "factory_repository.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; +using namespace Shared::Sound; + +namespace Glest{ namespace Game{ + +const int SoundRenderer::ambientFade= 6000; +const float SoundRenderer::audibleDist= 50.f; + +// ===================================================== +// class SoundRenderer +// ===================================================== + +SoundRenderer::SoundRenderer(){ + loadConfig(); +} + +void SoundRenderer::init(Window *window){ + SoundInterface &si= SoundInterface::getInstance(); + FactoryRepository &fr= FactoryRepository::getInstance(); + Config &config= Config::getInstance(); + + si.setFactory(fr.getSoundFactory(config.getString("FactorySound"))); + soundPlayer= si.newSoundPlayer(); + + SoundPlayerParams soundPlayerParams; + soundPlayerParams.staticBufferCount= config.getInt("SoundStaticBuffers"); + soundPlayerParams.strBufferCount= config.getInt("SoundStreamingBuffers"); + soundPlayer->init(&soundPlayerParams); +} + +SoundRenderer::~SoundRenderer(){ + delete soundPlayer; +} + +SoundRenderer &SoundRenderer::getInstance(){ + static SoundRenderer soundRenderer; + return soundRenderer; +} + +void SoundRenderer::update(){ + soundPlayer->updateStreams(); +} + +// ======================= Music ============================ + +void SoundRenderer::playMusic(StrSound *strSound){ + strSound->setVolume(musicVolume); + strSound->restart(); + soundPlayer->play(strSound); +} + +void SoundRenderer::stopMusic(StrSound *strSound){ + soundPlayer->stop(strSound); +} + +// ======================= Fx ============================ + +void SoundRenderer::playFx(StaticSound *staticSound, Vec3f soundPos, Vec3f camPos){ + if(staticSound!=NULL){ + float d= soundPos.dist(camPos); + + if(dsetVolume(correctedVol); + soundPlayer->play(staticSound); + } + } +} + +void SoundRenderer::playFx(StaticSound *staticSound){ + if(staticSound!=NULL){ + staticSound->setVolume(fxVolume); + soundPlayer->play(staticSound); + } +} + +// ======================= Ambient ============================ + +void SoundRenderer::playAmbient(StrSound *strSound){ + strSound->setVolume(ambientVolume); + soundPlayer->play(strSound, ambientFade); +} + +void SoundRenderer::stopAmbient(StrSound *strSound){ + soundPlayer->stop(strSound, ambientFade); +} + +// ======================= Misc ============================ + +void SoundRenderer::stopAllSounds(){ + soundPlayer->stopAllSounds(); +} + +void SoundRenderer::loadConfig(){ + Config &config= Config::getInstance(); + + fxVolume= config.getInt("SoundVolumeFx")/100.f; + musicVolume= config.getInt("SoundVolumeMusic")/100.f; + ambientVolume= config.getInt("SoundVolumeAmbient")/100.f; +} + +}}//end namespace diff --git a/source/glest_game/sound/sound_renderer.h b/source/glest_game/sound/sound_renderer.h new file mode 100644 index 00000000..7ad56783 --- /dev/null +++ b/source/glest_game/sound/sound_renderer.h @@ -0,0 +1,76 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_SOUNDRENDERER_H_ +#define _GLEST_GAME_SOUNDRENDERER_H_ + +#include "sound.h" +#include "sound_player.h" +#include "window.h" +#include "vec.h" + +namespace Glest{ namespace Game{ + +using Shared::Sound::StrSound; +using Shared::Sound::StaticSound; +using Shared::Sound::SoundPlayer; +using Shared::Graphics::Vec3f; + +// ===================================================== +// class SoundRenderer +// +/// Wrapper to acces the shared library sound engine +// ===================================================== + +class SoundRenderer{ +public: + static const int ambientFade; + static const float audibleDist; +private: + SoundPlayer *soundPlayer; + + //volume + float fxVolume; + float musicVolume; + float ambientVolume; + +private: + SoundRenderer(); + +public: + //misc + ~SoundRenderer(); + static SoundRenderer &getInstance(); + void init(Window *window); + void update(); + SoundPlayer *getSoundPlayer() const {return soundPlayer;} + + //music + void playMusic(StrSound *strSound); + void stopMusic(StrSound *strSound); + + //fx + void playFx(StaticSound *staticSound, Vec3f soundPos, Vec3f camPos); + void playFx(StaticSound *staticSound); + + //ambient + //void playAmbient(StaticSound *staticSound); + void playAmbient(StrSound *strSound); + void stopAmbient(StrSound *strSound); + + //misc + void stopAllSounds(); + void loadConfig(); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/type_instances/command.cpp b/source/glest_game/type_instances/command.cpp new file mode 100644 index 00000000..98da5582 --- /dev/null +++ b/source/glest_game/type_instances/command.cpp @@ -0,0 +1,60 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "command.h" + +#include "command_type.h" +#include "leak_dumper.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Command +// ===================================================== + +Command::Command(const CommandType *ct, const Vec2i &pos){ + this->commandType= ct; + this->pos= pos; + unitType= NULL; +} + +Command::Command(const CommandType *ct, Unit* unit){ + this->commandType= ct; + this->pos= Vec2i(0); + this->unitRef= unit; + unitType= NULL; + if(unit!=NULL){ + unit->resetHighlight(); + pos= unit->getCellPos(); + } +} + +Command::Command(const CommandType *ct, const Vec2i &pos, const UnitType *unitType){ + this->commandType= ct; + this->pos= pos; + this->unitType= unitType; +} + +// =============== set =============== + +void Command::setCommandType(const CommandType *commandType){ + this->commandType= commandType; +} + +void Command::setPos(const Vec2i &pos){ + this->pos= pos; +} + +void Command::setUnit(Unit *unit){ + this->unitRef= unit; +} + +}}//end namespace diff --git a/source/glest_game/type_instances/command.h b/source/glest_game/type_instances/command.h new file mode 100644 index 00000000..b1a86939 --- /dev/null +++ b/source/glest_game/type_instances/command.h @@ -0,0 +1,59 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_COMMAND_H_ +#define _GLEST_GAME_COMMAND_H_ + +#include + +#include "unit.h" +#include "vec.h" + +namespace Glest{ namespace Game{ + +using Shared::Graphics::Vec2i; + +class CommandType; + +// ===================================================== +// class Command +// +/// A unit command +// ===================================================== + +class Command{ +private: + const CommandType *commandType; + Vec2i pos; + UnitReference unitRef; //target unit, used to move and attack optinally + const UnitType *unitType; //used for build + +public: + //constructor + Command(const CommandType *ct, const Vec2i &pos=Vec2i(0)); + Command(const CommandType *ct, Unit *unit); + Command(const CommandType *ct, const Vec2i &pos, const UnitType *unitType); + + //get + const CommandType *getCommandType() const {return commandType;} + Vec2i getPos() const {return pos;} + Unit* getUnit() const {return unitRef.getUnit();} + const UnitType* getUnitType() const {return unitType;} + + //set + void setCommandType(const CommandType *commandType); + void setPos(const Vec2i &pos); + void setUnit(Unit *unit); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/type_instances/faction.cpp b/source/glest_game/type_instances/faction.cpp new file mode 100644 index 00000000..8e2a4502 --- /dev/null +++ b/source/glest_game/type_instances/faction.cpp @@ -0,0 +1,419 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "faction.h" + +#include +#include + +#include "resource_type.h" +#include "unit.h" +#include "util.h" +#include "sound_renderer.h" +#include "renderer.h" +#include "tech_tree.h" +#include "leak_dumper.h" + +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Faction +// ===================================================== + +void Faction::init( + const FactionType *factionType, ControlType control, TechTree *techTree, + int factionIndex, int teamIndex, int startLocationIndex, bool thisFaction, bool giveResources) +{ + this->control= control; + this->factionType= factionType; + this->startLocationIndex= startLocationIndex; + this->index= factionIndex; + this->teamIndex= teamIndex; + this->thisFaction= thisFaction; + + resources.resize(techTree->getResourceTypeCount()); + store.resize(techTree->getResourceTypeCount()); + for(int i=0; igetResourceTypeCount(); ++i){ + const ResourceType *rt= techTree->getResourceType(i); + int resourceAmount= giveResources? factionType->getStartingResourceAmount(rt): 0; + resources[i].init(rt, resourceAmount); + store[i].init(rt, 0); + } + + texture= Renderer::getInstance().newTexture2D(rsGame); + texture->load("data/core/faction_textures/faction"+intToStr(index)+".tga"); +} + +void Faction::end(){ + deleteValues(units.begin(), units.end()); +} + +// ================== get ================== + +const Resource *Faction::getResource(const ResourceType *rt) const{ + for(int i=0; iapplyUpgrade(ut); + } +} + +// ==================== reqs ==================== + +//checks if all required units and upgrades are present +bool Faction::reqsOk(const RequirableType *rt) const{ + + //required units + for(int i=0; igetUnitReqCount(); ++i){ + bool found=false; + for(int j=0; jgetType(); + if(rt->getUnitReq(i)==ut && unit->isOperative()){ + found= true; + break; + } + } + if(!found){ + return false; + } + } + + //required upgrades + for(int i=0; igetUpgradeReqCount(); ++i){ + if(!upgradeManager.isUpgraded(rt->getUpgradeReq(i))){ + return false; + } + } + return true; + +} + +bool Faction::reqsOk(const CommandType *ct) const{ + + if(ct->getProduced()!=NULL && !reqsOk(ct->getProduced())){ + return false; + } + + if(ct->getClass()==ccUpgrade){ + const UpgradeCommandType *uct= static_cast(ct); + if(upgradeManager.isUpgradingOrUpgraded(uct->getProducedUpgrade())){ + return false; + } + } + + return reqsOk(static_cast(ct)); +} + +// ================== cost application ================== + +//apply costs except static production (start building/production) +bool Faction::applyCosts(const ProducibleType *p){ + + if(!checkCosts(p)){ + return false; + } + + //for each unit cost spend it + //pass 2, decrease resources, except negative static costs (ie: farms) + for(int i=0; igetCostCount(); ++i){ + const ResourceType *rt= p->getCost(i)->getType(); + int cost= p->getCost(i)->getAmount(); + if((cost>0 || rt->getClass()!=rcStatic) && rt->getClass()!=rcConsumable){ + incResourceAmount(rt, -(cost)); + } + + } + return true; +} + +//apply discount (when a morph ends) +void Faction::applyDiscount(const ProducibleType *p, int discount){ + //increase resources + for(int i=0; igetCostCount(); ++i){ + const ResourceType *rt= p->getCost(i)->getType(); + int cost= p->getCost(i)->getAmount(); + if((cost>0 || rt->getClass()!=rcStatic) && rt->getClass()!=rcConsumable){ + incResourceAmount(rt, cost*discount/100); + } + } +} + +//apply static production (for starting units) +void Faction::applyStaticCosts(const ProducibleType *p){ + + //decrease static resources + for(int i=0; igetCostCount(); ++i){ + const ResourceType *rt= p->getCost(i)->getType(); + if(rt->getClass()==rcStatic){ + int cost= p->getCost(i)->getAmount(); + if(cost>0){ + incResourceAmount(rt, -cost); + } + } + } +} + +//apply static production (when a mana source is done) +void Faction::applyStaticProduction(const ProducibleType *p){ + + //decrease static resources + for(int i=0; igetCostCount(); ++i){ + const ResourceType *rt= p->getCost(i)->getType(); + if(rt->getClass()==rcStatic){ + int cost= p->getCost(i)->getAmount(); + if(cost<0){ + incResourceAmount(rt, -cost); + } + } + } +} + +//deapply all costs except static production (usually when a building is cancelled) +void Faction::deApplyCosts(const ProducibleType *p){ + + //increase resources + for(int i=0; igetCostCount(); ++i){ + const ResourceType *rt= p->getCost(i)->getType(); + int cost= p->getCost(i)->getAmount(); + if((cost>0 || rt->getClass()!=rcStatic) && rt->getClass()!=rcConsumable){ + incResourceAmount(rt, cost); + } + + } +} + +//deapply static costs (usually when a unit dies) +void Faction::deApplyStaticCosts(const ProducibleType *p){ + + //decrease resources + for(int i=0; igetCostCount(); ++i){ + const ResourceType *rt= p->getCost(i)->getType(); + if(rt->getClass()==rcStatic){ + int cost= p->getCost(i)->getAmount(); + incResourceAmount(rt, cost); + } + } +} + +//deapply static costs, but not negative costs, for when building gets killed +void Faction::deApplyStaticConsumption(const ProducibleType *p){ + + //decrease resources + for(int i=0; igetCostCount(); ++i){ + const ResourceType *rt= p->getCost(i)->getType(); + if(rt->getClass()==rcStatic){ + int cost= p->getCost(i)->getAmount(); + if(cost>0){ + incResourceAmount(rt, cost); + } + } + } +} + +//apply resource on interval (cosumable resouces) +void Faction::applyCostsOnInterval(){ + + //increment consumables + for(int j=0; jisOperative()){ + for(int k=0; kgetType()->getCostCount(); ++k){ + const Resource *resource= unit->getType()->getCost(k); + if(resource->getType()->getClass()==rcConsumable && resource->getAmount()<0){ + incResourceAmount(resource->getType(), -resource->getAmount()); + } + } + } + } + + //decrement consumables + for(int j=0; jisOperative()){ + for(int k=0; kgetType()->getCostCount(); ++k){ + const Resource *resource= unit->getType()->getCost(k); + if(resource->getType()->getClass()==rcConsumable && resource->getAmount()>0){ + incResourceAmount(resource->getType(), -resource->getAmount()); + + //decrease unit hp + if(getResource(resource->getType())->getAmount()<0){ + resetResourceAmount(resource->getType()); + unit->decHp(unit->getType()->getMaxHp()/3); + StaticSound *sound= unit->getType()->getFirstStOfClass(scDie)->getSound(); + if(sound!=NULL && thisFaction){ + SoundRenderer::getInstance().playFx(sound); + } + } + } + } + } + } +} + +bool Faction::checkCosts(const ProducibleType *pt){ + + //for each unit cost check if enough resources + for(int i=0; igetCostCount(); ++i){ + const ResourceType *rt= pt->getCost(i)->getType(); + int cost= pt->getCost(i)->getAmount(); + if(cost>0){ + int available= getResource(rt)->getAmount(); + if(cost>available){ + return false; + } + } + } + + return true; +} + +// ================== diplomacy ================== + +bool Faction::isAlly(const Faction *faction){ + return teamIndex==faction->getTeam(); +} + +// ================== misc ================== + +void Faction::incResourceAmount(const ResourceType *rt, int amount){ + for(int i=0; igetType()==rt){ + r->setAmount(r->getAmount()+amount); + if(r->getType()->getClass()!=rcStatic && r->getAmount()>getStoreAmount(rt)){ + r->setAmount(getStoreAmount(rt)); + } + return; + } + } + assert(false); +} + +void Faction::setResourceBalance(const ResourceType *rt, int balance){ + for(int i=0; igetType()==rt){ + r->setBalance(balance); + return; + } + } + assert(false); +} + +Unit *Faction::findUnit(int id){ + UnitMap::iterator it= unitMap.find(id); + + if(it==unitMap.end()){ + return NULL; + } + return it->second; +} + +void Faction::addUnit(Unit *unit){ + units.push_back(unit); + unitMap.insert(make_pair(unit->getId(), unit)); +} + +void Faction::removeUnit(Unit *unit){ + for(int i=0; igetId()); + assert(units.size()==unitMap.size()); + return; + } + } + assert(false); +} + +void Faction::addStore(const UnitType *unitType){ + for(int i=0; igetStoredResourceCount(); ++i){ + const Resource *r= unitType->getStoredResource(i); + for(int j=0; jgetType() == r->getType()){ + storedResource->setAmount(storedResource->getAmount() + r->getAmount()); + } + } + } +} + +void Faction::removeStore(const UnitType *unitType){ + for(int i=0; igetStoredResourceCount(); ++i){ + const Resource *r= unitType->getStoredResource(i); + for(int j=0; jgetType() == r->getType()){ + storedResource->setAmount(storedResource->getAmount() - r->getAmount()); + } + } + } + limitResourcesToStore(); +} + +void Faction::limitResourcesToStore(){ + for(int i=0; igetType()->getClass()!=rcStatic && r->getAmount()>s->getAmount()){ + r->setAmount(s->getAmount()); + } + } +} + +void Faction::resetResourceAmount(const ResourceType *rt){ + for(int i=0; i +#include + +#include "upgrade.h" +#include "texture.h" +#include "resource.h" +#include "game_constants.h" + +using std::map; +using std::vector; + +using Shared::Graphics::Texture2D; + +namespace Glest{ namespace Game{ + +class Unit; +class TechTree; +class FactionType; +class ProducibleType; +class RequirableType; +class CommandType; +class UnitType; + +// ===================================================== +// class Faction +// +/// Each of the game players +// ===================================================== + +class Faction{ +private: + typedef vector Resources; + typedef vector Store; + typedef vector Allies; + typedef vector Units; + typedef map UnitMap; + +private: + UpgradeManager upgradeManager; + + Resources resources; + Store store; + Allies allies; + Units units; + UnitMap unitMap; + + ControlType control; + + Texture2D *texture; + const FactionType *factionType; + + int index; + int teamIndex; + int startLocationIndex; + + bool thisFaction; + +public: + void init( + const FactionType *factionType, ControlType control, TechTree *techTree, + int factionIndex, int teamIndex, int startLocationIndex, bool thisFaction, bool giveResources); + void end(); + + //get + const Resource *getResource(const ResourceType *rt) const; + const Resource *getResource(int i) const {return &resources[i];} + int getStoreAmount(const ResourceType *rt) const; + const FactionType *getType() const {return factionType;} + int getIndex() const {return index;} + int getTeam() const {return teamIndex;} + bool getCpuControl() const; + bool getCpuEasyControl() const {return control==ctCpuEasy;} + bool getCpuUltraControl() const {return control==ctCpuUltra;} + bool getCpuMegaControl() const {return control==ctCpuMega;} + ControlType getControlType() const {return control;} + Unit *getUnit(int i) const {return units[i];} + int getUnitCount() const {return units.size();} + const UpgradeManager *getUpgradeManager() const {return &upgradeManager;} + const Texture2D *getTexture() const {return texture;} + int getStartLocationIndex() const {return startLocationIndex;} + + //upgrades + void startUpgrade(const UpgradeType *ut); + void cancelUpgrade(const UpgradeType *ut); + void finishUpgrade(const UpgradeType *ut); + + //cost application + bool applyCosts(const ProducibleType *p); + void applyDiscount(const ProducibleType *p, int discount); + void applyStaticCosts(const ProducibleType *p); + void applyStaticProduction(const ProducibleType *p); + void deApplyCosts(const ProducibleType *p); + void deApplyStaticCosts(const ProducibleType *p); + void deApplyStaticConsumption(const ProducibleType *p); + void applyCostsOnInterval(); + bool checkCosts(const ProducibleType *pt); + + //reqs + bool reqsOk(const RequirableType *rt) const; + bool reqsOk(const CommandType *ct) const; + + //diplomacy + bool isAlly(const Faction *faction); + + //other + Unit *findUnit(int id); + void addUnit(Unit *unit); + void removeUnit(Unit *unit); + void addStore(const UnitType *unitType); + void removeStore(const UnitType *unitType); + + //resources + void incResourceAmount(const ResourceType *rt, int amount); + void setResourceBalance(const ResourceType *rt, int balance); + +private: + void limitResourcesToStore(); + void resetResourceAmount(const ResourceType *rt); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/type_instances/object.cpp b/source/glest_game/type_instances/object.cpp new file mode 100644 index 00000000..437b89b2 --- /dev/null +++ b/source/glest_game/type_instances/object.cpp @@ -0,0 +1,64 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "object.h" + +#include "faction_type.h" +#include "tech_tree.h" +#include "resource.h" +#include "upgrade.h" +#include "object_type.h" +#include "resource.h" +#include "util.h" +#include "random.h" +#include "leak_dumper.h" + +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + + +// ===================================================== +// class Object +// ===================================================== + +Object::Object(ObjectType *objectType, const Vec3f &pos){ + Random random; + + random.init(static_cast(pos.x*pos.z)); + + this->objectType= objectType; + resource= NULL; + this->pos= pos + Vec3f(random.randRange(-0.6f, 0.6f), 0.0f, random.randRange(-0.6f, 0.6f)); + rotation= random.randRange(0.f, 360.f); + if(objectType!=NULL){ + variation = random.randRange(0, objectType->getModelCount()-1); + } +} + +Object::~Object(){ + delete resource; +} + +const Model *Object::getModel() const{ + return objectType==NULL? resource->getType()->getModel(): objectType->getModel(variation); +} + +bool Object::getWalkable() const{ + return objectType==NULL? false: objectType->getWalkable(); +} + +void Object::setResource(const ResourceType *resourceType, const Vec2i &pos){ + resource= new Resource(); + resource->init(resourceType, pos); +} + +}}//end namespace diff --git a/source/glest_game/type_instances/object.h b/source/glest_game/type_instances/object.h new file mode 100644 index 00000000..ce1c3c7d --- /dev/null +++ b/source/glest_game/type_instances/object.h @@ -0,0 +1,59 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== +#ifndef _GLEST_GAME_OBJECT_H_ +#define _GLEST_GAME_OBJECT_H_ + +#include "model.h" +#include "vec.h" + +namespace Glest{ namespace Game{ + +class ObjectType; +class ResourceType; +class Resource; + +using Shared::Graphics::Model; +using Shared::Graphics::Vec2i; +using Shared::Graphics::Vec3f; + +// ===================================================== +// class Object +// +/// A map object: tree, stone... +// ===================================================== + +class Object{ +private: + ObjectType *objectType; + Resource *resource; + Vec3f pos; + float rotation; + int variation; + +public: + Object(ObjectType *objectType, const Vec3f &pos); + ~Object(); + + void setHeight(float height) {pos.y= height;} + + const ObjectType *getType() const {return objectType;} + Resource *getResource() const {return resource;} + Vec3f getPos() const {return pos;} + float getRotation() {return rotation;} + const Model *getModel() const; + bool getWalkable() const; + + void setResource(const ResourceType *resourceType, const Vec2i &pos); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/type_instances/resource.cpp b/source/glest_game/type_instances/resource.cpp new file mode 100644 index 00000000..9c9b471f --- /dev/null +++ b/source/glest_game/type_instances/resource.cpp @@ -0,0 +1,59 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "resource.h" + +#include "conversion.h" +#include "resource_type.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Resource +// ===================================================== + +void Resource::init(const ResourceType *rt, int amount){ + this->type= rt; + this->amount= amount; + pos= Vec2i(0); + balance= 0; +} + +void Resource::init(const ResourceType *rt, const Vec2i &pos){ + this->type=rt; + amount=rt->getDefResPerPatch(); + this->pos= pos; +} + +string Resource::getDescription() const{ + string str; + + str+= type->getName(); + str+="\n"; + str+= intToStr(amount); + str+="/"; + str+= intToStr(type->getDefResPerPatch()); + + return str; +} + +bool Resource::decAmount(int i){ + amount-= i; + if(amount>0) + return false; + return true; +} + +}}//end namespace diff --git a/source/glest_game/type_instances/resource.h b/source/glest_game/type_instances/resource.h new file mode 100644 index 00000000..c409d75a --- /dev/null +++ b/source/glest_game/type_instances/resource.h @@ -0,0 +1,57 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== +#ifndef _GLEST_GAME_RESOURCE_H_ +#define _GLEST_GAME_RESOURCE_H_ + +#include + +#include "vec.h" + +using std::string; + +namespace Glest{ namespace Game{ + +using Shared::Graphics::Vec2i; + +class ResourceType; + +// ===================================================== +// class Resource +// +/// Amount of a given ResourceType +// ===================================================== + +class Resource{ +private: + int amount; + const ResourceType *type; + Vec2i pos; + int balance; + +public: + void init(const ResourceType *rt, int amount); + void init(const ResourceType *rt, const Vec2i &pos); + + int getAmount() const {return amount;} + const ResourceType * getType() const {return type;} + Vec2i getPos() const {return pos;} + int getBalance() const {return balance;} + string getDescription() const; + + void setAmount(int amount) {this->amount= amount;} + void setBalance(int balance) {this->balance= balance;} + + bool decAmount(int i); +}; + +}}// end namespace + +#endif diff --git a/source/glest_game/type_instances/unit.cpp b/source/glest_game/type_instances/unit.cpp new file mode 100644 index 00000000..d1095cf0 --- /dev/null +++ b/source/glest_game/type_instances/unit.cpp @@ -0,0 +1,978 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== +#include "faction.h" + +#include + +#include "unit.h" +#include "world.h" +#include "upgrade.h" +#include "map.h" +#include "command.h" +#include "object.h" +#include "config.h" +#include "skill_type.h" +#include "core_data.h" +#include "renderer.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class UnitPath +// ===================================================== + +const int UnitPath::maxBlockCount= 10; + +bool UnitPath::isEmpty(){ + return pathQueue.empty(); +} + +bool UnitPath::isBlocked(){ + return blockCount>=maxBlockCount; +} + +void UnitPath::clear(){ + pathQueue.clear(); + blockCount= 0; +} + +void UnitPath::incBlockCount(){ + pathQueue.clear(); + blockCount++; +} + +void UnitPath::push(const Vec2i &path){ + pathQueue.push_back(path); +} + +Vec2i UnitPath::pop(){ + Vec2i p= pathQueue.front(); + pathQueue.erase(pathQueue.begin()); + return p; +} + +// ===================================================== +// class UnitReference +// ===================================================== + +UnitReference::UnitReference(){ + id= -1; + faction= NULL; +} + +void UnitReference::operator=(const Unit *unit){ + if(unit==NULL){ + id= -1; + faction= NULL; + } + else{ + id= unit->getId(); + faction= unit->getFaction(); + } +} + +Unit *UnitReference::getUnit() const{ + if(faction!=NULL){ + return faction->findUnit(id); + } + return NULL; +} + +// ===================================================== +// class Unit +// ===================================================== + +const float Unit::speedDivider= 100.f; +const int Unit::maxDeadCount= 500; //time in until the corpse disapears +const float Unit::highlightTime= 0.5f; +const int Unit::invalidId= -1; + +// ============================ Constructor & destructor ============================= + +Unit::Unit(int id, const Vec2i &pos, const UnitType *type, Faction *faction, Map *map){ + Random random; + + this->pos=pos; + this->type=type; + this->faction=faction; + this->map= map; + + this->id= id; + lastPos= pos; + progress= 0; + lastAnimProgress= 0; + animProgress= 0; + progress2= 0; + kills= 0; + loadCount= 0; + ep= 0; + deadCount= 0; + hp= type->getMaxHp()/20; + toBeUndertaken= false; + level= NULL; + highlight= 0.f; + meetingPos= pos; + alive= true; + + float rot= 0.f; + + random.init(id); + rot+= random.randRange(-5, 5); + + rotation= rot; + lastRotation= rot; + targetRotation= rot; + + if(getType()->getField(fAir)) currField=fAir; + if(getType()->getField(fLand)) currField=fLand; + + fire= NULL; + + computeTotalUpgrade(); + + //starting skill + currSkill= getType()->getFirstStOfClass(scStop); +} + +Unit::~Unit(){ + //remove commands + while(!commands.empty()){ + delete commands.back(); + commands.pop_back(); + } +} + +// ====================================== get ====================================== + +int Unit::getFactionIndex() const{ + return faction->getIndex(); +} + +int Unit::getTeam() const{ + return faction->getTeam(); +} + +Vec2i Unit::getCenteredPos() const{ + return pos + Vec2i(type->getSize()/2, type->getSize()/2); +} + +Vec2f Unit::getFloatCenteredPos() const{ + return Vec2f(pos.x-0.5f+type->getSize()/2.f, pos.y-0.5f+type->getSize()/2.f); +} + +Vec2i Unit::getCellPos() const{ + + if(type->hasCellMap()){ + + //find nearest pos to center that is free + Vec2i centeredPos= getCenteredPos(); + float nearestDist= -1.f; + Vec2i nearestPos= pos; + + for(int i=0; igetSize(); ++i){ + for(int j=0; jgetSize(); ++j){ + if(type->getCellMapCell(i, j)){ + Vec2i currPos= pos + Vec2i(i, j); + float dist= currPos.dist(centeredPos); + if(nearestDist==-1.f || distgetProperty(UnitType::pRotatedClimb) && currSkill->getClass()==scMove){ + float heightDiff= map->getCell(pos)->getHeight() - map->getCell(targetPos)->getHeight(); + float dist= pos.dist(targetPos); + return radToDeg(atan2(heightDiff, dist)); + }*/ + return 0.f; +} + +int Unit::getProductionPercent() const{ + if(anyCommand()){ + const ProducibleType *produced= commands.front()->getCommandType()->getProduced(); + if(produced!=NULL){ + return clamp(progress2*100/produced->getProductionTime(), 0, 100); + } + } + return -1; +} + +float Unit::getHpRatio() const{ + return clamp(static_cast(hp)/type->getTotalMaxHp(&totalUpgrade), 0.f, 1.f); +} + +float Unit::getEpRatio() const{ + if(type->getMaxHp()==0){ + return 0.f; + } + else{ + return clamp(static_cast(ep)/type->getTotalMaxEp(&totalUpgrade), 0.f, 1.f); + } +} + +const Level *Unit::getNextLevel() const{ + if(level==NULL && type->getLevelCount()>0){ + return type->getLevel(0); + } + else{ + for(int i=1; igetLevelCount(); ++i){ + if(type->getLevel(i-1)==level){ + return type->getLevel(i); + } + } + } + return NULL; +} + +string Unit::getFullName() const{ + string str; + if(level!=NULL){ + str+= level->getName() + " "; + } + str+= type->getName(); + return str; +} + +// ====================================== is ====================================== + +bool Unit::isOperative() const{ + return isAlive() && isBuilt(); +} + +bool Unit::isBeingBuilt() const{ + return currSkill->getClass()==scBeBuilt; +} + +bool Unit::isBuilt() const{ + return !isBeingBuilt(); +} + +bool Unit::isPutrefacting() const{ + return deadCount!=0; +} + +bool Unit::isAlly(const Unit *unit) const{ + return faction->isAlly(unit->getFaction()); +} + +bool Unit::isDamaged() const{ + return hp < type->getTotalMaxHp(&totalUpgrade); +} + +bool Unit::isInteresting(InterestingUnitType iut) const{ + switch(iut){ + case iutIdleHarvester: + if(type->hasCommandClass(ccHarvest)){ + if(!commands.empty()){ + const CommandType *ct= commands.front()->getCommandType(); + if(ct!=NULL){ + return ct->getClass()==ccStop; + } + } + } + return false; + + case iutBuiltBuilding: + return type->hasSkillClass(scBeBuilt) && isBuilt(); + case iutProducer: + return type->hasSkillClass(scProduce); + case iutDamaged: + return isDamaged(); + case iutStore: + return type->getStoredResourceCount()>0; + default: + return false; + } +} + +// ====================================== set ====================================== + +void Unit::setCurrSkill(const SkillType *currSkill){ + if(currSkill->getClass()!=this->currSkill->getClass()){ + animProgress= 0; + lastAnimProgress= 0; + } + progress2= 0; + this->currSkill= currSkill; +} + +void Unit::setCurrSkill(SkillClass sc){ + setCurrSkill(getType()->getFirstStOfClass(sc)); +} + +void Unit::setTarget(const Unit *unit){ + + //find a free pos in cellmap + setTargetPos(unit->getCellPos()); + + //ser field and vector + targetField= unit->getCurrField(); + targetVec= unit->getCurrVector(); + targetRef= unit; +} + +void Unit::setPos(const Vec2i &pos){ + this->lastPos= this->pos; + this->pos= pos; + this->meetingPos= pos - Vec2i(1); +} + +void Unit::setTargetPos(const Vec2i &targetPos){ + + Vec2i relPos= targetPos - pos; + Vec2f relPosf= Vec2f(relPos.x, relPos.y); + targetRotation= radToDeg(atan2(relPosf.x, relPosf.y)); + targetRef= NULL; + + this->targetPos= targetPos; +} + +// =============================== Render related ================================== + +const Model *Unit::getCurrentModel() const{ + return currSkill->getAnimation(); +} + +Vec3f Unit::getCurrVector() const{ + return getCurrVectorFlat() + Vec3f(0.f, type->getHeight()/2.f, 0.f); +} + +Vec3f Unit::getCurrVectorFlat() const{ + Vec3f v; + + float y1= computeHeight(lastPos); + float y2= computeHeight(pos); + + if(currSkill->getClass()==scMove){ + v.x= lastPos.x + progress * (pos.x-lastPos.x); + v.z= lastPos.y + progress * (pos.y-lastPos.y); + v.y= y1+progress*(y2-y1); + } + else{ + v.x= static_cast(pos.x); + v.z= static_cast(pos.y); + v.y= y2; + } + v.x+= type->getSize()/2.f-0.5f; + v.z+= type->getSize()/2.f-0.5f; + + return v; +} + +// =================== Command list related =================== + +//any command +bool Unit::anyCommand() const{ + return !commands.empty(); +} + +//return current command, assert that there is always one command +Command *Unit::getCurrCommand() const{ + assert(!commands.empty()); + return commands.front(); +} +//returns the size of the commands +unsigned int Unit::getCommandSize() const{ + return commands.size(); +} + +//give one command (clear, and push back) +CommandResult Unit::giveCommand(Command *command){ + + if(command->getCommandType()->isQueuable()){ + //cancel current command if it is not queuable + if(!commands.empty() && !commands.front()->getCommandType()->isQueuable()){ + cancelCommand(); + } + } + else{ + //empty command queue + clearCommands(); + unitPath.clear(); + } + + //check command + CommandResult result= checkCommand(command); + if(result==crSuccess){ + applyCommand(command); + } + + //push back command + if(result== crSuccess){ + commands.push_back(command); + } + else{ + delete command; + } + + return result; +} + +//pop front (used when order is done) +CommandResult Unit::finishCommand(){ + + //is empty? + if(commands.empty()){ + return crFailUndefined; + } + + //pop front + delete commands.front(); + commands.erase(commands.begin()); + unitPath.clear(); + + return crSuccess; +} + +//to cancel a command +CommandResult Unit::cancelCommand(){ + + //is empty? + if(commands.empty()){ + return crFailUndefined; + } + + //undo command + undoCommand(commands.back()); + + //delete ans pop command + delete commands.back(); + commands.pop_back(); + + //clear routes + unitPath.clear(); + + return crSuccess; +} + +// =================== route stack =================== + +void Unit::create(bool startingUnit){ + faction->addUnit(this); + map->putUnitCells(this, pos); + if(startingUnit){ + faction->applyStaticCosts(type); + } +} + +void Unit::born(){ + faction->addStore(type); + faction->applyStaticProduction(type); + setCurrSkill(scStop); + hp= type->getMaxHp(); +} + +void Unit::kill(){ + + //no longer needs static resources + if(isBeingBuilt()){ + faction->deApplyStaticConsumption(type); + } + else{ + faction->deApplyStaticCosts(type); + } + + //do the cleaning + map->clearUnitCells(this, pos); + if(!isBeingBuilt()){ + faction->removeStore(type); + } + setCurrSkill(scDie); + + notifyObservers(UnitObserver::eKill); + + //clear commands + clearCommands(); +} + +void Unit::undertake(){ + faction->removeUnit(this); +} + +// =================== Referencers =================== + +void Unit::addObserver(UnitObserver *unitObserver){ + observers.push_back(unitObserver); +} + +void Unit::removeObserver(UnitObserver *unitObserver){ + observers.remove(unitObserver); +} + +void Unit::notifyObservers(UnitObserver::Event event){ + for(Observers::iterator it= observers.begin(); it!=observers.end(); ++it){ + (*it)->unitEvent(event, this); + } +} + +// =================== Other =================== + +void Unit::resetHighlight(){ + highlight= 1.f; +} + +const CommandType *Unit::computeCommandType(const Vec2i &pos, const Unit *targetUnit) const{ + const CommandType *commandType= NULL; + SurfaceCell *sc= map->getSurfaceCell(Map::toSurfCoords(pos)); + + if(targetUnit!=NULL){ + //attack enemies + if(!isAlly(targetUnit)){ + commandType= type->getFirstAttackCommand(targetUnit->getCurrField()); + } + + //repair allies + else{ + commandType= type->getFirstRepairCommand(targetUnit->getType()); + } + } + else{ + //check harvest command + Resource *resource= sc->getResource(); + if(resource!=NULL){ + commandType= type->getFirstHarvestCommand(resource->getType()); + } + } + + //default command is move command + if(commandType==NULL){ + commandType= type->getFirstCtOfClass(ccMove); + } + + return commandType; +} + +bool Unit::update(){ + assert(progress<=1.f); + + //highlight + if(highlight>0.f){ + highlight-= 1.f/(highlightTime*GameConstants::updateFps); + } + + //speed + int speed= currSkill->getTotalSpeed(&totalUpgrade); + + //speed modifier + float diagonalFactor= 1.f; + float heightFactor= 1.f; + if(currSkill->getClass()==scMove){ + + //if moving in diagonal move slower + Vec2i dest= pos-lastPos; + if(abs(dest.x)+abs(dest.y) == 2){ + diagonalFactor= 0.71f; + } + + //if movig to an higher cell move slower else move faster + float heightDiff= map->getCell(pos)->getHeight() - map->getCell(targetPos)->getHeight(); + heightFactor= clamp(1.f+heightDiff/5.f, 0.2f, 5.f); + } + + //update progresses + lastAnimProgress= animProgress; + progress+= (speed*diagonalFactor*heightFactor)/(speedDivider*GameConstants::updateFps); + animProgress+= (currSkill->getAnimSpeed()*heightFactor)/(speedDivider*GameConstants::updateFps); + + //update target + updateTarget(); + + //rotation + if(currSkill->getClass()!=scStop){ + const int rotFactor= 2; + if(progress<1.f/rotFactor){ + if(type->getFirstStOfClass(scMove)){ + if(abs(lastRotation-targetRotation)<180) + rotation= lastRotation+(targetRotation-lastRotation)*progress*rotFactor; + else{ + float rotationTerm= targetRotation>lastRotation? -360.f: +360.f; + rotation= lastRotation+(targetRotation-lastRotation+rotationTerm)*progress*rotFactor; + } + } + } + } + + //checks + if(animProgress>1.f){ + animProgress= currSkill->getClass()==scDie? 1.f: 0.f; + } + + //checks + if(progress>=1.f){ + lastRotation= targetRotation; + if(currSkill->getClass()!=scDie){ + progress= 0.f; + return true; + } + else{ + progress= 1.f; + deadCount++; + if(deadCount>=maxDeadCount){ + toBeUndertaken= true; + return false; + } + } + } + + return false; +} + +void Unit::tick(){ + + if(isAlive()){ + //regenerate hp + hp+= type->getHpRegeneration(); + if(hp>type->getTotalMaxHp(&totalUpgrade)){ + hp= type->getTotalMaxHp(&totalUpgrade); + } + + //regenerate ep + ep+= type->getEpRegeneration(); + if(ep>type->getTotalMaxEp(&totalUpgrade)){ + ep= type->getTotalMaxEp(&totalUpgrade); + } + } +} + +int Unit::update2(){ + progress2++; + return progress2; +} + +bool Unit::computeEp(){ + + //if not enough ep + if(ep-currSkill->getEpCost() < 0){ + return true; + } + + //decrease ep + ep-= currSkill->getEpCost(); + if(ep>getType()->getTotalMaxEp(&totalUpgrade)){ + ep= getType()->getTotalMaxEp(&totalUpgrade); + } + + return false; +} + +bool Unit::repair(){ + + //increase hp + hp+= getType()->getMaxHp()/type->getProductionTime() + 1; + if(hp>(getType()->getTotalMaxHp(&totalUpgrade))){ + hp= getType()->getTotalMaxHp(&totalUpgrade); + return true; + } + + //stop fire + if(hp>type->getMaxHp()/2 && fire!=NULL){ + fire->fade(); + fire= NULL; + } + return false; +} + +//decrements HP and returns if dead +bool Unit::decHp(int i){ + if(hp==0){ + return false; + } + + hp-=i; + + //fire + if(type->getProperty(UnitType::pBurnable) && hpgetMaxHp()/2 && fire==NULL){ + FireParticleSystem *fps; + fps= new FireParticleSystem(200); + fps->setSpeed(2.5f/GameConstants::updateFps); + fps->setPos(getCurrVector()); + fps->setRadius(type->getSize()/3.f); + fps->setTexture(CoreData::getInstance().getFireTexture()); + fps->setParticleSize(type->getSize()/3.f); + fire= fps; + Renderer::getInstance().manageParticleSystem(fps, rsGame); + } + + //stop fire on death + if(hp<=0){ + alive= false; + hp=0; + if(fire!=NULL){ + fire->fade(); + fire= NULL; + } + return true; + } + return false; +} + +string Unit::getDesc() const{ + + Lang &lang= Lang::getInstance(); + + //pos + //str+="Pos: "+v2iToStr(pos)+"\n"; + + //hp + string str= "\n" + lang.get("Hp")+ ": " + intToStr(hp) + "/" + intToStr(type->getTotalMaxHp(&totalUpgrade)); + if(type->getHpRegeneration()!=0){ + str+= " (" + lang.get("Regeneration") + ": " + intToStr(type->getHpRegeneration()) + ")"; + } + + //ep + if(getType()->getMaxEp()!=0){ + str+= "\n" + lang.get("Ep")+ ": " + intToStr(ep) + "/" + intToStr(type->getTotalMaxEp(&totalUpgrade)); + } + if(type->getEpRegeneration()!=0){ + str+= " (" + lang.get("Regeneration") + ": " + intToStr(type->getEpRegeneration()) + ")"; + } + + //armor + str+= "\n" + lang.get("Armor")+ ": " + intToStr(getType()->getArmor()); + if(totalUpgrade.getArmor()!=0){ + str+="+"+intToStr(totalUpgrade.getArmor()); + } + str+= " ("+getType()->getArmorType()->getName()+")"; + + //sight + str+="\n"+ lang.get("Sight")+ ": " + intToStr(getType()->getSight()); + if(totalUpgrade.getSight()!=0){ + str+="+"+intToStr(totalUpgrade.getSight()); + } + + //kills + const Level *nextLevel= getNextLevel(); + if(kills>0 || nextLevel!=NULL){ + str+= "\n" + lang.get("Kills") +": " + intToStr(kills); + if(nextLevel!=NULL){ + str+= " (" + nextLevel->getName() + ": " + intToStr(nextLevel->getKills()) + ")"; + } + } + + //str+= "\nskl: "+scToStr(currSkill->getClass()); + + //load + if(loadCount!=0){ + str+= "\n" + lang.get("Load")+ ": " + intToStr(loadCount) +" " + loadType->getName(); + } + + //consumable production + for(int i=0; igetCostCount(); ++i){ + const Resource *r= getType()->getCost(i); + if(r->getType()->getClass()==rcConsumable){ + str+= "\n"; + str+= r->getAmount()<0? lang.get("Produce")+": ": lang.get("Consume")+": "; + str+= intToStr(abs(r->getAmount())) + " " + r->getType()->getName(); + } + } + + //command info + if(!commands.empty()){ + str+= "\n" + commands.front()->getCommandType()->getName(); + if(commands.size()>1){ + str+="\n"+lang.get("OrdersOnQueue")+": "+intToStr(commands.size()); + } + } + else{ + //can store + if(getType()->getStoredResourceCount()>0){ + for(int i=0; igetStoredResourceCount(); ++i){ + const Resource *r= getType()->getStoredResource(i); + str+= "\n"+lang.get("Store")+": "; + str+= intToStr(r->getAmount()) + " " + r->getType()->getName(); + } + } + } + + return str; +} + +void Unit::applyUpgrade(const UpgradeType *upgradeType){ + if(upgradeType->isAffected(type)){ + totalUpgrade.sum(upgradeType); + hp+= upgradeType->getMaxHp(); + } +} + +void Unit::computeTotalUpgrade(){ + faction->getUpgradeManager()->computeTotalUpgrade(this, &totalUpgrade); +} + +void Unit::incKills(){ + ++kills; + + const Level *nextLevel= getNextLevel(); + if(nextLevel!=NULL && kills>= nextLevel->getKills()){ + level= nextLevel; + int maxHp= totalUpgrade.getMaxHp(); + totalUpgrade.incLevel(type); + hp+= totalUpgrade.getMaxHp()-maxHp; + } +} + +bool Unit::morph(const MorphCommandType *mct){ + const UnitType *morphUnitType= mct->getMorphUnit(); + + if(map->isFreeCellsOrHasUnit(pos, morphUnitType->getSize(), currField, this)){ + map->clearUnitCells(this, pos); + faction->deApplyStaticCosts(type); + hp+= morphUnitType->getMaxHp() - type->getMaxHp(); + type= morphUnitType; + level= NULL; + computeTotalUpgrade(); + map->putUnitCells(this, pos); + faction->applyDiscount(morphUnitType, mct->getDiscount()); + return true; + } + else{ + return false; + } +} + + +// ==================== PRIVATE ==================== + +float Unit::computeHeight(const Vec2i &pos) const{ + float height= map->getCell(pos)->getHeight(); + + if(currField==fAir){ + height+= World::airHeight; + } + + return height; +} + +void Unit::updateTarget(){ + Unit *target= targetRef.getUnit(); + if(target!=NULL){ + + //update target pos + targetPos= target->getCellPos(); + Vec2i relPos= targetPos - pos; + Vec2f relPosf= Vec2f(relPos.x, relPos.y); + targetRotation= radToDeg(atan2(relPosf.x, relPosf.y)); + + //update target vec + targetVec= target->getCurrVector(); + } +} + +void Unit::clearCommands(){ + while(!commands.empty()){ + undoCommand(commands.back()); + delete commands.back(); + commands.pop_back(); + } +} + +CommandResult Unit::checkCommand(Command *command) const{ + + //if not operative or has not command type => fail + if(!isOperative() || command->getUnit()==this || !getType()->hasCommandType(command->getCommandType())){ + return crFailUndefined; + } + + //if pos is not inside the world (if comand has not a pos, pos is (0, 0) and is inside world + if(!map->isInside(command->getPos())){ + return crFailUndefined; + } + + //check produced + const ProducibleType *produced= command->getCommandType()->getProduced(); + if(produced!=NULL){ + if(!faction->reqsOk(produced)){ + return crFailReqs; + } + if(!faction->checkCosts(produced)){ + return crFailRes; + } + } + + //build command specific, check resources and requirements for building + if(command->getCommandType()->getClass()==ccBuild){ + const UnitType *builtUnit= command->getUnitType(); + if(!faction->reqsOk(builtUnit)){ + return crFailReqs; + } + if(!faction->checkCosts(builtUnit)){ + return crFailRes; + } + } + + //upgrade command specific, check that upgrade is not upgraded + else if(command->getCommandType()->getClass()==ccUpgrade){ + const UpgradeCommandType *uct= static_cast(command->getCommandType()); + if(faction->getUpgradeManager()->isUpgradingOrUpgraded(uct->getProducedUpgrade())){ + return crFailUndefined; + } + } + + return crSuccess; +} + +void Unit::applyCommand(Command *command){ + + //check produced + const ProducibleType *produced= command->getCommandType()->getProduced(); + if(produced!=NULL){ + faction->applyCosts(produced); + } + + //build command specific + if(command->getCommandType()->getClass()==ccBuild){ + faction->applyCosts(command->getUnitType()); + } + + //upgrade command specific + else if(command->getCommandType()->getClass()==ccUpgrade){ + const UpgradeCommandType *uct= static_cast(command->getCommandType()); + faction->startUpgrade(uct->getProducedUpgrade()); + } +} + +CommandResult Unit::undoCommand(Command *command){ + + //return cost + const ProducibleType *produced= command->getCommandType()->getProduced(); + if(produced!=NULL){ + faction->deApplyCosts(produced); + } + + //return building cost if not already building it or dead + if(command->getCommandType()->getClass() == ccBuild){ + if(currSkill->getClass()!=scBuild && currSkill->getClass()!=scDie){ + faction->deApplyCosts(command->getUnitType()); + } + } + + //upgrade command cancel from list + if(command->getCommandType()->getClass() == ccUpgrade){ + const UpgradeCommandType *uct= static_cast(command->getCommandType()); + faction->cancelUpgrade(uct->getProducedUpgrade()); + } + + return crSuccess; +} + + +}}//end namespace diff --git a/source/glest_game/type_instances/unit.h b/source/glest_game/type_instances/unit.h new file mode 100644 index 00000000..f16e53d3 --- /dev/null +++ b/source/glest_game/type_instances/unit.h @@ -0,0 +1,299 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_UNIT_H_ +#define _GLEST_GAME_UNIT_H_ + +#include "model.h" +#include "upgrade_type.h" +#include "particle.h" +#include "skill_type.h" + +namespace Glest{ namespace Game{ + +using Shared::Graphics::ParticleSystem; +using Shared::Graphics::Vec4f; +using Shared::Graphics::Vec2f; +using Shared::Graphics::Vec3f; +using Shared::Graphics::Vec2i; +using Shared::Graphics::Model; + +class Map; +class Faction; +class Unit; +class Command; +class SkillType; +class ResourceType; +class CommandType; +class SkillType; +class UnitType; +class TotalUpgrade; +class UpgradeType; +class Level; +class MorphCommandType; + +enum CommandResult{ + crSuccess, + crFailRes, + crFailReqs, + crFailUndefined, + crSomeFailed +}; + +enum InterestingUnitType{ + iutIdleHarvester, + iutBuiltBuilding, + iutProducer, + iutDamaged, + iutStore +}; + +// ===================================================== +// class UnitObserver +// ===================================================== + +class UnitObserver{ +public: + enum Event{ + eKill + }; + +public: + virtual ~UnitObserver() {} + virtual void unitEvent(Event event, const Unit *unit)=0; +}; + +// ===================================================== +// class UnitReference +// ===================================================== + +class UnitReference{ +private: + int id; + Faction *faction; + +public: + UnitReference(); + + void operator=(const Unit *unit); + Unit *getUnit() const; +}; + +// ===================================================== +// class UnitPath +// +/// Holds the next cells of a Unit movement +// ===================================================== + +class UnitPath{ +private: + static const int maxBlockCount; + +private: + int blockCount; + vector pathQueue; + +public: + bool isBlocked(); + bool isEmpty(); + + void clear(); + void incBlockCount(); + void push(const Vec2i &path); + Vec2i pop(); +}; + +// =============================== +// class Unit +// +/// A game unit or building +// =============================== + +class Unit{ +private: + typedef list Commands; + typedef list Observers; + +public: + static const float speedDivider; + static const int maxDeadCount; + static const float highlightTime; + static const int invalidId; + +private: + int id; + int hp; + int ep; + int loadCount; + int deadCount; + float progress; //between 0 and 1 + float lastAnimProgress; //between 0 and 1 + float animProgress; //between 0 and 1 + float highlight; + int progress2; + int kills; + + UnitReference targetRef; + + Field currField; + Field targetField; + const Level *level; + + Vec2i pos; + Vec2i lastPos; + Vec2i targetPos; //absolute target pos + Vec3f targetVec; + Vec2i meetingPos; + + float lastRotation; //in degrees + float targetRotation; + float rotation; + + const UnitType *type; + const ResourceType *loadType; + const SkillType *currSkill; + + bool toBeUndertaken; + bool alive; + + Faction *faction; + ParticleSystem *fire; + TotalUpgrade totalUpgrade; + Map *map; + + UnitPath unitPath; + + Commands commands; + Observers observers; + +public: + Unit(int id, const Vec2i &pos, const UnitType *type, Faction *faction, Map *map); + ~Unit(); + + //queries + int getId() const {return id;} + Field getCurrField() const {return currField;} + int getLoadCount() const {return loadCount;} + float getLastAnimProgress() const {return lastAnimProgress;} + float getProgress() const {return progress;} + float getAnimProgress() const {return animProgress;} + float getHightlight() const {return highlight;} + int getProgress2() const {return progress2;} + int getFactionIndex() const; + int getTeam() const; + int getHp() const {return hp;} + int getEp() const {return ep;} + int getProductionPercent() const; + float getHpRatio() const; + float getEpRatio() const; + bool getToBeUndertaken() const {return toBeUndertaken;} + Vec2i getTargetPos() const {return targetPos;} + Vec3f getTargetVec() const {return targetVec;} + Field getTargetField() const {return targetField;} + Vec2i getMeetingPos() const {return meetingPos;} + Faction *getFaction() const {return faction;} + const ResourceType *getLoadType() const {return loadType;} + const UnitType *getType() const {return type;} + const SkillType *getCurrSkill() const {return currSkill;} + const TotalUpgrade *getTotalUpgrade() const {return &totalUpgrade;} + float getRotation() const {return rotation;} + float getVerticalRotation() const; + ParticleSystem *getFire() const {return fire;} + int getKills() {return kills;} + const Level *getLevel() const {return level;} + const Level *getNextLevel() const; + string getFullName() const; + const UnitPath *getPath() const {return &unitPath;} + UnitPath *getPath() {return &unitPath;} + + //pos + Vec2i getPos() const {return pos;} + Vec2i getLastPos() const {return lastPos;} + Vec2i getCenteredPos() const; + Vec2f getFloatCenteredPos() const; + Vec2i getCellPos() const; + + //is + bool isHighlighted() const {return highlight>0.f;} + bool isDead() const {return !alive;} + bool isAlive() const {return alive;} + bool isOperative() const; + bool isBeingBuilt() const; + bool isBuilt() const; + bool isPutrefacting() const; + bool isAlly(const Unit *unit) const; + bool isDamaged() const; + bool isInteresting(InterestingUnitType iut) const; + + //set + void setCurrField(Field currField) {this->currField= currField;} + void setCurrSkill(const SkillType *currSkill); + void setCurrSkill(SkillClass sc); + void setLoadCount(int loadCount) {this->loadCount= loadCount;} + void setLoadType(const ResourceType *loadType) {this->loadType= loadType;} + void setProgress2(int progress2) {this->progress2= progress2;} + void setPos(const Vec2i &pos); + void setTargetPos(const Vec2i &targetPos); + void setTarget(const Unit *unit); + void setTargetVec(const Vec3f &targetVec) {this->targetVec= targetVec;} + void setMeetingPos(const Vec2i &meetingPos) {this->meetingPos= meetingPos;} + + //render related + const Model *getCurrentModel() const; + Vec3f getCurrVector() const; + Vec3f getCurrVectorFlat() const; + + //command related + bool anyCommand() const; + Command *getCurrCommand() const; + uint getCommandSize() const; + CommandResult giveCommand(Command *command); //give a command + CommandResult finishCommand(); //command finished + CommandResult cancelCommand(); //cancel canceled + + //lifecycle + void create(bool startingUnit= false); + void born(); + void kill(); + void undertake(); + + //observers + void addObserver(UnitObserver *unitObserver) ; + void removeObserver(UnitObserver *unitObserver); + void notifyObservers(UnitObserver::Event event); + + //other + void resetHighlight(); + const CommandType *computeCommandType(const Vec2i &pos, const Unit *targetUnit= NULL) const; + string getDesc() const; + bool computeEp(); + bool repair(); + bool decHp(int i); + int update2(); + bool update(); + void tick(); + void applyUpgrade(const UpgradeType *upgradeType); + void computeTotalUpgrade(); + void incKills(); + bool morph(const MorphCommandType *mct); + CommandResult checkCommand(Command *command) const; + void applyCommand(Command *command); + +private: + float computeHeight(const Vec2i &pos) const; + void updateTarget(); + void clearCommands(); + CommandResult undoCommand(Command *command); +}; + +}}// end namespace + +#endif diff --git a/source/glest_game/type_instances/upgrade.cpp b/source/glest_game/type_instances/upgrade.cpp new file mode 100644 index 00000000..fc70e1fe --- /dev/null +++ b/source/glest_game/type_instances/upgrade.cpp @@ -0,0 +1,144 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "upgrade.h" + +#include + +#include "unit.h" +#include "util.h" +#include "upgrade_type.h" +#include "leak_dumper.h" + +using namespace std; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Upgrade +// ===================================================== + +Upgrade::Upgrade(const UpgradeType *type, int factionIndex){ + state= usUpgrading; + this->factionIndex= factionIndex; + this->type= type; +} + +// ============== get ============== + +UpgradeState Upgrade::getState() const{ + return state; +} + +int Upgrade::getFactionIndex() const{ + return factionIndex; +} + +const UpgradeType * Upgrade::getType() const{ + return type; +} + +// ============== set ============== + +void Upgrade::setState(UpgradeState state){ + this->state= state; +} + + +// ===================================================== +// class UpgradeManager +// ===================================================== + +UpgradeManager::~UpgradeManager(){ + deleteValues(upgrades.begin(), upgrades.end()); +} + +void UpgradeManager::startUpgrade(const UpgradeType *upgradeType, int factionIndex){ + upgrades.push_back(new Upgrade(upgradeType, factionIndex)); +} + +void UpgradeManager::cancelUpgrade(const UpgradeType *upgradeType){ + Upgrades::iterator it; + + for(it=upgrades.begin(); it!=upgrades.end(); it++){ + if((*it)->getType()==upgradeType){ + break; + } + } + + if(it!=upgrades.end()){ + upgrades.erase(it); + } + else{ + throw runtime_error("Error canceling upgrade, upgrade not found in upgrade manager"); + } +} + +void UpgradeManager::finishUpgrade(const UpgradeType *upgradeType){ + Upgrades::iterator it; + + for(it=upgrades.begin(); it!=upgrades.end(); it++){ + if((*it)->getType()==upgradeType){ + break; + } + } + + if(it!=upgrades.end()){ + (*it)->setState(usUpgraded); + } + else{ + throw runtime_error("Error finishing upgrade, upgrade not found in upgrade manager"); + } +} + +bool UpgradeManager::isUpgradingOrUpgraded(const UpgradeType *upgradeType) const{ + Upgrades::const_iterator it; + + for(it= upgrades.begin(); it!=upgrades.end(); it++){ + if((*it)->getType()==upgradeType){ + return true; + } + } + + return false; +} + +bool UpgradeManager::isUpgraded(const UpgradeType *upgradeType) const{ + for(Upgrades::const_iterator it= upgrades.begin(); it!=upgrades.end(); it++){ + if((*it)->getType()==upgradeType && (*it)->getState()==usUpgraded){ + return true; + } + } + return false; +} + +bool UpgradeManager::isUpgrading(const UpgradeType *upgradeType) const{ + for(Upgrades::const_iterator it= upgrades.begin(); it!=upgrades.end(); it++){ + if((*it)->getType()==upgradeType && (*it)->getState()==usUpgrading){ + return true; + } + } + return false; +} + +void UpgradeManager::computeTotalUpgrade(const Unit *unit, TotalUpgrade *totalUpgrade) const{ + totalUpgrade->reset(); + for(Upgrades::const_iterator it= upgrades.begin(); it!=upgrades.end(); it++){ + if((*it)->getFactionIndex()==unit->getFactionIndex() + && (*it)->getType()->isAffected(unit->getType()) + && (*it)->getState()==usUpgraded) + totalUpgrade->sum((*it)->getType()); + } + +} + +}}// end namespace diff --git a/source/glest_game/type_instances/upgrade.h b/source/glest_game/type_instances/upgrade.h new file mode 100644 index 00000000..d60ffead --- /dev/null +++ b/source/glest_game/type_instances/upgrade.h @@ -0,0 +1,87 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_UPGRADE_H_ +#define _GLEST_GAME_UPGRADE_H_ + +#include + +using std::vector; + +namespace Glest{ namespace Game{ + +class Unit; +class UpgradeType; + +enum UpgradeState{ + usUpgrading, + usUpgraded, + + upgradeStateCount +}; + +class UpgradeManager; +class TotalUpgrade; + +// ===================================================== +// class Upgrade +// +/// A bonus to an UnitType +// ===================================================== + +class Upgrade{ +private: + UpgradeState state; + int factionIndex; + const UpgradeType *type; + + friend class UpgradeManager; + +public: + Upgrade(const UpgradeType *upgradeType, int factionIndex); + +private: + //get + UpgradeState getState() const; + int getFactionIndex() const; + const UpgradeType * getType() const; + + //set + void setState(UpgradeState state); +}; + + +// =============================== +// class UpgradeManager +// =============================== + +class UpgradeManager{ +private: + typedef vector Upgrades; + Upgrades upgrades; +public: + ~UpgradeManager(); + + int getUpgradeCount() const {return upgrades.size();} + + void startUpgrade(const UpgradeType *upgradeType, int factionIndex); + void cancelUpgrade(const UpgradeType *upgradeType); + void finishUpgrade(const UpgradeType *upgradeType); + + bool isUpgraded(const UpgradeType *upgradeType) const; + bool isUpgrading(const UpgradeType *upgradeType) const; + bool isUpgradingOrUpgraded(const UpgradeType *upgradeType) const; + void computeTotalUpgrade(const Unit *unit, TotalUpgrade *totalUpgrade) const; +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/types/command_type.cpp b/source/glest_game/types/command_type.cpp new file mode 100644 index 00000000..771ba78a --- /dev/null +++ b/source/glest_game/types/command_type.cpp @@ -0,0 +1,744 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "command_type.h" + +#include +#include + +#include "upgrade_type.h" +#include "unit_type.h" +#include "sound.h" +#include "util.h" +#include "leak_dumper.h" +#include "graphics_interface.h" +#include "tech_tree.h" +#include "faction_type.h" +#include "unit_updater.h" +#include "renderer.h" +#include "leak_dumper.h" + +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + + +// ===================================================== +// class CommandType +// ===================================================== + +//get +CommandClass CommandType::getClass() const{ + assert(this!=NULL); + return commandTypeClass; +} + +void CommandType::load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut){ + this->id= id; + name= n->getChild("name")->getAttribute("value")->getRestrictedValue(); + + //image + const XmlNode *imageNode= n->getChild("image"); + image= Renderer::getInstance().newTexture2D(rsGame); + image->load(dir+"/"+imageNode->getAttribute("path")->getRestrictedValue()); + + //unit requirements + const XmlNode *unitRequirementsNode= n->getChild("unit-requirements"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *unitNode= unitRequirementsNode->getChild("unit", i); + string name= unitNode->getAttribute("name")->getRestrictedValue(); + unitReqs.push_back(ft->getUnitType(name)); + } + + //upgrade requirements + const XmlNode *upgradeRequirementsNode= n->getChild("upgrade-requirements"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *upgradeReqNode= upgradeRequirementsNode->getChild("upgrade", i); + string name= upgradeReqNode->getAttribute("name")->getRestrictedValue(); + upgradeReqs.push_back(ft->getUpgradeType(name)); + } +} + +// ===================================================== +// class StopCommandType +// ===================================================== + +//varios +StopCommandType::StopCommandType(){ + commandTypeClass= ccStop; + clicks= cOne; +} + +void StopCommandType::update(UnitUpdater *unitUpdater, Unit *unit) const{ + unitUpdater->updateStop(unit); +} + +string StopCommandType::getDesc(const TotalUpgrade *totalUpgrade) const{ + string str; + Lang &lang= Lang::getInstance(); + + str= name+"\n"; + str+= lang.get("ReactionSpeed")+": "+ intToStr(stopSkillType->getSpeed())+"\n"; + if(stopSkillType->getEpCost()!=0) + str+= lang.get("EpCost")+": "+intToStr(stopSkillType->getEpCost())+"\n"; + + return str; +} + +string StopCommandType::toString() const{ + Lang &lang= Lang::getInstance(); + return lang.get("Stop"); +} + +void StopCommandType::load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut){ + CommandType::load(id, n, dir, tt, ft, ut); + + //stop + string skillName= n->getChild("stop-skill")->getAttribute("value")->getRestrictedValue(); + stopSkillType= static_cast(ut.getSkillType(skillName, scStop)); +} + + +// ===================================================== +// class MoveCommandType +// ===================================================== + +//varios +MoveCommandType::MoveCommandType(){ + commandTypeClass= ccMove; + clicks= cTwo; +} + +void MoveCommandType::update(UnitUpdater *unitUpdater, Unit *unit) const{ + unitUpdater->updateMove(unit); +} + +void MoveCommandType::load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut){ + CommandType::load(id, n, dir, tt, ft, ut); + + //move + string skillName= n->getChild("move-skill")->getAttribute("value")->getRestrictedValue(); + moveSkillType= static_cast(ut.getSkillType(skillName, scMove)); +} + +string MoveCommandType::getDesc(const TotalUpgrade *totalUpgrade) const{ + string str; + Lang &lang= Lang::getInstance(); + + str= name+"\n"; + str+= lang.get("WalkSpeed")+": "+ intToStr(moveSkillType->getSpeed()); + if(totalUpgrade->getMoveSpeed()!=0){ + str+= "+" + intToStr(totalUpgrade->getMoveSpeed()); + } + str+="\n"; + if(moveSkillType->getEpCost()!=0){ + str+= lang.get("EpCost")+": "+intToStr(moveSkillType->getEpCost())+"\n"; + } + + return str; +} + +string MoveCommandType::toString() const{ + Lang &lang= Lang::getInstance(); + return lang.get("Move"); +} + +// ===================================================== +// class AttackCommandType +// ===================================================== + +//varios +AttackCommandType::AttackCommandType(){ + commandTypeClass= ccAttack; + clicks= cTwo; +} + +void AttackCommandType::update(UnitUpdater *unitUpdater, Unit *unit) const{ + unitUpdater->updateAttack(unit); +} + +void AttackCommandType::load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut){ + CommandType::load(id, n, dir, tt, ft, ut); + + //move + string skillName= n->getChild("move-skill")->getAttribute("value")->getRestrictedValue(); + moveSkillType= static_cast(ut.getSkillType(skillName, scMove)); + + //attack + skillName= n->getChild("attack-skill")->getAttribute("value")->getRestrictedValue(); + attackSkillType= static_cast(ut.getSkillType(skillName, scAttack)); +} + +string AttackCommandType::getDesc(const TotalUpgrade *totalUpgrade) const{ + string str; + Lang &lang= Lang::getInstance(); + + str= name+"\n"; + if(attackSkillType->getEpCost()!=0){ + str+= lang.get("EpCost") + ": " + intToStr(attackSkillType->getEpCost()) + "\n"; + } + + //attack strength + str+= lang.get("AttackStrenght")+": "; + str+= intToStr(attackSkillType->getAttackStrength()-attackSkillType->getAttackVar()); + str+= "..."; + str+= intToStr(attackSkillType->getAttackStrength()+attackSkillType->getAttackVar()); + if(totalUpgrade->getAttackStrength()!=0){ + str+= "+"+intToStr(totalUpgrade->getAttackStrength()); + } + str+= " ("+ attackSkillType->getAttackType()->getName() +")"; + str+= "\n"; + + //splash radius + if(attackSkillType->getSplashRadius()!=0){ + str+= lang.get("SplashRadius")+": "+intToStr(attackSkillType->getSplashRadius())+"\n"; + } + + //attack distance + str+= lang.get("AttackDistance")+": "+intToStr(attackSkillType->getAttackRange()); + if(totalUpgrade->getAttackRange()!=0){ + str+= "+"+intToStr(totalUpgrade->getAttackRange()!=0); + } + str+="\n"; + + //attack fields + str+= lang.get("Fields") + ": "; + for(int i= 0; i < fieldCount; i++){ + Field field = static_cast(i); + if( attackSkillType->getAttackField(field) ) + { + str+= SkillType::fieldToStr(field) + " "; + } + } + str+="\n"; + + //movement speed + str+= lang.get("WalkSpeed")+": "+ intToStr(moveSkillType->getSpeed()) ; + if(totalUpgrade->getMoveSpeed()!=0){ + str+= "+"+intToStr(totalUpgrade->getMoveSpeed()); + } + str+="\n"; + + str+= lang.get("AttackSpeed")+": "+ intToStr(attackSkillType->getSpeed()) +"\n"; + + return str; +} + +string AttackCommandType::toString() const{ + Lang &lang= Lang::getInstance(); + return lang.get("Attack"); +} + + +// ===================================================== +// class AttackStoppedCommandType +// ===================================================== + +//varios +AttackStoppedCommandType::AttackStoppedCommandType(){ + commandTypeClass= ccAttackStopped; + clicks= cOne; +} + +void AttackStoppedCommandType::update(UnitUpdater *unitUpdater, Unit *unit) const{ + unitUpdater->updateAttackStopped(unit); +} + +void AttackStoppedCommandType::load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut){ + CommandType::load(id, n, dir, tt, ft, ut); + + //stop + string skillName= n->getChild("stop-skill")->getAttribute("value")->getRestrictedValue(); + stopSkillType= static_cast(ut.getSkillType(skillName, scStop)); + + //attack + skillName= n->getChild("attack-skill")->getAttribute("value")->getRestrictedValue(); + attackSkillType= static_cast(ut.getSkillType(skillName, scAttack)); +} + +string AttackStoppedCommandType::getDesc(const TotalUpgrade *totalUpgrade) const{ + Lang &lang= Lang::getInstance(); + string str; + + str= name+"\n"; + if(attackSkillType->getEpCost()!=0){ + str+= lang.get("EpCost")+": "+intToStr(attackSkillType->getEpCost())+"\n"; + } + + //attack strength + str+= lang.get("AttackStrenght")+": "; + str+= intToStr(attackSkillType->getAttackStrength()-attackSkillType->getAttackVar()); + str+="..."; + str+= intToStr(attackSkillType->getAttackStrength()+attackSkillType->getAttackVar()); + if(totalUpgrade->getAttackStrength()!=0) + str+= "+"+intToStr(totalUpgrade->getAttackStrength()); + str+= " ("+ attackSkillType->getAttackType()->getName() +")"; + str+="\n"; + + //splash radius + if(attackSkillType->getSplashRadius()!=0){ + str+= lang.get("SplashRadius")+": "+intToStr(attackSkillType->getSplashRadius())+"\n"; + } + + //attack distance + str+= lang.get("AttackDistance")+": "+floatToStr(attackSkillType->getAttackRange()); + if(totalUpgrade->getAttackRange()!=0){ + str+= "+"+intToStr(totalUpgrade->getAttackRange()!=0); + } + str+="\n"; + + //attack fields + str+= lang.get("Fields") + ": "; + for(int i= 0; i < fieldCount; i++){ + Field field = static_cast(i); + if( attackSkillType->getAttackField(field) ) + { + str+= SkillType::fieldToStr(field) + " "; + } + } + str+="\n"; + + return str; +} + +string AttackStoppedCommandType::toString() const{ + Lang &lang= Lang::getInstance(); + return lang.get("AttackStopped"); +} + + +// ===================================================== +// class BuildCommandType +// ===================================================== + +//varios +BuildCommandType::BuildCommandType(){ + commandTypeClass= ccBuild; + clicks= cTwo; +} + +BuildCommandType::~BuildCommandType(){ + deleteValues(builtSounds.getSounds().begin(), builtSounds.getSounds().end()); + deleteValues(startSounds.getSounds().begin(), startSounds.getSounds().end()); +} + +void BuildCommandType::update(UnitUpdater *unitUpdater, Unit *unit) const{ + unitUpdater->updateBuild(unit); +} + +void BuildCommandType::load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut){ + CommandType::load(id, n, dir, tt, ft, ut); + + //move + string skillName= n->getChild("move-skill")->getAttribute("value")->getRestrictedValue(); + moveSkillType= static_cast(ut.getSkillType(skillName, scMove)); + + //build + skillName= n->getChild("build-skill")->getAttribute("value")->getRestrictedValue(); + buildSkillType= static_cast(ut.getSkillType(skillName, scBuild)); + + //buildings built + const XmlNode *buildingsNode= n->getChild("buildings"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *buildingNode= buildingsNode->getChild("building", i); + string name= buildingNode->getAttribute("name")->getRestrictedValue(); + buildings.push_back(ft->getUnitType(name)); + } + + //start sound + const XmlNode *startSoundNode= n->getChild("start-sound"); + if(startSoundNode->getAttribute("enabled")->getBoolValue()){ + startSounds.resize(startSoundNode->getChildCount()); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *soundFileNode= startSoundNode->getChild("sound-file", i); + string path= soundFileNode->getAttribute("path")->getRestrictedValue(); + StaticSound *sound= new StaticSound(); + sound->load(dir + "/" + path); + startSounds[i]= sound; + } + } + + //built sound + const XmlNode *builtSoundNode= n->getChild("built-sound"); + if(builtSoundNode->getAttribute("enabled")->getBoolValue()){ + builtSounds.resize(builtSoundNode->getChildCount()); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *soundFileNode= builtSoundNode->getChild("sound-file", i); + string path= soundFileNode->getAttribute("path")->getRestrictedValue(); + StaticSound *sound= new StaticSound(); + sound->load(dir + "/" + path); + builtSounds[i]= sound; + } + } +} + +string BuildCommandType::getDesc(const TotalUpgrade *totalUpgrade) const{ + string str; + Lang &lang= Lang::getInstance(); + + str= name+"\n"; + str+= lang.get("BuildSpeed")+": "+ intToStr(buildSkillType->getSpeed())+"\n"; + if(buildSkillType->getEpCost()!=0){ + str+= lang.get("EpCost")+": "+intToStr(buildSkillType->getEpCost())+"\n"; + } + + return str; +} + +string BuildCommandType::toString() const{ + Lang &lang= Lang::getInstance(); + return lang.get("Build"); +} + +// ===================================================== +// class HarvestCommandType +// ===================================================== + +//varios +HarvestCommandType::HarvestCommandType(){ + commandTypeClass= ccHarvest; + clicks= cTwo; +} + +void HarvestCommandType::update(UnitUpdater *unitUpdater, Unit *unit) const{ + unitUpdater->updateHarvest(unit); +} + +void HarvestCommandType::load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut){ + + CommandType::load(id, n, dir, tt, ft, ut); + + //move + string skillName= n->getChild("move-skill")->getAttribute("value")->getRestrictedValue(); + moveSkillType= static_cast(ut.getSkillType(skillName, scMove)); + + //harvest + skillName= n->getChild("harvest-skill")->getAttribute("value")->getRestrictedValue(); + harvestSkillType= static_cast(ut.getSkillType(skillName, scHarvest)); + + //stop loaded + skillName= n->getChild("stop-loaded-skill")->getAttribute("value")->getRestrictedValue(); + stopLoadedSkillType= static_cast(ut.getSkillType(skillName, scStop)); + + //move loaded + skillName= n->getChild("move-loaded-skill")->getAttribute("value")->getRestrictedValue(); + moveLoadedSkillType= static_cast(ut.getSkillType(skillName, scMove)); + + //resources can harvest + const XmlNode *resourcesNode= n->getChild("harvested-resources"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *resourceNode= resourcesNode->getChild("resource", i); + harvestedResources.push_back(tt->getResourceType(resourceNode->getAttribute("name")->getRestrictedValue())); + } + + maxLoad= n->getChild("max-load")->getAttribute("value")->getIntValue(); + hitsPerUnit= n->getChild("hits-per-unit")->getAttribute("value")->getIntValue(); +} + +string HarvestCommandType::getDesc(const TotalUpgrade *totalUpgrade) const{ + + Lang &lang= Lang::getInstance(); + string str; + + str= name+"\n"; + str+= lang.get("HarvestSpeed")+": "+ intToStr(harvestSkillType->getSpeed()/hitsPerUnit)+"\n"; + str+= lang.get("MaxLoad")+": "+ intToStr(maxLoad)+"\n"; + str+= lang.get("LoadedSpeed")+": "+ intToStr(moveLoadedSkillType->getSpeed())+"\n"; + if(harvestSkillType->getEpCost()!=0){ + str+= lang.get("EpCost")+": "+intToStr(harvestSkillType->getEpCost())+"\n"; + } + str+=lang.get("Resources")+":\n"; + for(int i=0; igetName()+"\n"; + } + + return str; +} + +string HarvestCommandType::toString() const{ + Lang &lang= Lang::getInstance(); + return lang.get("Harvest"); +} + +bool HarvestCommandType::canHarvest(const ResourceType *resourceType) const{ + return find(harvestedResources.begin(), harvestedResources.end(), resourceType) != harvestedResources.end(); +} + +// ===================================================== +// class RepairCommandType +// ===================================================== + +//varios +RepairCommandType::RepairCommandType(){ + commandTypeClass= ccRepair; + clicks= cTwo; +} + +RepairCommandType::~RepairCommandType(){ +} + +void RepairCommandType::update(UnitUpdater *unitUpdater, Unit *unit) const{ + unitUpdater->updateRepair(unit); +} + +void RepairCommandType::load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut){ + + CommandType::load(id, n, dir, tt, ft, ut); + + //move + string skillName= n->getChild("move-skill")->getAttribute("value")->getRestrictedValue(); + moveSkillType= static_cast(ut.getSkillType(skillName, scMove)); + + //repair + skillName= n->getChild("repair-skill")->getAttribute("value")->getRestrictedValue(); + repairSkillType= static_cast(ut.getSkillType(skillName, scRepair)); + + //repaired units + const XmlNode *unitsNode= n->getChild("repaired-units"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *unitNode= unitsNode->getChild("unit", i); + repairableUnits.push_back(ft->getUnitType(unitNode->getAttribute("name")->getRestrictedValue())); + } +} + +string RepairCommandType::getDesc(const TotalUpgrade *totalUpgrade) const{ + Lang &lang= Lang::getInstance(); + string str; + + str= name+"\n"; + str+= lang.get("RepairSpeed")+": "+ intToStr(repairSkillType->getSpeed())+"\n"; + if(repairSkillType->getEpCost()!=0){ + str+= lang.get("EpCost")+": "+intToStr(repairSkillType->getEpCost())+"\n"; + } + + str+="\n"+lang.get("CanRepair")+":\n"; + for(int i=0; i(repairableUnits[i]))->getName()+"\n"; + } + + return str; +} + +string RepairCommandType::toString() const{ + Lang &lang= Lang::getInstance(); + return lang.get("Repair"); +} + +//get +bool RepairCommandType::isRepairableUnitType(const UnitType *unitType) const{ + for(int i=0; i(repairableUnits[i])==unitType){ + return true; + } + } + return false; +} + +// ===================================================== +// class ProduceCommandType +// ===================================================== + +//varios +ProduceCommandType::ProduceCommandType(){ + commandTypeClass= ccProduce; + clicks= cOne; +} + +void ProduceCommandType::update(UnitUpdater *unitUpdater, Unit *unit) const{ + unitUpdater->updateProduce(unit); +} + +void ProduceCommandType::load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut){ + CommandType::load(id, n, dir, tt, ft, ut); + + //produce + string skillName= n->getChild("produce-skill")->getAttribute("value")->getRestrictedValue(); + produceSkillType= static_cast(ut.getSkillType(skillName, scProduce)); + + string producedUnitName= n->getChild("produced-unit")->getAttribute("name")->getRestrictedValue(); + producedUnit= ft->getUnitType(producedUnitName); +} + +string ProduceCommandType::getDesc(const TotalUpgrade *totalUpgrade) const{ + string str= name+"\n"; + Lang &lang= Lang::getInstance(); + + //prod speed + str+= lang.get("ProductionSpeed")+": "+ intToStr(produceSkillType->getSpeed()); + if(totalUpgrade->getProdSpeed()!=0){ + str+="+" + intToStr(totalUpgrade->getProdSpeed()); + } + str+="\n"; + + //mpcost + if(produceSkillType->getEpCost()!=0){ + str+= lang.get("EpCost")+": "+intToStr(produceSkillType->getEpCost())+"\n"; + } + + str+= "\n" + getProducedUnit()->getReqDesc(); + + return str; +} + +string ProduceCommandType::toString() const{ + Lang &lang= Lang::getInstance(); + return lang.get("Produce"); +} + +string ProduceCommandType::getReqDesc() const{ + return RequirableType::getReqDesc()+"\n"+getProducedUnit()->getReqDesc(); +} + +const ProducibleType *ProduceCommandType::getProduced() const{ + return producedUnit; +} + +// ===================================================== +// class UpgradeCommandType +// ===================================================== + +//varios +UpgradeCommandType::UpgradeCommandType(){ + commandTypeClass= ccUpgrade; + clicks= cOne; +} + +void UpgradeCommandType::update(UnitUpdater *unitUpdater, Unit *unit) const{ + unitUpdater->updateUpgrade(unit); +} + +void UpgradeCommandType::load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut){ + + CommandType::load(id, n, dir, tt, ft, ut); + + //upgrade + string skillName= n->getChild("upgrade-skill")->getAttribute("value")->getRestrictedValue(); + upgradeSkillType= static_cast(ut.getSkillType(skillName, scUpgrade)); + + string producedUpgradeName= n->getChild("produced-upgrade")->getAttribute("name")->getRestrictedValue(); + producedUpgrade= ft->getUpgradeType(producedUpgradeName); + +} + +string UpgradeCommandType::getDesc(const TotalUpgrade *totalUpgrade) const{ + string str; + Lang &lang= Lang::getInstance(); + + str= name+"\n"; + str+= lang.get("UpgradeSpeed")+": "+ intToStr(upgradeSkillType->getSpeed())+"\n"; + if(upgradeSkillType->getEpCost()!=0) + str+= lang.get("EpCost")+": "+intToStr(upgradeSkillType->getEpCost())+"\n"; + + str+= "\n"+getProducedUpgrade()->getReqDesc(); + + return str; +} + +string UpgradeCommandType::toString() const{ + Lang &lang= Lang::getInstance(); + return lang.get("Upgrade"); +} + +string UpgradeCommandType::getReqDesc() const{ + return RequirableType::getReqDesc()+"\n"+getProducedUpgrade()->getReqDesc(); +} + +const ProducibleType *UpgradeCommandType::getProduced() const{ + return producedUpgrade; +} + +// ===================================================== +// class MorphCommandType +// ===================================================== + +//varios +MorphCommandType::MorphCommandType(){ + commandTypeClass= ccMorph; + clicks= cOne; +} + +void MorphCommandType::update(UnitUpdater *unitUpdater, Unit *unit) const{ + unitUpdater->updateMorph(unit); +} + +void MorphCommandType::load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut){ + CommandType::load(id, n, dir, tt, ft, ut); + + //morph skill + string skillName= n->getChild("morph-skill")->getAttribute("value")->getRestrictedValue(); + morphSkillType= static_cast(ut.getSkillType(skillName, scMorph)); + + //morph unit + string morphUnitName= n->getChild("morph-unit")->getAttribute("name")->getRestrictedValue(); + morphUnit= ft->getUnitType(morphUnitName); + + //discount + discount= n->getChild("discount")->getAttribute("value")->getIntValue(); +} + +string MorphCommandType::getDesc(const TotalUpgrade *totalUpgrade) const{ + string str= name+"\n"; + Lang &lang= Lang::getInstance(); + + //prod speed + str+= lang.get("MorphSpeed")+": "+ intToStr(morphSkillType->getSpeed())+"\n"; + + //mpcost + if(morphSkillType->getEpCost()!=0){ + str+= lang.get("EpCost")+": "+intToStr(morphSkillType->getEpCost())+"\n"; + } + + //discount + if(discount!=0){ + str+= lang.get("Discount")+": "+intToStr(discount)+"%\n"; + } + + str+= "\n"+getProduced()->getReqDesc(); + + return str; +} + +string MorphCommandType::toString() const{ + Lang &lang= Lang::getInstance(); + return lang.get("Morph"); +} + +string MorphCommandType::getReqDesc() const{ + return RequirableType::getReqDesc() + "\n" + getProduced()->getReqDesc(); +} + +const ProducibleType *MorphCommandType::getProduced() const{ + return morphUnit; +} + +// ===================================================== +// class CommandFactory +// ===================================================== + +CommandTypeFactory::CommandTypeFactory(){ + registerClass("stop"); + registerClass("move"); + registerClass("attack"); + registerClass("attack_stopped"); + registerClass("build"); + registerClass("harvest"); + registerClass("repair"); + registerClass("produce"); + registerClass("upgrade"); + registerClass("morph"); +} + +CommandTypeFactory &CommandTypeFactory::getInstance(){ + static CommandTypeFactory ctf; + return ctf; +} + +}}//end namespace diff --git a/source/glest_game/types/command_type.h b/source/glest_game/types/command_type.h new file mode 100644 index 00000000..16fefe6e --- /dev/null +++ b/source/glest_game/types/command_type.h @@ -0,0 +1,343 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_COMMANDTYPE_H_ +#define _GLEST_GAME_COMMANDTYPE_H_ + +#include "element_type.h" +#include "resource_type.h" +#include "lang.h" +#include "skill_type.h" +#include "factory.h" +#include "xml_parser.h" +#include "sound_container.h" + +namespace Glest{ namespace Game{ + +using Shared::Util::MultiFactory; + +class UnitUpdater; +class Unit; +class UnitType; +class TechTree; +class FactionType; + +enum CommandClass{ + ccStop, + ccMove, + ccAttack, + ccAttackStopped, + ccBuild, + ccHarvest, + ccRepair, + ccProduce, + ccUpgrade, + ccMorph, + + ccCount, + ccNull +}; + +enum Clicks{ + cOne, + cTwo +}; + +// ===================================================== +// class CommandType +// +/// A complex action performed by a unit, composed by skills +// ===================================================== + +class CommandType: public RequirableType{ +protected: + CommandClass commandTypeClass; + Clicks clicks; + int id; + +public: + static const int invalidId= -1; + +public: + virtual void update(UnitUpdater *unitUpdater, Unit *unit) const= 0; + virtual void load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut); + virtual string getDesc(const TotalUpgrade *totalUpgrade) const= 0; + virtual string toString() const= 0; + virtual const ProducibleType *getProduced() const {return NULL;} + virtual bool isQueuable() const {return false;} + + //get + CommandClass getClass() const; + Clicks getClicks() const {return clicks;} + int getId() const {return id;} +}; + +// =============================== +// class StopCommandType +// =============================== + +class StopCommandType: public CommandType{ +private: + const StopSkillType* stopSkillType; + +public: + StopCommandType(); + virtual void update(UnitUpdater *unitUpdater, Unit *unit) const; + virtual void load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut); + virtual string getDesc(const TotalUpgrade *totalUpgrade) const; + virtual string toString() const; + + //get + const StopSkillType *getStopSkillType() const {return stopSkillType;}; +}; + + +// =============================== +// class MoveCommandType +// =============================== + +class MoveCommandType: public CommandType{ +private: + const MoveSkillType *moveSkillType; + +public: + MoveCommandType(); + virtual void update(UnitUpdater *unitUpdater, Unit *unit) const; + virtual void load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut); + virtual string getDesc(const TotalUpgrade *totalUpgrade) const; + virtual string toString() const; + + //get + const MoveSkillType *getMoveSkillType() const {return moveSkillType;}; +}; + + +// =============================== +// class AttackCommandType +// =============================== + +class AttackCommandType: public CommandType{ +private: + const MoveSkillType* moveSkillType; + const AttackSkillType* attackSkillType; + +public: + AttackCommandType(); + virtual void update(UnitUpdater *unitUpdater, Unit *unit) const; + virtual void load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut); + virtual string getDesc(const TotalUpgrade *totalUpgrade) const; + virtual string toString() const; + + //get + const MoveSkillType * getMoveSkillType() const {return moveSkillType;} + const AttackSkillType * getAttackSkillType() const {return attackSkillType;} +}; + +// ======================================= +// class AttackStoppedCommandType +// ======================================= + +class AttackStoppedCommandType: public CommandType{ +private: + const StopSkillType* stopSkillType; + const AttackSkillType* attackSkillType; + +public: + AttackStoppedCommandType(); + virtual void update(UnitUpdater *unitUpdater, Unit *unit) const; + virtual void load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut); + virtual string getDesc(const TotalUpgrade *totalUpgrade) const; + virtual string toString() const; + + //get + const StopSkillType * getStopSkillType() const {return stopSkillType;} + const AttackSkillType * getAttackSkillType() const {return attackSkillType;} +}; + + +// =============================== +// class BuildCommandType +// =============================== + +class BuildCommandType: public CommandType{ +private: + const MoveSkillType* moveSkillType; + const BuildSkillType* buildSkillType; + vector buildings; + SoundContainer startSounds; + SoundContainer builtSounds; + +public: + BuildCommandType(); + ~BuildCommandType(); + virtual void update(UnitUpdater *unitUpdater, Unit *unit) const; + virtual void load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut); + virtual string getDesc(const TotalUpgrade *totalUpgrade) const; + virtual string toString() const; + + //get + const MoveSkillType *getMoveSkillType() const {return moveSkillType;} + const BuildSkillType *getBuildSkillType() const {return buildSkillType;} + int getBuildingCount() const {return buildings.size();} + const UnitType * getBuilding(int i) const {return buildings[i];} + StaticSound *getStartSound() const {return startSounds.getRandSound();} + StaticSound *getBuiltSound() const {return builtSounds.getRandSound();} +}; + + +// =============================== +// class HarvestCommandType +// =============================== + +class HarvestCommandType: public CommandType{ +private: + const MoveSkillType *moveSkillType; + const MoveSkillType *moveLoadedSkillType; + const HarvestSkillType *harvestSkillType; + const StopSkillType *stopLoadedSkillType; + vector harvestedResources; + int maxLoad; + int hitsPerUnit; + +public: + HarvestCommandType(); + virtual void update(UnitUpdater *unitUpdater, Unit *unit) const; + virtual void load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut); + virtual string getDesc(const TotalUpgrade *totalUpgrade) const; + virtual string toString() const; + + //get + const MoveSkillType *getMoveSkillType() const {return moveSkillType;} + const MoveSkillType *getMoveLoadedSkillType() const {return moveLoadedSkillType;} + const HarvestSkillType *getHarvestSkillType() const {return harvestSkillType;} + const StopSkillType *getStopLoadedSkillType() const {return stopLoadedSkillType;} + int getMaxLoad() const {return maxLoad;} + int getHitsPerUnit() const {return hitsPerUnit;} + int getHarvestedResourceCount() const {return harvestedResources.size();} + const ResourceType* getHarvestedResource(int i) const {return harvestedResources[i];} + bool canHarvest(const ResourceType *resourceType) const; +}; + + +// =============================== +// class RepairCommandType +// =============================== + +class RepairCommandType: public CommandType{ +private: + const MoveSkillType* moveSkillType; + const RepairSkillType* repairSkillType; + vector repairableUnits; + +public: + RepairCommandType(); + ~RepairCommandType(); + virtual void update(UnitUpdater *unitUpdater, Unit *unit) const; + virtual void load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut); + virtual string getDesc(const TotalUpgrade *totalUpgrade) const; + virtual string toString() const; + + //get + const MoveSkillType *getMoveSkillType() const {return moveSkillType;}; + const RepairSkillType *getRepairSkillType() const {return repairSkillType;}; + bool isRepairableUnitType(const UnitType *unitType) const; +}; + + +// =============================== +// class ProduceCommandType +// =============================== + +class ProduceCommandType: public CommandType{ +private: + const ProduceSkillType* produceSkillType; + const UnitType *producedUnit; + +public: + ProduceCommandType(); + virtual void update(UnitUpdater *unitUpdater, Unit *unit) const; + virtual void load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut); + virtual string getDesc(const TotalUpgrade *totalUpgrade) const; + virtual string getReqDesc() const; + virtual string toString() const; + virtual const ProducibleType *getProduced() const; + virtual bool isQueuable() const {return true;} + + //get + const ProduceSkillType *getProduceSkillType() const {return produceSkillType;} + const UnitType *getProducedUnit() const {return producedUnit;} +}; + + +// =============================== +// class UpgradeCommandType +// =============================== + +class UpgradeCommandType: public CommandType{ +private: + const UpgradeSkillType* upgradeSkillType; + const UpgradeType* producedUpgrade; + +public: + UpgradeCommandType(); + virtual void update(UnitUpdater *unitUpdater, Unit *unit) const; + virtual void load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut); + virtual string getDesc(const TotalUpgrade *totalUpgrade) const; + virtual string toString() const; + virtual string getReqDesc() const; + virtual const ProducibleType *getProduced() const; + virtual bool isQueuable() const {return true;} + + //get + const UpgradeSkillType *getUpgradeSkillType() const {return upgradeSkillType;} + const UpgradeType *getProducedUpgrade() const {return producedUpgrade;} +}; + +// =============================== +// class MorphCommandType +// =============================== + +class MorphCommandType: public CommandType{ +private: + const MorphSkillType* morphSkillType; + const UnitType* morphUnit; + int discount; + +public: + MorphCommandType(); + virtual void update(UnitUpdater *unitUpdater, Unit *unit) const; + virtual void load(int id, const XmlNode *n, const string &dir, const TechTree *tt, const FactionType *ft, const UnitType &ut); + virtual string getDesc(const TotalUpgrade *totalUpgrade) const; + virtual string toString() const; + virtual string getReqDesc() const; + virtual const ProducibleType *getProduced() const; + + //get + const MorphSkillType *getMorphSkillType() const {return morphSkillType;} + const UnitType *getMorphUnit() const {return morphUnit;} + int getDiscount() const {return discount;} +}; + +// =============================== +// class CommandFactory +// =============================== + +class CommandTypeFactory: public MultiFactory{ +private: + CommandTypeFactory(); + +public: + static CommandTypeFactory &getInstance(); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/types/damage_multiplier.cpp b/source/glest_game/types/damage_multiplier.cpp new file mode 100644 index 00000000..685d2a08 --- /dev/null +++ b/source/glest_game/types/damage_multiplier.cpp @@ -0,0 +1,49 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "damage_multiplier.h" + +#include "leak_dumper.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class DamageMultiplierTable +// ===================================================== + +DamageMultiplierTable::DamageMultiplierTable(){ + values= NULL; +} + +DamageMultiplierTable::~DamageMultiplierTable(){ + delete [] values; +} + +void DamageMultiplierTable::init(int attackTypeCount, int armorTypeCount){ + this->attackTypeCount= attackTypeCount; + this->armorTypeCount= armorTypeCount; + + int valueCount= attackTypeCount*armorTypeCount; + values= new float[valueCount]; + for(int i=0; igetId()+att->getId()]; +} + +void DamageMultiplierTable::setDamageMultiplier(const AttackType *att, const ArmorType *art, float value){ + values[attackTypeCount*art->getId()+att->getId()]= value; +} + +}}//end namespaces diff --git a/source/glest_game/types/damage_multiplier.h b/source/glest_game/types/damage_multiplier.h new file mode 100644 index 00000000..0f1b43fa --- /dev/null +++ b/source/glest_game/types/damage_multiplier.h @@ -0,0 +1,79 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_DAMAGEMULTIPLIER_H_ +#define _GLEST_GAME_DAMAGEMULTIPLIER_H_ + +#include + +using std::string; + +namespace Glest{ namespace Game{ + +// =============================== +// class AttackType +// =============================== + +class AttackType{ +private: + string name; + int id; + +public: + int getId() const {return id;} + const string &getName() const {return name;} + + void setName(const string &name) {this->name= name;} + void setId(int id) {this->id= id;} +}; + +// =============================== +// class ArmorType +// =============================== + +class ArmorType{ +private: + string name; + int id; + +public: + int getId() const {return id;} + const string &getName() const {return name;} + + void setName(const string &name) {this->name= name;} + void setId(int id) {this->id= id;} +}; + +// ===================================================== +// class DamageMultiplierTable +// +/// Some attack types have bonuses against some +/// armor types and vice-versa +// ===================================================== + +class DamageMultiplierTable{ +private: + float *values; + int attackTypeCount; + int armorTypeCount; + +public: + DamageMultiplierTable(); + ~DamageMultiplierTable(); + + void init(int attackTypeCount, int armorTypeCount); + float getDamageMultiplier(const AttackType *att, const ArmorType *art) const; + void setDamageMultiplier(const AttackType *att, const ArmorType *art, float value); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/types/element_type.cpp b/source/glest_game/types/element_type.cpp new file mode 100644 index 00000000..b78b061f --- /dev/null +++ b/source/glest_game/types/element_type.cpp @@ -0,0 +1,110 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "element_type.h" + +#include + +#include "resource_type.h" +#include "upgrade_type.h" +#include "unit_type.h" +#include "resource.h" +#include "tech_tree.h" +#include "logger.h" +#include "lang.h" +#include "renderer.h" +#include "leak_dumper.h" + +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class DisplayableType +// ===================================================== + +DisplayableType::DisplayableType(){ + image= NULL; +} + +// ===================================================== +// class RequirableType +// ===================================================== + +string RequirableType::getReqDesc() const{ + bool anyReqs= false; + + string reqString; + for(int i=0; igetName(); + reqString+= "\n"; + anyReqs= true; + } + + for(int i=0; igetName(); + reqString+= "\n"; + anyReqs= true; + } + + string str= getName(); + if(anyReqs){ + return str + " " + Lang::getInstance().get("Reqs") + ":\n" + reqString; + } + else{ + return str; + } +} + +// ===================================================== +// class ProducibleType +// ===================================================== + +ProducibleType::ProducibleType(){ + cancelImage= NULL; +} + +ProducibleType::~ProducibleType(){ +} + +const Resource *ProducibleType::getCost(const ResourceType *rt) const{ + for(int i=0; igetAmount()!=0){ + str+= getCost(i)->getType()->getName(); + str+= ": "+ intToStr(getCost(i)->getAmount()); + str+= "\n"; + } + } + + for(int i=0; igetName(); + str+= "\n"; + } + + for(int i=0; igetName(); + str+= "\n"; + } + + return str; +} + +}}//end namespace diff --git a/source/glest_game/types/element_type.h b/source/glest_game/types/element_type.h new file mode 100644 index 00000000..11411409 --- /dev/null +++ b/source/glest_game/types/element_type.h @@ -0,0 +1,117 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_ELEMENTTYPE_H_ +#define _GLEST_GAME_ELEMENTTYPE_H_ + +#include +#include + +#include "texture.h" +#include "resource.h" + +using std::vector; +using std::string; + +using Shared::Graphics::Texture2D; + +namespace Glest{ namespace Game{ + +class UpgradeType; +class TechTree; +class UnitType; +class UpgradeType; +class DisplayableType; +class ResourceType; + +// ===================================================== +// class DisplayableType +// +/// Base class for anything that has a name and a portrait +// ===================================================== + +class DisplayableType{ +protected: + string name; //name + Texture2D *image; //portrait + +public: + DisplayableType(); + virtual ~DisplayableType(){}; + + //get + string getName() const {return name;} + const Texture2D *getImage() const {return image;} +}; + + +// ===================================================== +// class RequirableType +// +/// Base class for anything that has requirements +// ===================================================== + +class RequirableType: public DisplayableType{ +private: + typedef vector UnitReqs; + typedef vector UpgradeReqs; + +protected: + UnitReqs unitReqs; //needed units + UpgradeReqs upgradeReqs; //needed upgrades + +public: + //get + int getUpgradeReqCount() const {return upgradeReqs.size();} + int getUnitReqCount() const {return unitReqs.size();} + const UpgradeType *getUpgradeReq(int i) const {return upgradeReqs[i];} + const UnitType *getUnitReq(int i) const {return unitReqs[i];} + + //other + virtual string getReqDesc() const; +}; + + +// ===================================================== +// class ProducibleType +// +/// Base class for anything that can be produced +// ===================================================== + +class ProducibleType: public RequirableType{ +private: + typedef vector Costs; + +protected: + Costs costs; + Texture2D *cancelImage; + int productionTime; + +public: + ProducibleType(); + virtual ~ProducibleType(); + + //get + int getCostCount() const {return costs.size();} + const Resource *getCost(int i) const {return &costs[i];} + const Resource *getCost(const ResourceType *rt) const; + int getProductionTime() const {return productionTime;} + const Texture2D *getCancelImage() const {return cancelImage;} + + //varios + void checkCostStrings(TechTree *techTree); + + virtual string getReqDesc() const; +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/types/faction_type.cpp b/source/glest_game/types/faction_type.cpp new file mode 100644 index 00000000..e4ea8a10 --- /dev/null +++ b/source/glest_game/types/faction_type.cpp @@ -0,0 +1,156 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "faction_type.h" + +#include "logger.h" +#include "util.h" +#include "xml_parser.h" +#include "tech_tree.h" +#include "resource.h" +#include "platform_util.h" +#include "game_util.h" +#include "leak_dumper.h" + +using namespace Shared::Util; +using namespace Shared::Xml; + +namespace Glest{ namespace Game{ + +// ====================================================== +// Class FactionType +// ====================================================== + +FactionType::FactionType(){ + music= NULL; +} + +//load a faction, given a directory +void FactionType::load(const string &dir, const TechTree *techTree, Checksum* checksum){ + + name= lastDir(dir); + + Logger::getInstance().add("Faction type: "+ formatString(name), true); + + // a1) preload units + string unitsPath= dir + "/units/*."; + vector unitFilenames; + findAll(unitsPath, unitFilenames); + unitTypes.resize(unitFilenames.size()); + for(int i=0; i upgradeFilenames; + findAll(upgradesPath, upgradeFilenames); + upgradeTypes.resize(upgradeFilenames.size()); + for(int i=0; iaddFile(path); + + XmlTree xmlTree; + xmlTree.load(path); + const XmlNode *factionNode= xmlTree.getRootNode(); + + //read starting resources + const XmlNode *startingResourcesNode= factionNode->getChild("starting-resources"); + + startingResources.resize(startingResourcesNode->getChildCount()); + for(int i=0; igetChild("resource", i); + string name= resourceNode->getAttribute("name")->getRestrictedValue(); + int amount= resourceNode->getAttribute("amount")->getIntValue(); + startingResources[i].init(techTree->getResourceType(name), amount); + } + + //read starting units + const XmlNode *startingUnitsNode= factionNode->getChild("starting-units"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *unitNode= startingUnitsNode->getChild("unit", i); + string name= unitNode->getAttribute("name")->getRestrictedValue(); + int amount= unitNode->getAttribute("amount")->getIntValue(); + startingUnits.push_back(PairPUnitTypeInt(getUnitType(name), amount)); + } + + //read music + const XmlNode *musicNode= factionNode->getChild("music"); + bool value= musicNode->getAttribute("value")->getBoolValue(); + if(value){ + music= new StrSound(); + music->open(dir+"/"+musicNode->getAttribute("path")->getRestrictedValue()); + } +} + +FactionType::~FactionType(){ + delete music; +} + +// ==================== get ==================== + +const UnitType *FactionType::getUnitType(const string &name) const{ + for(int i=0; i PairPUnitTypeInt; + typedef vector UnitTypes; + typedef vector UpgradeTypes; + typedef vector StartingUnits; + typedef vector Resources; + +private: + string name; + UnitTypes unitTypes; + UpgradeTypes upgradeTypes; + StartingUnits startingUnits; + Resources startingResources; + StrSound *music; + +public: + //init + FactionType(); + void load(const string &dir, const TechTree *techTree, Checksum* checksum); + ~FactionType(); + + //get + int getUnitTypeCount() const {return unitTypes.size();} + int getUpgradeTypeCount() const {return upgradeTypes.size();} + string getName() const {return name;} + const UnitType *getUnitType(int i) const {return &unitTypes[i];} + const UpgradeType *getUpgradeType(int i) const {return &upgradeTypes[i];} + StrSound *getMusic() const {return music;} + int getStartingUnitCount() const {return startingUnits.size();} + const UnitType *getStartingUnit(int i) const {return startingUnits[i].first;} + int getStartingUnitAmount(int i) const {return startingUnits[i].second;} + + const UnitType *getUnitType(const string &name) const; + const UpgradeType *getUpgradeType(const string &name) const; + int getStartingResourceAmount(const ResourceType *resourceType) const; +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/types/object_type.cpp b/source/glest_game/types/object_type.cpp new file mode 100644 index 00000000..f9b0c76c --- /dev/null +++ b/source/glest_game/types/object_type.cpp @@ -0,0 +1,40 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "object_type.h" + +#include "renderer.h" +#include "leak_dumper.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class ObjectType +// ===================================================== + +void ObjectType::init(int modelCount, int objectClass, bool walkable){ + models.reserve(modelCount); + this->objectClass= objectClass; + this->walkable= walkable; +} + +void ObjectType::loadModel(const string &path){ + Model *model= Renderer::getInstance().newModel(rsGame); + model->load(path); + color= Vec3f(0.f); + if(model->getMeshCount()>0 && model->getMesh(0)->getTexture(0)!=NULL){ + const Pixmap2D *p= model->getMesh(0)->getTexture(0)->getPixmap(); + color= p->getPixel3f(p->getW()/2, p->getH()/2); + } + models.push_back(model); +} + +}}//end namespace diff --git a/source/glest_game/types/object_type.h b/source/glest_game/types/object_type.h new file mode 100644 index 00000000..0a943872 --- /dev/null +++ b/source/glest_game/types/object_type.h @@ -0,0 +1,62 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== +#ifndef _GLEST_GAME_OBJECTTYPE_H_ +#define _GLEST_GAME_OBJECTTYPE_H_ + +#include + +#include "model.h" +#include "vec.h" + +using std::vector; + +namespace Glest{ namespace Game{ + +using Shared::Graphics::Model; +using Shared::Graphics::Vec3f; + +// ===================================================== +// class ObjectType +// +/// Each of the possible objects of the map: trees, stones ... +// ===================================================== + +class ObjectType{ +private: + typedef vector Models; + +private: + static const int tree1= 0; + static const int tree2= 1; + static const int choppedTree= 2; + +private: + Models models; + Vec3f color; + int objectClass; + bool walkable; + +public: + void init(int modelCount, int objectClass, bool walkable); + + void loadModel(const string &path); + + Model *getModel(int i) {return models[i];} + int getModelCount() const {return models.size();} + const Vec3f &getColor() const {return color;} + int getClass() const {return objectClass;} + bool getWalkable() const {return walkable;} + bool isATree() const {return objectClass==tree1 || objectClass==tree2;} +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/types/resource_type.cpp b/source/glest_game/types/resource_type.cpp new file mode 100644 index 00000000..d5e46eb1 --- /dev/null +++ b/source/glest_game/types/resource_type.cpp @@ -0,0 +1,124 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "resource_type.h" + +#include "util.h" +#include "element_type.h" +#include "logger.h" +#include "renderer.h" +#include "xml_parser.h" +#include "game_util.h" +#include "leak_dumper.h" + +using namespace Shared::Util; +using namespace Shared::Xml; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class ResourceType +// ===================================================== + +void ResourceType::load(const string &dir, Checksum* checksum){ + + string path, str; + Renderer &renderer= Renderer::getInstance(); + + try{ + name= lastDir(dir); + + Logger::getInstance().add("Resource type: "+ formatString(name), true); + path= dir+"/"+name+".xml"; + + checksum->addFile(path); + + //tree + XmlTree xmlTree; + xmlTree.load(path); + const XmlNode *resourceNode= xmlTree.getRootNode(); + + //image + const XmlNode *imageNode= resourceNode->getChild("image"); + image= renderer.newTexture2D(rsGame); + image->load(dir+"/"+imageNode->getAttribute("path")->getRestrictedValue()); + + //type + const XmlNode *typeNode= resourceNode->getChild("type"); + resourceClass= strToRc(typeNode->getAttribute("value")->getRestrictedValue()); + + switch(resourceClass){ + case rcTech:{ + //model + const XmlNode *modelNode= typeNode->getChild("model"); + string path=dir+"/" + modelNode->getAttribute("path")->getRestrictedValue(); + + model= renderer.newModel(rsGame); + model->load(path); + + //default resources + const XmlNode *defaultAmountNode= typeNode->getChild("default-amount"); + defResPerPatch= defaultAmountNode->getAttribute("value")->getIntValue(); + + //resource number + const XmlNode *resourceNumberNode= typeNode->getChild("resource-number"); + resourceNumber= resourceNumberNode->getAttribute("value")->getIntValue(); + + } + break; + + case rcTileset:{ + //resource number + const XmlNode *defaultAmountNode= typeNode->getChild("default-amount"); + defResPerPatch= defaultAmountNode->getAttribute("value")->getIntValue(); + + //resource number + const XmlNode *tilesetObjectNode= typeNode->getChild("tileset-object"); + tilesetObject= tilesetObjectNode->getAttribute("value")->getIntValue(); + + } + break; + + case rcConsumable:{ + //interval + const XmlNode *intervalNode= typeNode->getChild("interval"); + interval= intervalNode->getAttribute("value")->getIntValue(); + } + break; + default: + break; + } + } + catch(const exception &e){ + throw runtime_error("Error loading resource type: " + path + "\n" + e.what()); + } +} + + +// ==================== misc ==================== + +ResourceClass ResourceType::strToRc(const string &s){ + if(s=="tech"){ + return rcTech; + } + if(s=="tileset"){ + return rcTileset; + } + if(s=="static"){ + return rcStatic; + } + if(s=="consumable"){ + return rcConsumable; + } + throw runtime_error("Error converting from string ro resourceClass, found: " + s); +} + +}}//end namespace diff --git a/source/glest_game/types/resource_type.h b/source/glest_game/types/resource_type.h new file mode 100644 index 00000000..cfd484d5 --- /dev/null +++ b/source/glest_game/types/resource_type.h @@ -0,0 +1,62 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_RESOURCETYPE_H_ +#define _GLEST_GAME_RESOURCETYPE_H_ + +#include "element_type.h" +#include "model.h" +#include "checksum.h" + +namespace Glest{ namespace Game{ + +using Shared::Graphics::Model; +using Shared::Util::Checksum; + +enum ResourceClass{ + rcTech, + rcTileset, + rcStatic, + rcConsumable +}; + +// ===================================================== +// class ResourceType +// +/// A type of resource that can be harvested or not +// ===================================================== + +class ResourceType: public DisplayableType{ +private: + ResourceClass resourceClass; + int tilesetObject; //used only if class==rcTileset + int resourceNumber; //used only if class==rcTech, resource number in the map + int interval; //used only if class==rcConsumable + int defResPerPatch; //used only if class==rcTileset || class==rcTech + Model *model; + +public: + void load(const string &dir, Checksum* checksum); + + //get + int getClass() const {return resourceClass;} + int getTilesetObject() const {return tilesetObject;} + int getResourceNumber() const {return resourceNumber;} + int getInterval() const {return interval;} + int getDefResPerPatch() const {return defResPerPatch;} + const Model *getModel() const {return model;} + + static ResourceClass strToRc(const string &s); +}; + +}} //end namespace + +#endif diff --git a/source/glest_game/types/skill_type.cpp b/source/glest_game/types/skill_type.cpp new file mode 100644 index 00000000..b7dd33d7 --- /dev/null +++ b/source/glest_game/types/skill_type.cpp @@ -0,0 +1,379 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "skill_type.h" + +#include + +#include "sound.h" +#include "util.h" +#include "lang.h" +#include "renderer.h" +#include "particle_type.h" +#include "tech_tree.h" +#include "faction_type.h" +#include "leak_dumper.h" + +using namespace Shared::Util; +using namespace Shared::Graphics; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class SkillType +// ===================================================== + +SkillType::~SkillType(){ + deleteValues(sounds.getSounds().begin(), sounds.getSounds().end()); +} + +void SkillType::load(const XmlNode *sn, const string &dir, const TechTree *tt, const FactionType *ft){ + //name + name= sn->getChild("name")->getAttribute("value")->getRestrictedValue(); + + //ep cost + mpCost= sn->getChild("ep-cost")->getAttribute("value")->getIntValue(); + + //speed + speed= sn->getChild("speed")->getAttribute("value")->getIntValue(); + + //anim speed + animSpeed= sn->getChild("anim-speed")->getAttribute("value")->getIntValue(); + + //model + string path= sn->getChild("animation")->getAttribute("path")->getRestrictedValue(); + animation= Renderer::getInstance().newModel(rsGame); + animation->load(dir + "/" + path); + + //sound + const XmlNode *soundNode= sn->getChild("sound"); + if(soundNode->getAttribute("enabled")->getBoolValue()){ + + soundStartTime= soundNode->getAttribute("start-time")->getFloatValue(); + + sounds.resize(soundNode->getChildCount()); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *soundFileNode= soundNode->getChild("sound-file", i); + string path= soundFileNode->getAttribute("path")->getRestrictedValue(); + StaticSound *sound= new StaticSound(); + sound->load(dir + "/" + path); + sounds[i]= sound; + } + } +} + +string SkillType::skillClassToStr(SkillClass skillClass){ + switch(skillClass){ + case scStop: return "Stop"; + case scMove: return "Move"; + case scAttack: return "Attack"; + case scHarvest: return "Harvest"; + case scRepair: return "Repair"; + case scBuild: return "Build"; + case scDie: return "Die"; + case scBeBuilt: return "Be Built"; + case scProduce: return "Produce"; + case scUpgrade: return "Upgrade"; + default: + assert(false); + return ""; + }; +} + +string SkillType::fieldToStr(Field field){ + switch(field){ + case fLand: return "Land"; + case fAir: return "Air"; + default: + assert(false); + return ""; + }; +} + + +// ===================================================== +// class StopSkillType +// ===================================================== + +StopSkillType::StopSkillType(){ + skillClass= scStop; +} + +string StopSkillType::toString() const{ + return Lang::getInstance().get("Stop"); +} + +// ===================================================== +// class MoveSkillType +// ===================================================== + +MoveSkillType::MoveSkillType(){ + skillClass= scMove; +} + +string MoveSkillType::toString() const{ + return Lang::getInstance().get("Move"); +} + +int MoveSkillType::getTotalSpeed(const TotalUpgrade *totalUpgrade) const{ + return speed + totalUpgrade->getMoveSpeed(); +} + +// ===================================================== +// class AttackSkillType +// ===================================================== + +//varios +AttackSkillType::AttackSkillType(){ + skillClass= scAttack; + attackType= NULL; + projectile= false; + splash= false; + splashRadius= 0; + projectileParticleSystemType= NULL; + splashParticleSystemType= NULL; + for(int i=0; igetChild("attack-strenght")->getAttribute("value")->getIntValue(); + attackVar= sn->getChild("attack-var")->getAttribute("value")->getIntValue(); + attackRange= sn->getChild("attack-range")->getAttribute("value")->getIntValue(); + string attackTypeName= sn->getChild("attack-type")->getAttribute("value")->getRestrictedValue(); + attackType= tt->getAttackType(attackTypeName); + attackStartTime= sn->getChild("attack-start-time")->getAttribute("value")->getFloatValue(); + + //attack fields + const XmlNode *attackFieldsNode= sn->getChild("attack-fields"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *fieldNode= attackFieldsNode->getChild("field", i); + string fieldName= fieldNode->getAttribute("value")->getRestrictedValue(); + if(fieldName=="land"){ + attackFields[fLand]= true; + } + else if(fieldName=="air"){ + attackFields[fAir]= true; + } + else{ + throw runtime_error("Not a valid field: "+fieldName+": "+ dir); + } + } + + //projectile + const XmlNode *projectileNode= sn->getChild("projectile"); + projectile= projectileNode->getAttribute("value")->getBoolValue(); + if(projectile){ + + //proj particle + const XmlNode *particleNode= projectileNode->getChild("particle"); + bool particleEnabled= particleNode->getAttribute("value")->getBoolValue(); + if(particleEnabled){ + string path= particleNode->getAttribute("path")->getRestrictedValue(); + projectileParticleSystemType= new ParticleSystemTypeProjectile(); + projectileParticleSystemType->load(dir, dir + "/" + path); + } + + //proj sounds + const XmlNode *soundNode= projectileNode->getChild("sound"); + if(soundNode->getAttribute("enabled")->getBoolValue()){ + + projSounds.resize(soundNode->getChildCount()); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *soundFileNode= soundNode->getChild("sound-file", i); + string path= soundFileNode->getAttribute("path")->getRestrictedValue(); + StaticSound *sound= new StaticSound(); + sound->load(dir + "/" + path); + projSounds[i]= sound; + } + } + } + + //splash + const XmlNode *splashNode= sn->getChild("splash"); + splash= splashNode->getAttribute("value")->getBoolValue(); + if(splash){ + splashRadius= splashNode->getChild("radius")->getAttribute("value")->getIntValue(); + splashDamageAll= splashNode->getChild("damage-all")->getAttribute("value")->getBoolValue(); + + //splash particle + const XmlNode *particleNode= splashNode->getChild("particle"); + bool particleEnabled= particleNode->getAttribute("value")->getBoolValue(); + if(particleEnabled){ + string path= particleNode->getAttribute("path")->getRestrictedValue(); + splashParticleSystemType= new ParticleSystemTypeSplash(); + splashParticleSystemType->load(dir, dir + "/" + path); + } + } +} + +string AttackSkillType::toString() const{ + return Lang::getInstance().get("Attack"); +} + +//get totals +int AttackSkillType::getTotalAttackStrength(const TotalUpgrade *totalUpgrade) const{ + return attackStrength + totalUpgrade->getAttackStrength(); +} + +int AttackSkillType::getTotalAttackRange(const TotalUpgrade *totalUpgrade) const{ + return attackRange + totalUpgrade->getAttackRange(); +} + +// ===================================================== +// class BuildSkillType +// ===================================================== + +BuildSkillType::BuildSkillType(){ + skillClass= scBuild; +} + +string BuildSkillType::toString() const{ + return Lang::getInstance().get("Build"); +} + +// ===================================================== +// class HarvestSkillType +// ===================================================== + +HarvestSkillType::HarvestSkillType(){ + skillClass= scHarvest; +} + +string HarvestSkillType::toString() const{ + return Lang::getInstance().get("Harvest"); +} + +// ===================================================== +// class RepairSkillType +// ===================================================== + +RepairSkillType::RepairSkillType(){ + skillClass= scRepair; +} + +string RepairSkillType::toString() const{ + return Lang::getInstance().get("Repair"); +} + +// ===================================================== +// class ProduceSkillType +// ===================================================== + +ProduceSkillType::ProduceSkillType(){ + skillClass= scProduce; +} + +string ProduceSkillType::toString() const{ + return Lang::getInstance().get("Produce"); +} + +int ProduceSkillType::getTotalSpeed(const TotalUpgrade *totalUpgrade) const{ + return speed + totalUpgrade->getProdSpeed(); +} + +// ===================================================== +// class UpgradeSkillType +// ===================================================== + +UpgradeSkillType::UpgradeSkillType(){ + skillClass= scUpgrade; +} + +string UpgradeSkillType::toString() const{ + return Lang::getInstance().get("Upgrade"); +} + +int UpgradeSkillType::getTotalSpeed(const TotalUpgrade *totalUpgrade) const{ + return speed + totalUpgrade->getProdSpeed(); +} + +// ===================================================== +// class BeBuiltSkillType +// ===================================================== + +BeBuiltSkillType::BeBuiltSkillType(){ + skillClass= scBeBuilt; +} + +string BeBuiltSkillType::toString() const{ + return "Be built"; +} + +// ===================================================== +// class MorphSkillType +// ===================================================== + +MorphSkillType::MorphSkillType(){ + skillClass= scMorph; +} + +string MorphSkillType::toString() const{ + return "Morph"; +} + +int MorphSkillType::getTotalSpeed(const TotalUpgrade *totalUpgrade) const{ + return speed + totalUpgrade->getProdSpeed(); +} + +// ===================================================== +// class DieSkillType +// ===================================================== + +DieSkillType::DieSkillType(){ + skillClass= scDie; +} + +void DieSkillType::load(const XmlNode *sn, const string &dir, const TechTree *tt, const FactionType *ft){ + SkillType::load(sn, dir, tt, ft); + + fade= sn->getChild("fade")->getAttribute("value")->getBoolValue(); +} + +string DieSkillType::toString() const{ + return "Die"; +} + +// ===================================================== +// class SkillTypeFactory +// ===================================================== + +SkillTypeFactory::SkillTypeFactory(){ + registerClass("stop"); + registerClass("move"); + registerClass("attack"); + registerClass("build"); + registerClass("be_built"); + registerClass("harvest"); + registerClass("repair"); + registerClass("produce"); + registerClass("upgrade"); + registerClass("morph"); + registerClass("die"); +} + +SkillTypeFactory &SkillTypeFactory::getInstance(){ + static SkillTypeFactory ctf; + return ctf; +} + +}} //end namespace diff --git a/source/glest_game/types/skill_type.h b/source/glest_game/types/skill_type.h new file mode 100644 index 00000000..e2935f16 --- /dev/null +++ b/source/glest_game/types/skill_type.h @@ -0,0 +1,284 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_SKILLTYPE_H_ +#define _GLEST_GAME_SKILLTYPE_H_ + +#include "sound.h" +#include "vec.h" +#include "model.h" +#include "xml_parser.h" +#include "util.h" +#include "damage_multiplier.h" +#include "element_type.h" +#include "factory.h" +#include "sound_container.h" + +using Shared::Sound::StaticSound; +using Shared::Xml::XmlNode; +using Shared::Graphics::Vec3f; +using Shared::Graphics::Model; + +namespace Glest{ namespace Game{ + +using Shared::Util::MultiFactory; + +class ParticleSystemTypeProjectile; +class ParticleSystemTypeSplash; +class FactionType; +class TechTree; +class Lang; +class TotalUpgrade; + +enum Field{ + fLand, + fAir, + + fieldCount +}; + +enum SkillClass{ + scStop, + scMove, + scAttack, + scBuild, + scHarvest, + scRepair, + scBeBuilt, + scProduce, + scUpgrade, + scMorph, + scDie, + + scCount +}; + +// ===================================================== +// class SkillType +// +/// A basic action that an unit can perform +// ===================================================== + +class SkillType{ +protected: + SkillClass skillClass; + string name; + int mpCost; + int speed; + int animSpeed; + Model *animation; + SoundContainer sounds; + float soundStartTime; + +public: + //varios + virtual ~SkillType(); + virtual void load(const XmlNode *sn, const string &dir, const TechTree *tt, const FactionType *ft); + + //get + const string &getName() const {return name;} + SkillClass getClass() const {return skillClass;} + int getEpCost() const {return mpCost;} + int getSpeed() const {return speed;} + int getAnimSpeed() const {return animSpeed;} + const Model *getAnimation() const {return animation;} + StaticSound *getSound() const {return sounds.getRandSound();} + float getSoundStartTime() const {return soundStartTime;} + + //other + virtual string toString() const= 0; + virtual int getTotalSpeed(const TotalUpgrade *) const {return speed;} + static string skillClassToStr(SkillClass skillClass); + static string fieldToStr(Field field); +}; + +// =============================== +// class StopSkillType +// =============================== + +class StopSkillType: public SkillType{ +public: + StopSkillType(); + virtual string toString() const; +}; + +// =============================== +// class MoveSkillType +// =============================== + +class MoveSkillType: public SkillType{ +public: + MoveSkillType(); + virtual string toString() const; + + virtual int getTotalSpeed(const TotalUpgrade *totalUpgrade) const; +}; + +// =============================== +// class AttackSkillType +// =============================== + +class AttackSkillType: public SkillType{ +private: + int attackStrength; + int attackVar; + int attackRange; + const AttackType *attackType; + bool attackFields[fieldCount]; + float attackStartTime; + + bool projectile; + ParticleSystemTypeProjectile* projectileParticleSystemType; + SoundContainer projSounds; + + bool splash; + int splashRadius; + bool splashDamageAll; + ParticleSystemTypeSplash* splashParticleSystemType; + +public: + AttackSkillType(); + ~AttackSkillType(); + virtual void load(const XmlNode *sn, const string &dir, const TechTree *tt, const FactionType *ft); + virtual string toString() const; + + //get + int getAttackStrength() const {return attackStrength;} + int getAttackVar() const {return attackVar;} + int getAttackRange() const {return attackRange;} + const AttackType *getAttackType() const {return attackType;} + bool getAttackField(Field field) const {return attackFields[field];} + float getAttackStartTime() const {return attackStartTime;} + + //get proj + bool getProjectile() const {return projectile;} + ParticleSystemTypeProjectile * getProjParticleType() const {return projectileParticleSystemType;} + StaticSound *getProjSound() const {return projSounds.getRandSound();} + + //get splash + bool getSplash() const {return splash;} + int getSplashRadius() const {return splashRadius;} + bool getSplashDamageAll() const {return splashDamageAll;} + ParticleSystemTypeSplash * getSplashParticleType() const {return splashParticleSystemType;} + + //misc + int getTotalAttackStrength(const TotalUpgrade *totalUpgrade) const; + int getTotalAttackRange(const TotalUpgrade *totalUpgrade) const; +}; + + +// =============================== +// class BuildSkillType +// =============================== + +class BuildSkillType: public SkillType{ +public: + BuildSkillType(); + virtual string toString() const; +}; + +// =============================== +// class HarvestSkillType +// =============================== + +class HarvestSkillType: public SkillType{ +public: + HarvestSkillType(); + virtual string toString() const; +}; + +// =============================== +// class RepairSkillType +// =============================== + +class RepairSkillType: public SkillType{ +public: + RepairSkillType(); + virtual string toString() const; +}; + +// =============================== +// class ProduceSkillType +// =============================== + +class ProduceSkillType: public SkillType{ +public: + ProduceSkillType(); + virtual string toString() const; + + virtual int getTotalSpeed(const TotalUpgrade *totalUpgrade) const; +}; + +// =============================== +// class UpgradeSkillType +// =============================== + +class UpgradeSkillType: public SkillType{ +public: + UpgradeSkillType(); + virtual string toString() const; + + virtual int getTotalSpeed(const TotalUpgrade *totalUpgrade) const; +}; + + +// =============================== +// class BeBuiltSkillType +// =============================== + +class BeBuiltSkillType: public SkillType{ +public: + BeBuiltSkillType(); + virtual string toString() const; +}; + +// =============================== +// class MorphSkillType +// =============================== + +class MorphSkillType: public SkillType{ +public: + MorphSkillType(); + virtual string toString() const; + + virtual int getTotalSpeed(const TotalUpgrade *totalUpgrade) const; +}; + +// =============================== +// class DieSkillType +// =============================== + +class DieSkillType: public SkillType{ +private: + bool fade; + +public: + DieSkillType(); + bool getFade() const {return fade;} + + virtual void load(const XmlNode *sn, const string &dir, const TechTree *tt, const FactionType *ft); + virtual string toString() const; +}; + +// =============================== +// class SkillFactory +// =============================== + +class SkillTypeFactory: public MultiFactory{ +private: + SkillTypeFactory(); +public: + static SkillTypeFactory &getInstance(); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/types/tech_tree.cpp b/source/glest_game/types/tech_tree.cpp new file mode 100644 index 00000000..1054777e --- /dev/null +++ b/source/glest_game/types/tech_tree.cpp @@ -0,0 +1,190 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "tech_tree.h" + +#include + +#include "util.h" +#include "resource.h" +#include "faction_type.h" +#include "logger.h" +#include "xml_parser.h" +#include "platform_util.h" +#include "game_util.h" +#include "leak_dumper.h" + +using namespace Shared::Util; +using namespace Shared::Xml; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class TechTree +// ===================================================== + +void TechTree::load(const string &dir, set &factions, Checksum* checksum){ + + string str; + vector filenames; + string name= lastDir(dir); + + Logger::getInstance().add("TechTree: "+ formatString(name), true); + + //load resources + str= dir+"/resources/*."; + + try{ + findAll(str, filenames); + resourceTypes.resize(filenames.size()); + + for(int i=0; iaddFile(path); + + xmlTree.load(path); + const XmlNode *techTreeNode= xmlTree.getRootNode(); + + //attack types + const XmlNode *attackTypesNode= techTreeNode->getChild("attack-types"); + attackTypes.resize(attackTypesNode->getChildCount()); + for(int i=0; igetChild("attack-type", i); + attackTypes[i].setName(attackTypeNode->getAttribute("name")->getRestrictedValue()); + attackTypes[i].setId(i); + } + + //armor types + const XmlNode *armorTypesNode= techTreeNode->getChild("armor-types"); + armorTypes.resize(armorTypesNode->getChildCount()); + for(int i=0; igetChild("armor-type", i); + armorTypes[i].setName(armorTypeNode->getAttribute("name")->getRestrictedValue()); + armorTypes[i].setId(i); + } + + //damage multipliers + damageMultiplierTable.init(attackTypes.size(), armorTypes.size()); + const XmlNode *damageMultipliersNode= techTreeNode->getChild("damage-multipliers"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *damageMultiplierNode= damageMultipliersNode->getChild("damage-multiplier", i); + const AttackType *attackType= getAttackType(damageMultiplierNode->getAttribute("attack")->getRestrictedValue()); + const ArmorType *armorType= getArmorType(damageMultiplierNode->getAttribute("armor")->getRestrictedValue()); + float multiplier= damageMultiplierNode->getAttribute("value")->getFloatValue(); + damageMultiplierTable.setDamageMultiplier(attackType, armorType, multiplier); + } + } + catch(const exception &e){ + throw runtime_error("Error loading Tech Tree: "+ dir + "\n" + e.what()); + } + + //load factions + str= dir+"/factions/*."; + try{ + factionTypes.resize(factions.size()); + + int i=0; + for ( set::iterator it = factions.begin(); it != factions.end(); ++it ) { + str=dir+"/factions/" + *it; + factionTypes[i++].load(str, this, checksum); + } + } + catch(const exception &e){ + throw runtime_error("Error loading Faction Types: "+ dir + "\n" + e.what()); + } + +} + +TechTree::~TechTree(){ + Logger::getInstance().add("Tech tree", true); +} + + +// ==================== get ==================== + +const FactionType *TechTree::getType(const string &name) const{ + for(int i=0; igetResourceNumber()==i && rt->getClass()==rcTech) + return getResourceType(j); + } + + return getFirstTechResourceType(); +} + +const ResourceType *TechTree::getFirstTechResourceType() const{ + for(int i=0; igetResourceNumber()==1 && rt->getClass()==rcTech) + return getResourceType(i); + } + + throw runtime_error("This tech tree has not tech resources, one at least is required"); +} + +const ResourceType *TechTree::getResourceType(const string &name) const{ + + for(int i=0; i + +#include "util.h" +#include "resource_type.h" +#include "faction_type.h" +#include "damage_multiplier.h" + +namespace Glest{ namespace Game{ + +// ===================================================== +// class TechTree +// +/// A set of factions and resources +// ===================================================== + +class TechTree{ +private: + typedef vector ResourceTypes; + typedef vector FactionTypes; + typedef vector ArmorTypes; + typedef vector AttackTypes; + +private: + string desc; + ResourceTypes resourceTypes; + FactionTypes factionTypes; + ArmorTypes armorTypes; + AttackTypes attackTypes; + DamageMultiplierTable damageMultiplierTable; + +public: + void load(const string &dir, set &factions, Checksum* checksum); + ~TechTree(); + + //get + int getResourceTypeCount() const {return resourceTypes.size();} + int getTypeCount() const {return factionTypes.size();} + const FactionType *getType(int i) const {return &factionTypes[i];} + const ResourceType *getResourceType(int i) const {return &resourceTypes[i];} + const string &getDesc() const {return desc;} + const FactionType *getType(const string &name) const; + const ResourceType *getResourceType(const string &name) const; + const ResourceType *getTechResourceType(int i) const; + const ResourceType *getFirstTechResourceType() const; + const ArmorType *getArmorType(const string &name) const; + const AttackType *getAttackType(const string &name) const; + float getDamageMultiplier(const AttackType *att, const ArmorType *art) const; +}; + +}} //end namespace + +#endif diff --git a/source/glest_game/types/unit_type.cpp b/source/glest_game/types/unit_type.cpp new file mode 100644 index 00000000..dfa9992a --- /dev/null +++ b/source/glest_game/types/unit_type.cpp @@ -0,0 +1,499 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "unit_type.h" + +#include + +#include "util.h" +#include "upgrade_type.h" +#include "resource_type.h" +#include "sound.h" +#include "logger.h" +#include "xml_parser.h" +#include "tech_tree.h" +#include "resource.h" +#include "renderer.h" +#include "game_util.h" +#include "leak_dumper.h" + +using namespace Shared::Xml; +using namespace Shared::Graphics; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// =============================== +// class Level +// =============================== + +void Level::init(string name, int kills){ + this->name= name; + this->kills= kills; +} + +// ===================================================== +// class UnitType +// ===================================================== + +// ===================== PUBLIC ======================== + +const char *UnitType::propertyNames[]= {"burnable", "rotated_climb"}; + +// ==================== creation and loading ==================== + +UnitType::UnitType(){ + + lightColor= Vec3f(0.f); + light= false; + multiSelect= false; + armorType= NULL; + + for(int i=0; iid= id; + string path; + + try{ + + Logger::getInstance().add("Unit type: " + formatString(name), true); + + //file load + path= dir+"/"+name+".xml"; + + checksum->addFile(path); + + XmlTree xmlTree; + xmlTree.load(path); + const XmlNode *unitNode= xmlTree.getRootNode(); + + const XmlNode *parametersNode= unitNode->getChild("parameters"); + + //size + size= parametersNode->getChild("size")->getAttribute("value")->getIntValue(); + + //height + height= parametersNode->getChild("height")->getAttribute("value")->getIntValue(); + + //maxHp + maxHp= parametersNode->getChild("max-hp")->getAttribute("value")->getIntValue(); + + //hpRegeneration + hpRegeneration= parametersNode->getChild("max-hp")->getAttribute("regeneration")->getIntValue(); + + //maxEp + maxEp= parametersNode->getChild("max-ep")->getAttribute("value")->getIntValue(); + + if(maxEp!=0){ + //wpRegeneration + epRegeneration= parametersNode->getChild("max-ep")->getAttribute("regeneration")->getIntValue(); + } + + //armor + armor= parametersNode->getChild("armor")->getAttribute("value")->getIntValue(); + + //armor type string + string armorTypeName= parametersNode->getChild("armor-type")->getAttribute("value")->getRestrictedValue(); + armorType= techTree->getArmorType(armorTypeName); + + //sight + sight= parametersNode->getChild("sight")->getAttribute("value")->getIntValue(); + + //prod time + productionTime= parametersNode->getChild("time")->getAttribute("value")->getIntValue(); + + //multi selection + multiSelect= parametersNode->getChild("multi-selection")->getAttribute("value")->getBoolValue(); + + //cellmap + const XmlNode *cellMapNode= parametersNode->getChild("cellmap"); + bool hasCellMap= cellMapNode->getAttribute("value")->getBoolValue(); + if(hasCellMap){ + cellMap= new bool[size*size]; + for(int i=0; igetChild("row", i); + string row= rowNode->getAttribute("value")->getRestrictedValue(); + if(row.size()!=size){ + throw runtime_error("Cellmap row has not the same length as unit size"); + } + for(int j=0; jgetChild("levels"); + levels.resize(levelsNode->getChildCount()); + for(int i=0; igetChild("level", i); + levels[i].init( + levelNode->getAttribute("name")->getRestrictedValue(), + levelNode->getAttribute("kills")->getIntValue()); + } + + //fields + const XmlNode *fieldsNode= parametersNode->getChild("fields"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *fieldNode= fieldsNode->getChild("field", i); + string fieldName= fieldNode->getAttribute("value")->getRestrictedValue(); + if(fieldName=="land"){ + fields[fLand]= true; + } + else if(fieldName=="air"){ + fields[fAir]= true; + } + else{ + throw runtime_error("Not a valid field: "+fieldName+": "+ path); + } + } + + //properties + const XmlNode *propertiesNode= parametersNode->getChild("properties"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *propertyNode= propertiesNode->getChild("property", i); + string propertyName= propertyNode->getAttribute("value")->getRestrictedValue(); + bool found= false; + for(int i=0; igetChild("light"); + light= lightNode->getAttribute("enabled")->getBoolValue(); + if(light){ + lightColor.x= lightNode->getAttribute("red")->getFloatValue(0.f, 1.f); + lightColor.y= lightNode->getAttribute("green")->getFloatValue(0.f, 1.f); + lightColor.z= lightNode->getAttribute("blue")->getFloatValue(0.f, 1.f); + } + + //unit requirements + const XmlNode *unitRequirementsNode= parametersNode->getChild("unit-requirements"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *unitNode= unitRequirementsNode->getChild("unit", i); + string name= unitNode->getAttribute("name")->getRestrictedValue(); + unitReqs.push_back(factionType->getUnitType(name)); + } + + //upgrade requirements + const XmlNode *upgradeRequirementsNode= parametersNode->getChild("upgrade-requirements"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *upgradeReqNode= upgradeRequirementsNode->getChild("upgrade", i); + string name= upgradeReqNode->getAttribute("name")->getRestrictedValue(); + upgradeReqs.push_back(factionType->getUpgradeType(name)); + } + + //resource requirements + const XmlNode *resourceRequirementsNode= parametersNode->getChild("resource-requirements"); + costs.resize(resourceRequirementsNode->getChildCount()); + for(int i=0; igetChild("resource", i); + string name= resourceNode->getAttribute("name")->getRestrictedValue(); + int amount= resourceNode->getAttribute("amount")->getIntValue(); + costs[i].init(techTree->getResourceType(name), amount); + } + + //resources stored + const XmlNode *resourcesStoredNode= parametersNode->getChild("resources-stored"); + storedResources.resize(resourcesStoredNode->getChildCount()); + for(int i=0; igetChild("resource", i); + string name= resourceNode->getAttribute("name")->getRestrictedValue(); + int amount= resourceNode->getAttribute("amount")->getIntValue(); + storedResources[i].init(techTree->getResourceType(name), amount); + } + + //image + const XmlNode *imageNode= parametersNode->getChild("image"); + image= Renderer::getInstance().newTexture2D(rsGame); + image->load(dir+"/"+imageNode->getAttribute("path")->getRestrictedValue()); + + //image cancel + const XmlNode *imageCancelNode= parametersNode->getChild("image-cancel"); + cancelImage= Renderer::getInstance().newTexture2D(rsGame); + cancelImage->load(dir+"/"+imageCancelNode->getAttribute("path")->getRestrictedValue()); + + //meeting point + const XmlNode *meetingPointNode= parametersNode->getChild("meeting-point"); + meetingPoint= meetingPointNode->getAttribute("value")->getBoolValue(); + if(meetingPoint){ + meetingPointImage= Renderer::getInstance().newTexture2D(rsGame); + meetingPointImage->load(dir+"/"+meetingPointNode->getAttribute("image-path")->getRestrictedValue()); + } + + //selection sounds + const XmlNode *selectionSoundNode= parametersNode->getChild("selection-sounds"); + if(selectionSoundNode->getAttribute("enabled")->getBoolValue()){ + selectionSounds.resize(selectionSoundNode->getChildCount()); + for(int i=0; igetChild("sound", i); + string path= soundNode->getAttribute("path")->getRestrictedValue(); + StaticSound *sound= new StaticSound(); + sound->load(dir + "/" + path); + selectionSounds[i]= sound; + } + } + + //command sounds + const XmlNode *commandSoundNode= parametersNode->getChild("command-sounds"); + if(commandSoundNode->getAttribute("enabled")->getBoolValue()){ + commandSounds.resize(commandSoundNode->getChildCount()); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *soundNode= commandSoundNode->getChild("sound", i); + string path= soundNode->getAttribute("path")->getRestrictedValue(); + StaticSound *sound= new StaticSound(); + sound->load(dir + "/" + path); + commandSounds[i]= sound; + } + } + + //skills + const XmlNode *skillsNode= unitNode->getChild("skills"); + skillTypes.resize(skillsNode->getChildCount()); + for(int i=0; igetChild("skill", i); + const XmlNode *typeNode= sn->getChild("type"); + string classId= typeNode->getAttribute("value")->getRestrictedValue(); + SkillType *skillType= SkillTypeFactory::getInstance().newInstance(classId); + skillType->load(sn, dir, techTree, factionType); + skillTypes[i]= skillType; + } + + //commands + const XmlNode *commandsNode= unitNode->getChild("commands"); + commandTypes.resize(commandsNode->getChildCount()); + for(int i=0; igetChild("command", i); + const XmlNode *typeNode= commandNode->getChild("type"); + string classId= typeNode->getAttribute("value")->getRestrictedValue(); + CommandType *commandType= CommandTypeFactory::getInstance().newInstance(classId); + commandType->load(i, commandNode, dir, techTree, factionType, *this); + commandTypes[i]= commandType; + } + + computeFirstStOfClass(); + computeFirstCtOfClass(); + + if(getFirstStOfClass(scStop)==NULL){ + throw runtime_error("Every unit must have at least one stop skill: "+ path); + } + if(getFirstStOfClass(scDie)==NULL){ + throw runtime_error("Every unit must have at least one die skill: "+ path); + } + + } + //Exception handling (conversions and so on); + catch(const exception &e){ + throw runtime_error("Error loading UnitType: " + path + "\n" + e.what()); + } +} + +// ==================== get ==================== + +const CommandType *UnitType::getFirstCtOfClass(CommandClass commandClass) const{ + return firstCommandTypeOfClass[commandClass]; +} + +const SkillType *UnitType::getFirstStOfClass(SkillClass skillClass) const{ + return firstSkillTypeOfClass[skillClass]; +} + +const HarvestCommandType *UnitType::getFirstHarvestCommand(const ResourceType *resourceType) const{ + for(int i=0; igetClass()== ccHarvest){ + const HarvestCommandType *hct= static_cast(commandTypes[i]); + if(hct->canHarvest(resourceType)){ + return hct; + } + } + } + return NULL; +} + +const AttackCommandType *UnitType::getFirstAttackCommand(Field field) const{ + for(int i=0; igetClass()== ccAttack){ + const AttackCommandType *act= static_cast(commandTypes[i]); + if(act->getAttackSkillType()->getAttackField(field)){ + return act; + } + } + } + return NULL; +} + +const RepairCommandType *UnitType::getFirstRepairCommand(const UnitType *repaired) const{ + for(int i=0; igetClass()== ccRepair){ + const RepairCommandType *rct= static_cast(commandTypes[i]); + if(rct->isRepairableUnitType(repaired)){ + return rct; + } + } + } + return NULL; +} + +int UnitType::getStore(const ResourceType *rt) const{ + for(int i=0; igetName()==skillName){ + if(skillTypes[i]->getClass()==skillClass){ + return skillTypes[i]; + } + else{ + throw runtime_error("Skill \""+skillName+"\" is not of class \""+SkillType::skillClassToStr(skillClass)); + } + } + } + throw runtime_error("No skill named \""+skillName+"\""); +} + +// ==================== totals ==================== + +int UnitType::getTotalMaxHp(const TotalUpgrade *totalUpgrade) const{ + return maxHp + totalUpgrade->getMaxHp(); +} + +int UnitType::getTotalMaxEp(const TotalUpgrade *totalUpgrade) const{ + return maxEp + totalUpgrade->getMaxEp(); +} + +int UnitType::getTotalArmor(const TotalUpgrade *totalUpgrade) const{ + return armor + totalUpgrade->getArmor(); +} + +int UnitType::getTotalSight(const TotalUpgrade *totalUpgrade) const{ + return sight + totalUpgrade->getSight(); +} + +// ==================== has ==================== + +bool UnitType::hasSkillClass(SkillClass skillClass) const{ + return firstSkillTypeOfClass[skillClass]!=NULL; +} + +bool UnitType::hasCommandType(const CommandType *commandType) const{ + assert(commandType!=NULL); + for(int i=0; igetClass()== SkillClass(j)){ + firstSkillTypeOfClass[j]= skillTypes[i]; + break; + } + } + } +} + +void UnitType::computeFirstCtOfClass(){ + for(int j=0; jgetClass()== CommandClass(j)){ + firstCommandTypeOfClass[j]= commandTypes[i]; + break; + } + } + } +} + +const CommandType* UnitType::findCommandTypeById(int id) const{ + for(int i=0; igetId()==id){ + return commandType; + } + } + return NULL; +} + +}}//end namespace diff --git a/source/glest_game/types/unit_type.h b/source/glest_game/types/unit_type.h new file mode 100644 index 00000000..316c7ebb --- /dev/null +++ b/source/glest_game/types/unit_type.h @@ -0,0 +1,189 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_UNITTYPE_H_ +#define _GLEST_GAME_UNITTYPE_H_ + +#include "element_type.h" +#include "command_type.h" +#include "damage_multiplier.h" +#include "sound_container.h" +#include "checksum.h" + +namespace Glest{ namespace Game{ + +using Shared::Sound::StaticSound; +using Shared::Util::Checksum; + +class UpgradeType; +class UnitType; +class ResourceType; +class TechTree; +class FactionType; + + +// =============================== +// class Level +// =============================== + +class Level{ +private: + string name; + int kills; + +public: + void init(string name, int kills); + + const string &getName() const {return name;} + int getKills() const {return kills;} +}; + +// =============================== +// class UnitType +// +/// A unit or building type +// =============================== + +enum UnitClass{ + ucWarrior, + ucWorker, + ucBuilding +}; + +class UnitType: public ProducibleType{ +public: + enum Property{ + pBurnable, + pRotatedClimb, + + pCount + }; + + static const char *propertyNames[]; + +private: + typedef vector SkillTypes; + typedef vector CommandTypes; + typedef vector StoredResources; + typedef vector Levels; + +private: + //basic + int id; + int maxHp; + int hpRegeneration; + int maxEp; + int epRegeneration; + bool fields[fieldCount]; //fields: land, sea or air + bool properties[pCount]; //properties + int armor; //armor + const ArmorType *armorType; + bool light; + Vec3f lightColor; + bool multiSelect; + int sight; + int size; //size in cells + int height; + + //cellmap + bool *cellMap; + + //sounds + SoundContainer selectionSounds; + SoundContainer commandSounds; + + //info + SkillTypes skillTypes; + CommandTypes commandTypes; + StoredResources storedResources; + Levels levels; + + //meeting point + bool meetingPoint; + Texture2D *meetingPointImage; + + //OPTIMIZATION: store first command type and skill type of each class + const CommandType *firstCommandTypeOfClass[ccCount]; + const SkillType *firstSkillTypeOfClass[scCount]; + +public: + //creation and loading + UnitType(); + virtual ~UnitType(); + void preLoad(const string &dir); + void load(int id, const string &dir, const TechTree *techTree, const FactionType *factionType, Checksum* checksum); + + //get + int getId() const {return id;} + int getMaxHp() const {return maxHp;} + int getHpRegeneration() const {return hpRegeneration;} + int getMaxEp() const {return maxEp;} + int getEpRegeneration() const {return epRegeneration;} + bool getField(Field field) const {return fields[field];} + bool getProperty(Property property) const {return properties[property];} + int getArmor() const {return armor;} + const ArmorType *getArmorType() const {return armorType;} + const SkillType *getSkillType(int i) const {return skillTypes[i];} + const CommandType *getCommandType(int i) const {return commandTypes[i];} + const Level *getLevel(int i) const {return &levels[i];} + int getSkillTypeCount() const {return skillTypes.size();} + int getCommandTypeCount() const {return commandTypes.size();} + int getLevelCount() const {return levels.size();} + bool getLight() const {return light;} + Vec3f getLightColor() const {return lightColor;} + bool getMultiSelect() const {return multiSelect;} + int getSight() const {return sight;} + int getSize() const {return size;} + int getHeight() const {return height;} + int getStoredResourceCount() const {return storedResources.size();} + const Resource *getStoredResource(int i) const {return &storedResources[i];} + bool getCellMapCell(int x, int y) const {return cellMap[size*y+x];} + bool getMeetingPoint() const {return meetingPoint;} + Texture2D *getMeetingPointImage() const {return meetingPointImage;} + StaticSound *getSelectionSound() const {return selectionSounds.getRandSound();} + StaticSound *getCommandSound() const {return commandSounds.getRandSound();} + + int getStore(const ResourceType *rt) const; + const SkillType *getSkillType(const string &skillName, SkillClass skillClass) const; + const SkillType *getFirstStOfClass(SkillClass skillClass) const; + const CommandType *getFirstCtOfClass(CommandClass commandClass) const; + const HarvestCommandType *getFirstHarvestCommand(const ResourceType *resourceType) const; + const AttackCommandType *getFirstAttackCommand(Field field) const; + const RepairCommandType *getFirstRepairCommand(const UnitType *repaired) const; + + //get totals + int getTotalMaxHp(const TotalUpgrade *totalUpgrade) const; + int getTotalMaxEp(const TotalUpgrade *totalUpgrade) const; + int getTotalArmor(const TotalUpgrade *totalUpgrade) const; + int getTotalSight(const TotalUpgrade *totalUpgrade) const; + + //has + bool hasCommandType(const CommandType *commandType) const; + bool hasCommandClass(CommandClass commandClass) const; + bool hasSkillType(const SkillType *skillType) const; + bool hasSkillClass(SkillClass skillClass) const; + bool hasCellMap() const {return cellMap!=NULL;} + + //is + bool isOfClass(UnitClass uc) const; + + //find + const CommandType* findCommandTypeById(int id) const; + +private: + void computeFirstStOfClass(); + void computeFirstCtOfClass(); +}; + +}}//end namespace + + +#endif diff --git a/source/glest_game/types/upgrade_type.cpp b/source/glest_game/types/upgrade_type.cpp new file mode 100644 index 00000000..8409990c --- /dev/null +++ b/source/glest_game/types/upgrade_type.cpp @@ -0,0 +1,209 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "upgrade_type.h" + +#include +#include + +#include "unit_type.h" +#include "util.h" +#include "logger.h" +#include "lang.h" +#include "xml_parser.h" +#include "tech_tree.h" +#include "faction_type.h" +#include "resource.h" +#include "renderer.h" +#include "game_util.h" +#include "leak_dumper.h" + +using namespace Shared::Util; +using namespace Shared::Xml; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class UpgradeType +// ===================================================== + +// ==================== get ==================== + +bool UpgradeType::isAffected(const UnitType *unitType) const{ + return find(effects.begin(), effects.end(), unitType)!=effects.end(); +} + +// ==================== misc ==================== + +void UpgradeType::preLoad(const string &dir){ + name=lastDir(dir); +} + +void UpgradeType::load(const string &dir, const TechTree *techTree, const FactionType *factionType, Checksum* checksum){ + string path; + + Logger::getInstance().add("Upgrade type: "+ formatString(name), true); + + path=dir+"/"+name+".xml"; + + try{ + checksum->addFile(path); + + XmlTree xmlTree; + xmlTree.load(path); + const XmlNode *upgradeNode= xmlTree.getRootNode(); + + //image + const XmlNode *imageNode= upgradeNode->getChild("image"); + image= Renderer::getInstance().newTexture2D(rsGame); + image->load(dir+"/"+imageNode->getAttribute("path")->getRestrictedValue()); + + //image cancel + const XmlNode *imageCancelNode= upgradeNode->getChild("image-cancel"); + cancelImage= Renderer::getInstance().newTexture2D(rsGame); + cancelImage->load(dir+"/"+imageCancelNode->getAttribute("path")->getRestrictedValue()); + + //upgrade time + const XmlNode *upgradeTimeNode= upgradeNode->getChild("time"); + productionTime= upgradeTimeNode->getAttribute("value")->getIntValue(); + + //unit requirements + const XmlNode *unitRequirementsNode= upgradeNode->getChild("unit-requirements"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *unitNode= unitRequirementsNode->getChild("unit", i); + string name= unitNode->getAttribute("name")->getRestrictedValue(); + unitReqs.push_back(factionType->getUnitType(name)); + } + + //upgrade requirements + const XmlNode *upgradeRequirementsNode= upgradeNode->getChild("upgrade-requirements"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *upgradeReqNode= upgradeRequirementsNode->getChild("upgrade", i); + string name= upgradeReqNode->getAttribute("name")->getRestrictedValue(); + upgradeReqs.push_back(factionType->getUpgradeType(name)); + } + + //resource requirements + const XmlNode *resourceRequirementsNode= upgradeNode->getChild("resource-requirements"); + costs.resize(resourceRequirementsNode->getChildCount()); + for(int i=0; igetChild("resource", i); + string name= resourceNode->getAttribute("name")->getRestrictedValue(); + int amount= resourceNode->getAttribute("amount")->getIntValue(); + costs[i].init(techTree->getResourceType(name), amount); + } + + //effects + const XmlNode *effectsNode= upgradeNode->getChild("effects"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *unitNode= effectsNode->getChild("unit", i); + string name= unitNode->getAttribute("name")->getRestrictedValue(); + effects.push_back(factionType->getUnitType(name)); + } + + //values + maxHp= upgradeNode->getChild("max-hp")->getAttribute("value")->getIntValue(); + maxEp= upgradeNode->getChild("max-ep")->getAttribute("value")->getIntValue(); + sight= upgradeNode->getChild("sight")->getAttribute("value")->getIntValue(); + attackStrength= upgradeNode->getChild("attack-strenght")->getAttribute("value")->getIntValue(); + attackRange= upgradeNode->getChild("attack-range")->getAttribute("value")->getIntValue(); + armor= upgradeNode->getChild("armor")->getAttribute("value")->getIntValue(); + moveSpeed= upgradeNode->getChild("move-speed")->getAttribute("value")->getIntValue(); + prodSpeed= upgradeNode->getChild("production-speed")->getAttribute("value")->getIntValue(); + + } + catch(const exception &e){ + throw runtime_error("Error loading UpgradeType: "+ dir + "\n" +e.what()); + } +} + +string UpgradeType::getReqDesc() const{ + + string str; + int i; + Lang &lang= Lang::getInstance(); + + str= ProducibleType::getReqDesc(); + if(getEffectCount()>0){ + str+= "\n"+ lang.get("Upgrades")+":\n"; + for(i=0; igetName()+"\n"; + } + } + + if(maxHp!=0){ + str+= lang.get("Hp")+" +"+intToStr(maxHp); + } + if(sight!=0){ + str+= lang.get("Sight")+" +"+intToStr(sight); + } + if(maxEp!=0){ + str+= lang.get("Ep")+" +"+intToStr(maxEp)+"\n"; + } + if(attackStrength!=0){ + str+= lang.get("AttackStrenght")+" +"+intToStr(attackStrength)+"\n"; + } + if(attackRange!=0){ + str+= lang.get("AttackDistance")+" +"+intToStr(attackRange)+"\n"; + } + if(armor!=0){ + str+= lang.get("Armor")+" +"+intToStr(armor)+"\n"; + } + if(moveSpeed!=0){ + str+= lang.get("WalkSpeed")+"+ "+intToStr(moveSpeed)+"\n"; + } + if(prodSpeed!=0){ + str+= lang.get("ProductionSpeed")+" +"+intToStr(prodSpeed)+"\n"; + } + + return str; +} + + + +// =============================== +// class TotalUpgrade +// =============================== + +TotalUpgrade::TotalUpgrade(){ + reset(); +} + +void TotalUpgrade::reset(){ + maxHp= 0; + maxEp= 0; + sight=0; + armor= 0; + attackStrength= 0; + attackRange= 0; + moveSpeed= 0; + prodSpeed=0; +} + +void TotalUpgrade::sum(const UpgradeType *ut){ + maxHp+= ut->getMaxHp(); + maxEp+= ut->getMaxEp(); + sight+= ut->getSight(); + armor+= ut->getArmor(); + attackStrength+= ut->getAttackStrength(); + attackRange+= ut->getAttackRange(); + moveSpeed+= ut->getMoveSpeed(); + prodSpeed+= ut->getProdSpeed(); +} + +void TotalUpgrade::incLevel(const UnitType *ut){ + maxHp+= ut->getMaxHp()*50/100; + maxEp+= ut->getMaxEp()*50/100; + sight+= ut->getSight()*20/100; + armor+= ut->getArmor()*50/100; +} + +}}//end namespace diff --git a/source/glest_game/types/upgrade_type.h b/source/glest_game/types/upgrade_type.h new file mode 100644 index 00000000..549c2f6b --- /dev/null +++ b/source/glest_game/types/upgrade_type.h @@ -0,0 +1,88 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_UPGRADETYPE_H_ +#define _GLEST_GAME_UPGRADETYPE_H_ + +#include "element_type.h" +#include "checksum.h" + +using Shared::Util::Checksum; + +namespace Glest{ namespace Game{ + +class TechTree; +class FactionType; +class UnitType; + +// =============================== +// class UpgradeTypeBase +// =============================== + +class UpgradeTypeBase{ +protected: + int maxHp; + int sight; + int maxEp; + int armor; + int attackStrength; + int attackRange; + int moveSpeed; + int prodSpeed; + +public: + int getMaxHp() const {return maxHp;} + int getSight() const {return sight;} + int getMaxEp() const {return maxEp;} + int getArmor() const {return armor;} + int getAttackStrength() const {return attackStrength;} + int getAttackRange() const {return attackRange;} + int getMoveSpeed() const {return moveSpeed;} + int getProdSpeed() const {return prodSpeed;} +}; + +// =============================== +// class UpgradeType +// =============================== + +class UpgradeType: public UpgradeTypeBase, public ProducibleType{ +private: + vector effects; + +public: + void preLoad(const string &dir); + void load(const string &dir, const TechTree *techTree, const FactionType *factionType, Checksum* checksum); + + //get all + int getEffectCount() const {return effects.size();} + const UnitType * getEffect(int i) const {return effects[i];} + bool isAffected(const UnitType *unitType) const; + + //other methods + virtual string getReqDesc() const; +}; + +// =============================== +// class TotalUpgrade +// =============================== + +class TotalUpgrade: public UpgradeTypeBase{ +public: + TotalUpgrade(); + + void reset(); + void sum(const UpgradeType *ut); + void incLevel(const UnitType *ut); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/world/map.cpp b/source/glest_game/world/map.cpp new file mode 100644 index 00000000..5580d6fe --- /dev/null +++ b/source/glest_game/world/map.cpp @@ -0,0 +1,690 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "map.h" + +#include + +#include "tileset.h" +#include "unit.h" +#include "resource.h" +#include "logger.h" +#include "tech_tree.h" +#include "config.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Cell +// ===================================================== + +Cell::Cell(){ + //game data + for(int i=0; iisPutrefacting(); +} + +// ===================================================== +// class SurfaceCell +// ===================================================== + +SurfaceCell::SurfaceCell(){ + object= NULL; + vertex= Vec3f(0.f); + normal= Vec3f(0.f, 1.f, 0.f); + surfaceType= -1; + surfaceTexture= NULL; +} + +SurfaceCell::~SurfaceCell(){ + delete object; +} + +bool SurfaceCell::isFree() const{ + return object==NULL || object->getWalkable(); +} + +void SurfaceCell::deleteResource(){ + delete object; + object= NULL; +} + +void SurfaceCell::setExplored(int teamIndex, bool explored){ + this->explored[teamIndex]= explored; +} + +void SurfaceCell::setVisible(int teamIndex, bool visible){ + this->visible[teamIndex]= visible; +} + +// ===================================================== +// class Map +// ===================================================== + +// ===================== PUBLIC ======================== + +const int Map::cellScale= 2; +const int Map::mapScale= 2; + +Map::Map(){ + cells= NULL; + surfaceCells= NULL; + startLocations= NULL; +} + +Map::~Map(){ + Logger::getInstance().add("Cells", true); + + delete [] cells; + delete [] surfaceCells; + delete [] startLocations; +} + +void Map::load(const string &path, TechTree *techTree, Tileset *tileset){ + + struct MapFileHeader{ + int32 version; + int32 maxPlayers; + int32 width; + int32 height; + int32 altFactor; + int32 waterLevel; + int8 title[128]; + int8 author[128]; + int8 description[256]; + }; + + try{ + FILE *f= fopen(path.c_str(), "rb"); + if(f!=NULL){ + + //read header + MapFileHeader header; + fread(&header, sizeof(MapFileHeader), 1, f); + + if(next2Power(header.width) != header.width){ + throw runtime_error("Map width is not a power of 2"); + } + + if(next2Power(header.height) != header.height){ + throw runtime_error("Map height is not a power of 2"); + } + + heightFactor= header.altFactor; + waterLevel= static_cast((header.waterLevel-0.01f)/heightFactor); + title= header.title; + maxPlayers= header.maxPlayers; + surfaceW= header.width; + surfaceH= header.height; + w= surfaceW*cellScale; + h= surfaceH*cellScale; + + + //start locations + startLocations= new Vec2i[maxPlayers]; + for(int i=0; isetVertex(Vec3f(i*mapScale, alt / heightFactor, j*mapScale)); + } + } + + //read surfaces + for(int j=0; jsetSurfaceType(surf-1); + } + } + + //read objects and resources + for(int j=0; jsetObject(NULL); + } + else if(objNumber <= Tileset::objCount){ + Object *o= new Object(tileset->getObjectType(objNumber-1), sc->getVertex()); + sc->setObject(o); + for(int k=0; kgetResourceTypeCount(); ++k){ + const ResourceType *rt= techTree->getResourceType(k); + if(rt->getClass()==rcTileset && rt->getTilesetObject()==objNumber){ + o->setResource(rt, Vec2i(i, j)); + } + } + } + else{ + const ResourceType *rt= techTree->getTechResourceType(objNumber - Tileset::objCount) ; + Object *o= new Object(NULL, sc->getVertex()); + o->setResource(rt, Vec2i(i, j)); + sc->setObject(o); + } + } + } + } + else{ + throw runtime_error("Can't open file"); + } + fclose(f); + } + catch(const exception &e){ + throw runtime_error("Error loading map: "+ path+ "\n"+ e.what()); + } +} + +void Map::init(){ + Logger::getInstance().add("Heightmap computations", true); + smoothSurface(); + computeNormals(); + computeInterpolatedHeights(); + computeNearSubmerged(); + computeCellColors(); +} + + +// ==================== is ==================== + +bool Map::isInside(int x, int y) const{ + return x>=0 && y>=0 && x=0 && sy>=0 && sxgetResource(); + if(r!=NULL){ + if(r->getType()==rt){ + resourcePos= pos + Vec2i(i,j); + return true; + } + } + } + } + } + return false; +} + + +// ==================== free cells ==================== + +bool Map::isFreeCell(const Vec2i &pos, Field field) const{ + return + isInside(pos) && + getCell(pos)->isFree(field) && + (field==fAir || getSurfaceCell(toSurfCoords(pos))->isFree()) && + (field!=fLand || !getDeepSubmerged(getCell(pos))); +} + +bool Map::isFreeCellOrHasUnit(const Vec2i &pos, Field field, const Unit *unit) const{ + if(isInside(pos)){ + Cell *c= getCell(pos); + if(c->getUnit(unit->getCurrField())==unit){ + return true; + } + else{ + return isFreeCell(pos, field); + } + } + return false; +} + +bool Map::isAproxFreeCell(const Vec2i &pos, Field field, int teamIndex) const{ + + if(isInside(pos)){ + const SurfaceCell *sc= getSurfaceCell(toSurfCoords(pos)); + + if(sc->isVisible(teamIndex)){ + return isFreeCell(pos, field); + } + else if(sc->isExplored(teamIndex)){ + return field==fLand? sc->isFree() && !getDeepSubmerged(getCell(pos)): true; + } + else{ + return true; + } + } + return false; +} + +bool Map::isFreeCells(const Vec2i & pos, int size, Field field) const{ + for(int i=pos.x; igetType()->getSize(); + + for(int i=pos2.x; igetUnit(unit->getCurrField())!=unit){ + if(!isFreeCell(Vec2i(i, j), unit->getCurrField())){ + return false; + } + } + } + else{ + return false; + } + } + } + return true; +} + +//checks if a unit can move from between 2 cells using only visible cells (for pathfinding) +bool Map::aproxCanMove(const Unit *unit, const Vec2i &pos1, const Vec2i &pos2) const{ + int size= unit->getType()->getSize(); + int teamIndex= unit->getTeam(); + Field field= unit->getCurrField(); + + //single cell units + if(size==1){ + if(!isAproxFreeCell(pos2, field, teamIndex)){ + return false; + } + if(pos1.x!=pos2.x && pos1.y!=pos2.y){ + if(!isAproxFreeCell(Vec2i(pos1.x, pos2.y), field, teamIndex)){ + return false; + } + if(!isAproxFreeCell(Vec2i(pos2.x, pos1.y), field, teamIndex)){ + return false; + } + } + return true; + } + + //multi cell units + else{ + for(int i=pos2.x; igetUnit(unit->getCurrField())!=unit){ + if(!isAproxFreeCell(Vec2i(i, j), field, teamIndex)){ + return false; + } + } + } + else{ + return false; + } + } + } + return true; + } +} + +//put a units into the cells +void Map::putUnitCells(Unit *unit, const Vec2i &pos){ + + assert(unit!=NULL); + const UnitType *ut= unit->getType(); + + for(int i=0; igetSize(); ++i){ + for(int j=0; jgetSize(); ++j){ + Vec2i currPos= pos + Vec2i(i, j); + assert(isInside(currPos)); + if(!ut->hasCellMap() || ut->getCellMapCell(i, j)){ + assert(getCell(currPos)->getUnit(unit->getCurrField())==NULL); + getCell(currPos)->setUnit(unit->getCurrField(), unit); + } + + } + } + unit->setPos(pos); +} + +//removes a unit from cells +void Map::clearUnitCells(Unit *unit, const Vec2i &pos){ + + assert(unit!=NULL); + const UnitType *ut= unit->getType(); + + for(int i=0; igetSize(); ++i){ + for(int j=0; jgetSize(); ++j){ + Vec2i currPos= pos + Vec2i(i, j); + assert(isInside(currPos)); + if(!ut->hasCellMap() || ut->getCellMapCell(i, j)){ + assert(getCell(currPos)->getUnit(unit->getCurrField())==unit); + getCell(currPos)->setUnit(unit->getCurrField(), NULL); + } + } + } +} + +// ==================== misc ==================== + +//returnis if unit is next to pos +bool Map::isNextTo(const Vec2i &pos, const Unit *unit) const{ + + for(int i=-1; i<=1; ++i){ + for(int j=-1; j<=1; ++j){ + if(isInside(pos.x+i, pos.y+j)) { + if(getCell(pos.x+i, pos.y+j)->getUnit(fLand)==unit){ + return true; + } + } + } + } + return false; +} + +void Map::clampPos(Vec2i &pos) const{ + if(pos.x<0){ + pos.x=0; + } + if(pos.y<0){ + pos.y=0; + } + if(pos.x>=w){ + pos.x=w-1; + } + if(pos.y>=h){ + pos.y=h-1; + } +} + +void Map::prepareTerrain(const Unit *unit){ + flatternTerrain(unit); + computeNormals(); + computeInterpolatedHeights(); +} + +// ==================== PRIVATE ==================== + +// ==================== compute ==================== + +void Map::flatternTerrain(const Unit *unit){ + float refHeight= getSurfaceCell(toSurfCoords(unit->getCenteredPos()))->getHeight(); + for(int i=-1; i<=unit->getType()->getSize(); ++i){ + for(int j=-1; j<=unit->getType()->getSize(); ++j){ + Vec2i pos= unit->getPos()+Vec2i(i, j); + Cell *c= getCell(pos); + SurfaceCell *sc= getSurfaceCell(toSurfCoords(pos)); + //we change height if pos is inside world, if its free or ocupied by the currenty building + if(isInside(pos) && sc->getObject()==NULL && (c->getUnit(fLand)==NULL || c->getUnit(fLand)==unit)){ + sc->setHeight(refHeight); + } + } + } +} + +//compute normals +void Map::computeNormals(){ + //compute center normals + for(int i=1; isetNormal( + getSurfaceCell(i, j)->getVertex().normal(getSurfaceCell(i, j-1)->getVertex(), + getSurfaceCell(i+1, j)->getVertex(), + getSurfaceCell(i, j+1)->getVertex(), + getSurfaceCell(i-1, j)->getVertex())); + } + } +} + +void Map::computeInterpolatedHeights(){ + + for(int i=0; isetHeight(getSurfaceCell(toSurfCoords(Vec2i(i, j)))->getHeight()); + } + } + + for(int i=1; isetHeight(getSurfaceCell(i, j)->getHeight()); + } + else if(k!=0 && l==0){ + getCell(i*cellScale+k, j*cellScale)->setHeight(( + getSurfaceCell(i, j)->getHeight()+ + getSurfaceCell(i+1, j)->getHeight())/2.f); + } + else if(l!=0 && k==0){ + getCell(i*cellScale, j*cellScale+l)->setHeight(( + getSurfaceCell(i, j)->getHeight()+ + getSurfaceCell(i, j+1)->getHeight())/2.f); + } + else{ + getCell(i*cellScale+k, j*cellScale+l)->setHeight(( + getSurfaceCell(i, j)->getHeight()+ + getSurfaceCell(i, j+1)->getHeight()+ + getSurfaceCell(i+1, j)->getHeight()+ + getSurfaceCell(i+1, j+1)->getHeight())/4.f); + } + } + } + } + } +} + + +void Map::smoothSurface(){ + + float *oldHeights= new float[surfaceW*surfaceH]; + + for(int i=0; isetHeight(height); + Object *object= getSurfaceCell(i, j)->getObject(); + if(object!=NULL){ + object->setHeight(height); + } + } + } + + delete [] oldHeights; +} + +void Map::computeNearSubmerged(){ + + for(int i=0; isetNearSubmerged(anySubmerged); + } + } +} + +void Map::computeCellColors(){ + for(int i=0; igetHeight()*1.5f, 1.f, 1.5f); + sc->setColor(Vec3f(1.0f, 1.0f, 1.0f)/factor); + } + else{ + sc->setColor(Vec3f(1.0f, 1.0f, 1.0f)); + } + } + } +} + +// ===================================================== +// class PosCircularIterator +// ===================================================== + +PosCircularIterator::PosCircularIterator(const Map *map, const Vec2i ¢er, int radius){ + this->map= map; + this->radius= radius; + this->center= center; + pos= center - Vec2i(radius, radius); + pos.x-= 1; +} + +bool PosCircularIterator::next(){ + + //iterate while dont find a cell that is inside the world + //and at less or equal distance that the radius + do{ + pos.x++; + if(pos.x > center.x+radius){ + pos.x= center.x-radius; + pos.y++; + } + if(pos.y>center.y+radius) + return false; + } + while(floor(pos.dist(center)) >= (radius+1) || !map->isInside(pos)); + //while(!(pos.dist(center) <= radius && map->isInside(pos))); + + return true; +} + +const Vec2i &PosCircularIterator::getPos(){ + return pos; +} + + +// ===================================================== +// class PosQuadIterator +// ===================================================== + +PosQuadIterator::PosQuadIterator(const Map *map, const Quad2i &quad, int step){ + this->map= map; + this->quad= quad; + this->boundingRect= quad.computeBoundingRect(); + this->step= step; + pos= boundingRect.p[0]; + --pos.x; + pos.x= (pos.x/step)*step; + pos.y= (pos.y/step)*step; +} + +bool PosQuadIterator::next(){ + + do{ + pos.x+= step; + if(pos.x > boundingRect.p[1].x){ + pos.x= (boundingRect.p[0].x/step)*step; + pos.y+= step; + } + if(pos.y>boundingRect.p[1].y) + return false; + } + while(!quad.isInside(pos)); + + return true; +} + +void PosQuadIterator::skipX(){ + pos.x+= step; +} + +const Vec2i &PosQuadIterator::getPos(){ + return pos; +} + +}}//end namespace diff --git a/source/glest_game/world/map.h b/source/glest_game/world/map.h new file mode 100644 index 00000000..010e8795 --- /dev/null +++ b/source/glest_game/world/map.h @@ -0,0 +1,273 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_MAP_H_ +#define _GLEST_GAME_MAP_H_ + +#include "vec.h" +#include "math_util.h" +#include "command_type.h" +#include "logger.h" +#include "object.h" +#include "game_constants.h" + +#include + +namespace Glest{ namespace Game{ + +using Shared::Graphics::Vec4f; +using Shared::Graphics::Quad2i; +using Shared::Graphics::Rect2i; +using Shared::Graphics::Vec4f; +using Shared::Graphics::Vec2f; +using Shared::Graphics::Vec2i; +using Shared::Graphics::Texture2D; + +class Tileset; +class Unit; +class Resource; +class TechTree; + + +// ===================================================== +// class Cell +// +/// A map cell that holds info about units present on it +// ===================================================== + +class Cell{ +private: + Unit *units[fieldCount]; //units on this cell + float height; + +private: + Cell(Cell&); + void operator=(Cell&); + +public: + Cell(); + + //get + Unit *getUnit(int field) const {return units[field];} + float getHeight() const {return height;} + + void setUnit(int field, Unit *unit) {units[field]= unit;} + void setHeight(float height) {this->height= height;} + + bool isFree(Field field) const; +}; + +// ===================================================== +// class SurfaceCell +// +// A heightmap cell, each surface cell is composed by more than one Cell +// ===================================================== + +class SurfaceCell{ +private: + //geometry + Vec3f vertex; + Vec3f normal; + Vec3f color; + + //tex coords + Vec2f fowTexCoord; //tex coords for TEXTURE1 when multitexturing and fogOfWar + Vec2f surfTexCoord; //tex coords for TEXTURE0 + + //surface + int surfaceType; + const Texture2D *surfaceTexture; + + //object & resource + Object *object; + + //visibility + bool visible[GameConstants::maxPlayers]; + bool explored[GameConstants::maxPlayers]; + + //cache + bool nearSubmerged; + +public: + SurfaceCell(); + ~SurfaceCell(); + + //get + const Vec3f &getVertex() const {return vertex;} + float getHeight() const {return vertex.y;} + const Vec3f &getColor() const {return color;} + const Vec3f &getNormal() const {return normal;} + int getSurfaceType() const {return surfaceType;} + const Texture2D *getSurfaceTexture() const {return surfaceTexture;} + Object *getObject() const {return object;} + Resource *getResource() const {return object==NULL? NULL: object->getResource();} + const Vec2f &getFowTexCoord() const {return fowTexCoord;} + const Vec2f &getSurfTexCoord() const {return surfTexCoord;} + bool getNearSubmerged() const {return nearSubmerged;} + + bool isVisible(int teamIndex) const {return visible[teamIndex];} + bool isExplored(int teamIndex) const {return explored[teamIndex];} + + //set + void setVertex(const Vec3f &vertex) {this->vertex= vertex;} + void setHeight(float height) {vertex.y= height;} + void setNormal(const Vec3f &normal) {this->normal= normal;} + void setColor(const Vec3f &color) {this->color= color;} + void setSurfaceType(int surfaceType) {this->surfaceType= surfaceType;} + void setSurfaceTexture(const Texture2D *st) {this->surfaceTexture= st;} + void setObject(Object *object) {this->object= object;} + void setFowTexCoord(const Vec2f &ftc) {this->fowTexCoord= ftc;} + void setSurfTexCoord(const Vec2f &stc) {this->surfTexCoord= stc;} + void setExplored(int teamIndex, bool explored); + void setVisible(int teamIndex, bool visible); + void setNearSubmerged(bool nearSubmerged) {this->nearSubmerged= nearSubmerged;} + + //misc + void deleteResource(); + bool isFree() const; +}; + + +// ===================================================== +// class Map +// +/// Represents the game map (and loads it from a gbm file) +// ===================================================== + +class Map{ +public: + static const int cellScale; //number of cells per surfaceCell + static const int mapScale; //horizontal scale of surface + +private: + string title; + float waterLevel; + float heightFactor; + int w; + int h; + int surfaceW; + int surfaceH; + int maxPlayers; + Cell *cells; + SurfaceCell *surfaceCells; + Vec2i *startLocations; + +private: + Map(Map&); + void operator=(Map&); + +public: + Map(); + ~Map(); + + void init(); + void load(const string &path, TechTree *techTree, Tileset *tileset); + + //get + Cell *getCell(int x, int y) const {return &cells[y*w+x];} + Cell *getCell(const Vec2i &pos) const {return getCell(pos.x, pos.y);} + SurfaceCell *getSurfaceCell(int sx, int sy) const {return &surfaceCells[sy*surfaceW+sx];} + SurfaceCell *getSurfaceCell(const Vec2i &sPos) const {return getSurfaceCell(sPos.x, sPos.y);} + int getW() const {return w;} + int getH() const {return h;} + int getSurfaceW() const {return surfaceW;} + int getSurfaceH() const {return surfaceH;} + int getMaxPlayers() const {return maxPlayers;} + float getHeightFactor() const {return heightFactor;} + float getWaterLevel() const {return waterLevel;} + Vec2i getStartLocation(int loactionIndex) const {return startLocations[loactionIndex];} + bool getSubmerged(const SurfaceCell *sc) const {return sc->getHeight()getHeight()getHeight()getHeight() + +#include "world.h" +#include "vec.h" +#include "renderer.h" +#include "config.h" +#include "object.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Minimap +// ===================================================== + +const float Minimap::exploredAlpha= 0.5f; + +Minimap::Minimap(){ + fowPixmap0= NULL; + fowPixmap1= NULL; + fogOfWar= Config::getInstance().getBool("FogOfWar"); +} + +void Minimap::init(int w, int h, const World *world){ + int scaledW= w/Map::cellScale; + int scaledH= h/Map::cellScale; + + Renderer &renderer= Renderer::getInstance(); + + //fow pixmaps + float f= 0.f; + fowPixmap0= new Pixmap2D(next2Power(scaledW), next2Power(scaledH), 1); + fowPixmap1= new Pixmap2D(next2Power(scaledW), next2Power(scaledH), 1); + fowPixmap0->setPixels(&f); + fowPixmap1->setPixels(&f); + + //fow tex + fowTex= renderer.newTexture2D(rsGame); + fowTex->setMipmap(false); + fowTex->setPixmapInit(false); + fowTex->setFormat(Texture::fAlpha); + fowTex->getPixmap()->init(next2Power(scaledW), next2Power(scaledH), 1); + fowTex->getPixmap()->setPixels(&f); + + //tex + tex= renderer.newTexture2D(rsGame); + tex->getPixmap()->init(scaledW, scaledH, 3); + tex->setMipmap(false); + + computeTexture(world); +} + +Minimap::~Minimap(){ + Logger::getInstance().add("Minimap", true); + delete fowPixmap0; + delete fowPixmap1; +} + +// ==================== set ==================== + +void Minimap::incFowTextureAlphaSurface(const Vec2i &sPos, float alpha){ + + assert(sPos.xgetW() && sPos.ygetH()); + + if(fowPixmap1->getPixelf(sPos.x, sPos.y)setPixel(sPos.x, sPos.y, alpha); + } +} + +void Minimap::resetFowTex(){ + Pixmap2D *tmpPixmap= fowPixmap0; + fowPixmap0= fowPixmap1; + fowPixmap1= tmpPixmap; + + for(int i=0; igetPixmap()->getW(); ++i){ + for(int j=0; jgetPixmap()->getH(); ++j){ + if(fogOfWar){ + float p0= fowPixmap0->getPixelf(i, j); + float p1= fowPixmap1->getPixelf(i, j); + + if(p1>exploredAlpha){ + fowPixmap1->setPixel(i, j, exploredAlpha); + } + if(p0>p1){ + fowPixmap1->setPixel(i, j, p0); + } + } + else{ + fowPixmap1->setPixel(i, j, 1.f); + } + } + } +} + +void Minimap::updateFowTex(float t){ + for(int i=0; igetW(); ++i){ + for(int j=0; jgetH(); ++j){ + float p1= fowPixmap1->getPixelf(i, j); + if(p1!=fowTex->getPixmap()->getPixelf(i, j)){ + float p0= fowPixmap0->getPixelf(i, j); + fowTex->getPixmap()->setPixel(i, j, p0+(t*(p1-p0))); + } + } + } +} + +// ==================== PRIVATE ==================== + +void Minimap::computeTexture(const World *world){ + + Vec3f color; + const Map *map= world->getMap(); + + tex->getPixmap()->setPixels(Vec4f(1.f, 1.f, 1.f, 0.1f).ptr()); + + for(int j=0; jgetPixmap()->getH(); ++j){ + for(int i=0; igetPixmap()->getW(); ++i){ + SurfaceCell *sc= map->getSurfaceCell(i, j); + + if(sc->getObject()==NULL || sc->getObject()->getType()==NULL){ + const Pixmap2D *p= world->getTileset()->getSurfPixmap(sc->getSurfaceType(), 0); + color= p->getPixel3f(p->getW()/2, p->getH()/2); + color= color * static_cast(sc->getVertex().y/6.f); + + if(sc->getVertex().y<= world->getMap()->getWaterLevel()){ + color+= Vec3f(0.5f, 0.5f, 1.0f); + } + + if(color.x>1.f) color.x=1.f; + if(color.y>1.f) color.y=1.f; + if(color.z>1.f) color.z=1.f; + } + else{ + color= sc->getObject()->getType()->getColor(); + } + tex->getPixmap()->setPixel(i, j, color); + } + } +} + +}}//end namespace diff --git a/source/glest_game/world/minimap.h b/source/glest_game/world/minimap.h new file mode 100644 index 00000000..6c91f7a1 --- /dev/null +++ b/source/glest_game/world/minimap.h @@ -0,0 +1,69 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_MINIMAP_H_ +#define _GLEST_GAME_MINIMAP_H_ + +#include "pixmap.h" +#include "texture.h" + +namespace Glest{ namespace Game{ + +using Shared::Graphics::Vec4f; +using Shared::Graphics::Vec3f; +using Shared::Graphics::Vec2i; +using Shared::Graphics::Pixmap2D; +using Shared::Graphics::Texture2D; + +class World; + +enum ExplorationState{ + esNotExplored, + esExplored, + esVisible +}; + +// ===================================================== +// class Minimap +// +/// State of the in-game minimap +// ===================================================== + +class Minimap{ +private: + Pixmap2D *fowPixmap0; + Pixmap2D *fowPixmap1; + Texture2D *tex; + Texture2D *fowTex; //Fog Of War Texture2D + bool fogOfWar; + +private: + static const float exploredAlpha; + +public: + void init(int x, int y, const World *world); + Minimap(); + ~Minimap(); + + const Texture2D *getFowTexture() const {return fowTex;} + const Texture2D *getTexture() const {return tex;} + + void incFowTextureAlphaSurface(const Vec2i &sPos, float alpha); + void resetFowTex(); + void updateFowTex(float t); + +private: + void computeTexture(const World *world); +}; + +}}//end namespace + +#endif diff --git a/source/glest_game/world/scenario.cpp b/source/glest_game/world/scenario.cpp new file mode 100644 index 00000000..ede4976a --- /dev/null +++ b/source/glest_game/world/scenario.cpp @@ -0,0 +1,72 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "scenario.h" + +#include + +#include "logger.h" +#include "xml_parser.h" +#include "util.h" +#include "game_util.h" +#include "leak_dumper.h" + +using namespace Shared::Xml; +using namespace Shared::Util; +using namespace std; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Scenario +// ===================================================== + +Scenario::~Scenario(){ + +} + +void Scenario::load(const string &path){ + try{ + string name= cutLastExt(lastDir(path)); + Logger::getInstance().add("Scenario: "+formatString(name), true); + + //parse xml + XmlTree xmlTree; + xmlTree.load(path); + const XmlNode *scenarioNode= xmlTree.getRootNode(); + const XmlNode *scriptsNode= scenarioNode->getChild("scripts"); + + for(int i= 0; igetChildCount(); ++i){ + const XmlNode *scriptNode = scriptsNode->getChild(i); + + scripts.push_back(Script(getFunctionName(scriptNode), scriptNode->getText())); + } + } + //Exception handling (conversions and so on); + catch(const exception &e){ + throw runtime_error("Error: " + path + "\n" + e.what()); + } +} + +string Scenario::getScenarioPath(const string &dir, const string &scenarioName){ + return dir + "/" + scenarioName + "/" + scenarioName + ".xml"; +} + +string Scenario::getFunctionName(const XmlNode *scriptNode){ + string name= scriptNode->getName(); + + for(int i= 0; igetAttributeCount(); ++i){ + name+= "_" + scriptNode->getAttribute(i)->getValue(); + } + return name; +} + +}}//end namespace diff --git a/source/glest_game/world/scenario.h b/source/glest_game/world/scenario.h new file mode 100644 index 00000000..bffe1b6c --- /dev/null +++ b/source/glest_game/world/scenario.h @@ -0,0 +1,70 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2005 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#ifndef _GLEST_GAME_SCENARIO_H_ +#define _GLEST_GAME_SCENARIO_H_ + +#include +#include + +#include "xml_parser.h" + +using std::string; +using std::vector; +using std::pair; + +using Shared::Xml::XmlNode; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Script +// ===================================================== + +class Script{ +private: + string name; + string code; + +public: + Script(const string &name, const string &code) {this->name= name; this->code= code;} + + const string &getName() const {return name;} + const string &getCode() const {return code;} +}; + +// ===================================================== +// class Scenario +// ===================================================== + +class Scenario{ +private: + typedef pair NameScriptPair; + typedef vector