void CStarmap::AddBase(const Sector §or, BYTE propTech) { AssertBotE(sector.on_map()); // merken, dass Sektor einen Außenposten besitzt; falls der Außenposten schon vorhanden ist, die folgende // Berechnung trotzdem durchführen, da eine andere <code>rangeMap</code> vorgegeben sein könnte. if (!IsBase(sector)) m_lBases.push_back(sector); // --- Map mit Entfernungen aktualisieren --- this->CalcRangeMap(propTech); // lokale Rangemap durchlaufen for (char x = -m_RangeMap.x0; x < m_RangeMap.w - m_RangeMap.x0; x++) for (char y = -m_RangeMap.y0; y < m_RangeMap.h - m_RangeMap.y0; y++) { Sector pt(sector.x + x, sector.y + y); if (pt.is_in_rect(0, 0, STARMAP_SECTORS_HCOUNT, STARMAP_SECTORS_VCOUNT)) { // Wert überschreiben, wenn der neue Einfluss größer ist m_Range.at(CoordsToIndex(pt.x, pt.y)) = max(m_Range.at(CoordsToIndex(pt.x, pt.y)), GetRangeMapValue(x, y)); } } // pathStart zurücksetzen, damit sämtliche Wege beim nächsten Aufruf von // CalcPath() neu berechnet werden pathStart = Sector(); #ifdef DEBUG_AI_BASE_DEMO // ACHTUNG: verwenden i. A. falsche RangeMap! RecalcRangePoints(); RecalcConnectionPoints(); RecalcTargetPoints(); #endif }
void CStarmap::Select(const Sector §or) { if (sector.x > -1 && sector.y > -1) { AssertBotE(sector.on_map()); m_Selection = sector; } }
Sector CStarmap::GetClickedSector(const CPoint &pt) { Sector result; result.x = result.y = -1; // wenn innerhalb der Karte geklickt, dann Koordinaten umrechnen if (PT_IN_RECT(pt, 0, 0, STARMAP_TOTALWIDTH, STARMAP_TOTALHEIGHT)) { result.x = pt.x / STARMAP_SECTOR_WIDTH; result.y = pt.y / STARMAP_SECTOR_HEIGHT; AssertBotE(result.on_map()); } return result; }
void CStarmap::AddKnownSystem(const Sector §or) { AssertBotE(sector.on_map()); AssertBotE(m_bAICalculation); if (!m_bAICalculation || !sector.is_in_rect(0, 0, STARMAP_SECTORS_HCOUNT, STARMAP_SECTORS_VCOUNT)) return; // prüfen, ob Ziel bereits in Liste vorhanden ist for (std::list<Sector>::const_iterator it = m_lAIKnownSystems.begin(); it != m_lAIKnownSystems.end(); ++it) if (*it == sector) return; // sonst hinzufügen m_lAIKnownSystems.push_back(sector); #ifdef DEBUG_AI_BASE_DEMO // Bewertung für Ausbreitungsrichtungen aktualisieren // ACHTUNG: Berechnung hier nicht notwendig RecalcTargetPoints(); #endif }
Sector CStarmap::CalcPath(const Sector &pos, const Sector &target, unsigned char range, unsigned char speed, CArray<Sector> &path) { AssertBotE(pos.on_map()); AssertBotE(target.on_map()); // bisherige Einträge von path löschen path.RemoveAll(); // gegebene Parameter prüfen if (pos == target // Start == Ziel || range < 1 || range > 3 || m_Range.at(CoordsToIndex(pos.x, pos.y)) < range // Start außerhalb des Gebiets der Reichweite || m_Range.at(CoordsToIndex(target.x, target.y)) < range // Ziel außerhalb der Reichweite || speed < 1) { return Sector(); } // Array zur Berechnung der Koordinaten sämtlicher Nachbarn eines Sektors (schräg/gerade abwechselnd, // mit schräg beginnend) Sector neighbours[8] = {Sector(-1, -1), Sector(0, -1), Sector(1, -1), Sector(1, 0), Sector(1, 1), Sector(0, 1), Sector(-1, 1), Sector(-1, 0)}; // Berechnung neu beginnen? if (pos != pathStart || range != pathRange) { // pathMap zurücksetzen for (int j = 0; j < STARMAP_SECTORS_VCOUNT; j++) for (int i = 0; i < STARMAP_SECTORS_HCOUNT; i++) { /*PathSector *tmp = &(pathMap.at(CoordsToIndex(i, j))); tmp->used = false; tmp->distance = 0.; tmp->hops = 0; tmp->parent.x = tmp->parent.y = -1; tmp->position.x = i; // für Zugriff aus leafList heraus merken tmp->position.y = j;*/ const int index = CoordsToIndex(i, j); pathMap.at(index).used=false; pathMap.at(index).distance=0; pathMap.at(index).hops=0; pathMap.at(index).parent.x=-1; pathMap.at(index).parent.y=-1; pathMap.at(index).position.x=i; pathMap.at(index).position.y=j; } // leaves zurücksetzen leaves.Clear(); // Startknoten zur Liste der auszuwählenden Blätter hinzufügen leaves.Add(&(pathMap.at(CoordsToIndex(pos.x, pos.y)))); // Parameter merken pathStart = pos; pathRange = range; } // ist der Weg zum angegebenen Ziel bereits bekannt? if (pathMap.at(CoordsToIndex(target.x, target.y)).parent.x == -1 || pathMap.at(CoordsToIndex(target.x, target.y)).parent.y == -1) { // kürzeste Wege zu allen anderen Knoten bestimmen, bis uns der Zielknoten über den Weg läuft bool found = false; while (!found) { // Zeiger auf ein neues Blatt mit einer kürzesten Gesamtentfernung zum // Start-Sektor ermitteln PathSector *next = leaves.PopFirst(); if (!next) return Sector(); // keine Knoten mehr, Zielknoten ist nicht erreichbar if (next->used) continue; // Knoten wurde schonmal gewählt // Knoten als ausgewählt markieren next->used = true; // bisher noch nicht ausgewählte Nachbarn innerhalb der Reichweite in leaves // eintragen; // die Nachbarn müssen auch eingetragen werden, wenn next bereits der Zielknoten ist, // da der nächste Aufruf von CalcPath() die Zwischenergebnisse wiederverwendet for (int i = 0; i < 8; i++) { // Koordinaten des Nachbarn ermitteln (Sektoren nur betrachten, wenn sie // noch auf der Starmap liegen!) Sector npos = next->position + neighbours[i]; if (!npos.is_in_rect(0, 0, STARMAP_SECTORS_HCOUNT, STARMAP_SECTORS_VCOUNT)) continue; // nur Nachbarn betrachten, die noch nicht ausgewählt wurden und innerhalb der // Reichweite liegen PathSector *neighb = &(pathMap.at(CoordsToIndex(npos.x, npos.y))); if (neighb->used || m_Range.at(CoordsToIndex(npos.x, npos.y)) < range) continue; // kann der Nachbar über next auf einem kürzeren Weg als bisher erreicht werden, // dann die bisherige Info überschreiben double distance = next->distance + ((i % 2) ? WEIGHT_DIR : WEIGHT_DIAG); // Anomalien beachten distance += m_BadMapModifiers.at(next->position.x + next->position.y * STARMAP_SECTORS_HCOUNT); if (neighb->distance == 0. || distance < neighb->distance) { // (distance ist für alle anderen Sektoren außer dem Start-Sektor > 0., // der Wert 0. weist darauf hin, dass distance noch nicht gesetzt wurde) neighb->distance = distance; neighb->hops = next->hops + 1; neighb->parent = next->position; // den Knoten in leaves neu einsortieren (derselbe Knoten ist evtl. unter // einer anderen Entfernung früher bereits einsortiert worden; da nun die // Entfernung aber kürzer ist, wird das neu einsortierte Element zuerst // gewählt; liefert die Liste eines der vorher eingeordneten Elemente, ist // dessen used-Feld bereits true und es wird sofort mit dem nächsten Eintrag // fortgesetzt); // @TODO besser wäre es, den früher einsortierten Knoten zu entfernen leaves.Add(neighb); } } if (next->position == target) found = true; // Zielknoten gefunden } } // Ziel gefunden; Weg vom Ziel bis zum Startknoten zurück verfolgen, // dabei von hinten beginnend in Array eintragen Sector next = target; int idx = pathMap.at(CoordsToIndex(target.x, target.y)).hops; AssertBotE(idx >= 1); path.SetSize(idx); // Größe des Arrays setzen (= Länge des Weges) while (next.x > -1 && next.y > -1 && --idx >= 0) // Start-Sektor nicht mit eintragen { AssertBotE(next.on_map()); path[idx] = next; next = pathMap.at(CoordsToIndex(next.x, next.y)).parent; } AssertBotE(idx == -1); // entsprechend speed den nächsten Knoten des Weges zurückgeben; bzw. den Zielknoten, // wenn der Weg kürzer ist return path[min(speed - 1, path.GetUpperBound())]; }
CPoint CStarmap::GetSectorCoords(const Sector& sector) { AssertBotE(sector.on_map()); return CPoint(sector.x * STARMAP_SECTOR_WIDTH, sector.y * STARMAP_SECTOR_HEIGHT); }