/// Fills \a buffer with the ends points for the lines that connect /// technologies in \a techs void FillArcBuffer(GG::GL2DVertexBuffer& buffer, const std::set<std::string>& techs) { for (std::set<std::string>::const_iterator it = techs.begin(); it != techs.end(); ++it) { const std::vector<TechTreeLayout::Edge*> edges = m_layout.GetOutEdges(*it); //prerequisite edge for (std::vector<TechTreeLayout::Edge*>::const_iterator edge = edges.begin(); edge != edges.end(); edge++) { std::vector<std::pair<double, double> > points; const std::string& from = (*edge)->GetTechFrom(); const std::string& to = (*edge)->GetTechTo(); // Do not show lines leading to techs // we are not showing if (techs.find(to) == techs.end()) { continue; } // Remember what edges we are showing so // we can eventually highlight them m_edges_to_show[from].insert(to); if (!GetTech(from) || !GetTech(to)) { ErrorLogger() << "TechTreeArcs::FillArcBuffer missing arc endpoint tech " << from << "->" << to; continue; } (*edge)->ReadPoints(points); // To be able to draw all the lines in one call, // we will draw the with GL_LINES, which means all // vertices except the first and the last must occur twice for (unsigned i = 0; i < points.size() - 1; ++i){ buffer.store(points[i].first, points[i].second); buffer.store(points[i+1].first, points[i+1].second); } } } buffer.createServerBuffer(); }
void TechTreeLayout::AddNode(const std::string& tech, GG::X width, GG::Y height) { assert(width > 0 && height > 0 && GetTech(tech)); TechTreeLayout::Node* node = new TechTreeLayout::Node(tech, width, height); //DebugLogger() << "Adding Node: " << node << " for tech " << tech; m_nodes.push_back(node); m_node_map[tech] = node; }
int IssueDequeueTechOrder(const std::string& tech_name) { const Tech* tech = GetTech(tech_name); if (!tech) { Logger().errorStream() << "AIInterface::IssueDequeueTechOrder : passed tech_name that is not the name of a tech."; return 0; } int empire_id = AIClientApp::GetApp()->EmpireID(); AIClientApp::GetApp()->Orders().IssueOrder(OrderPtr(new ResearchQueueOrder(empire_id, tech_name))); return 1; }
int IssueEnqueueTechOrder(const std::string& tech_name, int position) { const Tech* tech = GetTech(tech_name); if (!tech) { ErrorLogger() << "IssueEnqueueTechOrder : passed tech_name that is not the name of a tech."; return 0; } int empire_id = AIClientApp::GetApp()->EmpireID(); AIClientApp::GetApp()->Orders().IssueOrder(OrderPtr( new ResearchQueueOrder(empire_id, tech_name, position))); return 1; }
static void LoadShader() { GLuint techID = MakeShader("content/shader.vsh", "content/shader.fsh"); Shader = malloc(sizeof(ColorShader)); GLuint error = glGetError(); if (error>0) { __android_log_print(ANDROID_LOG_INFO, "NATIVE", "MAKESHADER ERROR %i", error); assert(error==0); } GetTech(techID, Shader); error = glGetError(); if (error>0) { __android_log_print(ANDROID_LOG_INFO, "NATIVE", "GET TECH ERROR %i", error); assert(error==0); } }
/** * creates and initialises all nodes * @param column_width width of each column * @param row_height height of each row * @param x_margin horizontal part of arrow before changing direction to child node */ void TechTreeLayout::DoLayout(double column_width, double row_height, double x_margin) { assert(column_width > 0 && row_height > 0); double internal_height = row_height / NODE_CELL_HEIGHT; // node has NODE_CELL_HEIGHT rows internally //1. set all node depths from root parents for (std::vector<Node*>::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it) if ((*it)->IsStartNode()) (*it)->SetDepthRecursive(0); // also sets all children's depths // find max node depth int max_node_depth = 0; for (std::vector<Node*>::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it) max_node_depth = std::max(max_node_depth, (*it)->GetDepth()); //2. create placeholder nodes DebugLogger() << "TechTreeLayout::DoLayout creaing place holder nodes..."; std::vector<Node*> raw_nodes = m_nodes; // just iterator over initial nodes, not also over the placeholders for (std::vector<Node*>::iterator it = raw_nodes.begin(); it != raw_nodes.end(); ++it) (*it)->CreatePlaceHolder(m_nodes); //3. put nodes into containers for each depth column std::vector<std::vector<Node*> > nodes_at_each_depth(max_node_depth + 1); for (std::vector<Node*>::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it) { Node* node = *it; assert(node->GetDepth() >= 0 && node->GetDepth() < nodes_at_each_depth.size()); nodes_at_each_depth[node->GetDepth()].push_back(node); } // sort within each depth column for (std::vector<std::vector<Node*> >::iterator it = nodes_at_each_depth.begin(); it != nodes_at_each_depth.end(); ++it) { std::sort(it->begin(), it->end(), NodePointerCmp()); } //4. do layout std::vector<Column> row_index = std::vector<Column>(nodes_at_each_depth.size()); // in what order do columns receive nodes? std::vector<int> column_order; column_order.reserve(nodes_at_each_depth.size()); // start with column with most nodes, progess outwards from it int first_column = 0; unsigned int max_column_nodes = 0; for (unsigned int i = 0; i < nodes_at_each_depth.size(); ++i) { if (nodes_at_each_depth[i].size() > max_column_nodes) { max_column_nodes = nodes_at_each_depth[i].size(); first_column = i; } } // progress outwards from initial column column_order.push_back(first_column); int next_column = column_order[0] + 1; int prev_column = column_order[0] - 1; while (column_order.size() < nodes_at_each_depth.size()) { if (prev_column >= 0) { column_order.push_back(prev_column); prev_column--; } if (next_column < static_cast<int>(nodes_at_each_depth.size())) { column_order.push_back(next_column); next_column++; } } // distribute tech nodes over the table, one column at a time for (std::vector<int>::iterator it = column_order.begin(); it != column_order.end(); ++it) { int column = *it; std::vector<Node*>& column_nodes = nodes_at_each_depth[column]; std::string current_category; for (std::vector<Node*>::iterator it = column_nodes.begin(); it != column_nodes.end(); ++it) { Node* node = *it; const Tech* node_tech = GetTech(node->GetTech()); const std::string& node_category = node_tech ? node_tech->Category() : ""; bool new_category = node_category != current_category; node->DoLayout(row_index, new_category); current_category = node_category; } } // optimize layout, every node gets a rating if moving would shorten the distance to it's family // if the movement is possible either if the place if free or the neighbour has the opposite wish bool movement = true; while (movement) { movement = false; for (unsigned int i = m_nodes.size(); i --> 0;) { if (m_nodes[i]->Wobble(row_index[m_nodes[i]->m_depth])) { movement = true; break; } } } //4.d. count used rows and columns unsigned int column_count = row_index.size(); unsigned int row_count = 0; for (int i = row_index.size(); i-->0;) row_count = std::max(row_count, row_index[i].Size()); //4.e. set size for (int i = m_nodes.size(); i --> 0 ; ) m_nodes[i]->CalculateCoordinate(column_width, internal_height); m_width = column_count * column_width; m_height = row_count * internal_height; //5. create edges for (int i = m_nodes.size(); i --> 0 ; ) m_nodes[i]->CreateEdges(x_margin, column_width, internal_height); }
//////////////// // class Edge // //////////////// TechTreeLayout::Edge::Edge(const std::string& from, const std::string& to) : m_points(std::vector<std::pair<double,double> >()), m_from(from), m_to(to) { assert(GetTech(from) && GetTech(to)); }
void ResearchQueue::Update(float RPs, const std::map<std::string, float>& research_progress) { // status of all techs for this empire const Empire* empire = GetEmpire(m_empire_id); if (!empire) return; std::map<std::string, TechStatus> sim_tech_status_map; for (const auto& tech : GetTechManager()) { const std::string& tech_name = tech->Name(); sim_tech_status_map[tech_name] = empire->GetTechStatus(tech_name); } SetTechQueueElementSpending(RPs, research_progress, sim_tech_status_map, m_queue, m_total_RPs_spent, m_projects_in_progress, m_empire_id); if (m_queue.empty()) { ResearchQueueChangedSignal(); return; // nothing more to do... } const int TOO_MANY_TURNS = 500; // stop counting turns to completion after this long, to prevent seemingly endless loops // initialize status of everything to never getting done for (Element& element : m_queue) element.turns_left = -1; if (RPs <= EPSILON) { ResearchQueueChangedSignal(); return; // nothing more to do if not enough RP... } boost::posix_time::ptime dp_time_start; boost::posix_time::ptime dp_time_end; // "Dynamic Programming" version of research queue simulator -- copy the queue simulator containers // perform dynamic programming calculation of completion times, then after regular simulation is done compare results (if both enabled) //record original order & progress // will take advantage of fact that sets (& map keys) are by default kept in sorted order lowest to highest std::map<std::string, float> dp_prog = research_progress; std::map< std::string, int > orig_queue_order; std::map<int, float> dpsim_research_progress; for (unsigned int i = 0; i < m_queue.size(); ++i) { std::string tname = m_queue[i].name; orig_queue_order[tname] = i; dpsim_research_progress[i] = dp_prog[tname]; } std::map<std::string, TechStatus> dpsim_tech_status_map = std::move(sim_tech_status_map); // initialize simulation_results with -1 for all techs, so that any techs that aren't // finished in simulation by turn TOO_MANY_TURNS will be left marked as never to be finished std::vector<int> dpsimulation_results(m_queue.size(), -1); const int DP_TURNS = TOO_MANY_TURNS; // track up to this many turns std::map<std::string, std::set<std::string>> waiting_for_prereqs; std::set<int> dp_researchable_techs; for (unsigned int i = 0; i < m_queue.size(); ++i) { std::string techname = m_queue[i].name; if (m_queue[i].paused) continue; const Tech* tech = GetTech(techname); if (!tech) continue; if (dpsim_tech_status_map[techname] == TS_RESEARCHABLE) { dp_researchable_techs.insert(i); } else if (dpsim_tech_status_map[techname] == TS_UNRESEARCHABLE || dpsim_tech_status_map[techname] == TS_HAS_RESEARCHED_PREREQ) { std::set<std::string> these_prereqs = tech->Prerequisites(); for (auto ptech_it = these_prereqs.begin(); ptech_it != these_prereqs.end();) { if (dpsim_tech_status_map[*ptech_it] != TS_COMPLETE) { ++ptech_it; } else { auto erase_it = ptech_it; ++ptech_it; these_prereqs.erase(erase_it); } } waiting_for_prereqs[techname] = these_prereqs; } } int dp_turns = 0; //pp_still_available[turn-1] gives the RP still available in this resource pool at turn "turn" std::vector<float> rp_still_available(DP_TURNS, RPs); // initialize to the full RP allocation for every turn while ((dp_turns < DP_TURNS) && !(dp_researchable_techs.empty())) {// if we haven't used up our turns and still have techs to process ++dp_turns; std::map<int, bool> already_processed; for (int tech_id : dp_researchable_techs) { already_processed[tech_id] = false; } auto cur_tech_it = dp_researchable_techs.begin(); while ((rp_still_available[dp_turns-1] > EPSILON)) { // try to use up this turns RPs if (cur_tech_it == dp_researchable_techs.end()) { break; //will be wasting some RP this turn } int cur_tech = *cur_tech_it; if (already_processed[cur_tech]) { ++cur_tech_it; continue; } already_processed[cur_tech] = true; const std::string& tech_name = m_queue[cur_tech].name; const Tech* tech = GetTech(tech_name); float progress = dpsim_research_progress[cur_tech]; float tech_cost = tech ? tech->ResearchCost(m_empire_id) : 0.0f; float RPs_needed = tech ? tech_cost * (1.0f - std::min(progress, 1.0f)) : 0.0f; float RPs_per_turn_limit = tech ? tech->PerTurnCost(m_empire_id) : 1.0f; float RPs_to_spend = std::min(std::min(RPs_needed, RPs_per_turn_limit), rp_still_available[dp_turns-1]); progress += RPs_to_spend / std::max(EPSILON, tech_cost); dpsim_research_progress[cur_tech] = progress; rp_still_available[dp_turns-1] -= RPs_to_spend; auto next_res_tech_it = cur_tech_it; int next_res_tech_idx; if (++next_res_tech_it == dp_researchable_techs.end()) { next_res_tech_idx = m_queue.size()+1; } else { next_res_tech_idx = *(next_res_tech_it); } if (tech_cost - EPSILON <= progress * tech_cost) { dpsim_tech_status_map[tech_name] = TS_COMPLETE; dpsimulation_results[cur_tech] = dp_turns; #ifndef ORIG_RES_SIMULATOR m_queue[cur_tech].turns_left = dp_turns; #endif dp_researchable_techs.erase(cur_tech_it); std::set<std::string> unlocked_techs; if (tech) unlocked_techs = tech->UnlockedTechs(); for (std::string u_tech_name : unlocked_techs) { auto prereq_tech_it = waiting_for_prereqs.find(u_tech_name); if (prereq_tech_it != waiting_for_prereqs.end() ){ std::set<std::string>& these_prereqs = prereq_tech_it->second; auto just_finished_it = these_prereqs.find(tech_name); if (just_finished_it != these_prereqs.end() ) { //should always find it these_prereqs.erase(just_finished_it); if (these_prereqs.empty()) { // tech now fully unlocked int this_tech_idx = orig_queue_order[u_tech_name]; dp_researchable_techs.insert(this_tech_idx); waiting_for_prereqs.erase(prereq_tech_it); already_processed[this_tech_idx] = true; //doesn't get any allocation on current turn if (this_tech_idx < next_res_tech_idx ) { next_res_tech_idx = this_tech_idx; } } } else { //couldnt find tech_name in prereqs list DebugLogger() << "ResearchQueue::Update tech unlocking problem:"<< tech_name << "thought it was a prereq for " << u_tech_name << "but the latter disagreed"; } } //else { //tech_name thinks itself a prereq for ytechName, but u_tech_name not in prereqs -- not a problem so long as u_tech_name not in our queue at all // DebugLogger() << "ResearchQueue::Update tech unlocking problem:"<< tech_name << "thought it was a prereq for " << u_tech_name << "but the latter disagreed"; //} } }// if (tech->ResearchCost() - EPSILON <= progress * tech_cost) cur_tech_it = dp_researchable_techs.find(next_res_tech_idx); }//while ((rp_still_available[dp_turns-1]> EPSILON)) //dp_time = dpsim_queue_timer.elapsed() * 1000; // DebugLogger() << "ProductionQueue::Update queue dynamic programming sim time: " << dpsim_queue_timer.elapsed() * 1000.0; } // while ((dp_turns < DP_TURNS ) && !(dp_researchable_techs.empty() ) ) ResearchQueueChangedSignal(); }