static void manageConstructionEvent(color_ostream& out) { if ( handlers[EventType::CONSTRUCTION].empty() ) return; unordered_set<df::construction*> constructionsNow(df::global::world->constructions.begin(), df::global::world->constructions.end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::CONSTRUCTION].begin(), handlers[EventType::CONSTRUCTION].end()); for ( auto a = constructions.begin(); a != constructions.end(); a++ ) { df::construction* construction = *a; if ( constructionsNow.find(construction) != constructionsNow.end() ) continue; for ( auto b = copy.begin(); b != copy.end(); b++ ) { EventHandler handle = (*b).second; handle.eventHandler(out, (void*)construction); } } for ( auto a = constructionsNow.begin(); a != constructionsNow.end(); a++ ) { df::construction* construction = *a; if ( constructions.find(construction) != constructions.end() ) continue; for ( auto b = copy.begin(); b != copy.end(); b++ ) { EventHandler handle = (*b).second; handle.eventHandler(out, (void*)construction); } } constructions.clear(); constructions.insert(constructionsNow.begin(), constructionsNow.end()); }
static void manageSyndromeEvent(color_ostream& out) { if (!df::global::world) return; multimap<Plugin*,EventHandler> copy(handlers[EventType::SYNDROME].begin(), handlers[EventType::SYNDROME].end()); int32_t highestTime = -1; for ( auto a = df::global::world->units.all.begin(); a != df::global::world->units.all.end(); a++ ) { df::unit* unit = *a; /* if ( unit->flags1.bits.dead ) continue; */ for ( size_t b = 0; b < unit->syndromes.active.size(); b++ ) { df::unit_syndrome* syndrome = unit->syndromes.active[b]; int32_t startTime = syndrome->year*ticksPerYear + syndrome->year_time; if ( startTime > highestTime ) highestTime = startTime; if ( startTime <= lastSyndromeTime ) continue; SyndromeData data(unit->id, b); for ( auto c = copy.begin(); c != copy.end(); c++ ) { EventHandler handle = (*c).second; handle.eventHandler(out, (void*)&data); } } } lastSyndromeTime = highestTime; }
static void manageTickEvent(color_ostream& out) { if (!df::global::world) return; unordered_set<EventHandler> toRemove; int32_t tick = df::global::world->frame_counter; while ( !tickQueue.empty() ) { if ( tick < (*tickQueue.begin()).first ) break; EventHandler handle = (*tickQueue.begin()).second; tickQueue.erase(tickQueue.begin()); handle.eventHandler(out, (void*)tick); toRemove.insert(handle); } if ( toRemove.empty() ) return; for ( auto a = handlers[EventType::TICK].begin(); a != handlers[EventType::TICK].end(); ) { EventHandler handle = (*a).second; if ( toRemove.find(handle) == toRemove.end() ) { a++; continue; } a = handlers[EventType::TICK].erase(a); toRemove.erase(handle); if ( toRemove.empty() ) break; } }
static void manageBuildingEvent(color_ostream& out) { /* * TODO: could be faster * consider looking at jobs: building creation / destruction **/ if ( handlers[EventType::BUILDING].empty() ) return; multimap<Plugin*,EventHandler> copy(handlers[EventType::BUILDING].begin(), handlers[EventType::BUILDING].end()); //first alert people about new buildings for ( int32_t a = nextBuilding; a < *df::global::building_next_id; a++ ) { int32_t index = df::building::binsearch_index(df::global::world->buildings.all, a); if ( index == -1 ) { //out.print("%s, line %d: Couldn't find new building with id %d.\n", __FILE__, __LINE__, a); //the tricky thing is that when the game first starts, it's ok to skip buildings, but otherwise, if you skip buildings, something is probably wrong. TODO: make this smarter continue; } buildings.insert(a); for ( auto b = copy.begin(); b != copy.end(); b++ ) { EventHandler bob = (*b).second; bob.eventHandler(out, (void*)a); } } nextBuilding = *df::global::building_next_id; //now alert people about destroyed buildings unordered_set<int32_t> toDelete; for ( auto a = buildings.begin(); a != buildings.end(); a++ ) { int32_t id = *a; int32_t index = df::building::binsearch_index(df::global::world->buildings.all,id); if ( index != -1 ) continue; toDelete.insert(id); for ( auto b = copy.begin(); b != copy.end(); b++ ) { EventHandler bob = (*b).second; bob.eventHandler(out, (void*)id); } } for ( auto a = toDelete.begin(); a != toDelete.end(); a++ ) { int32_t id = *a; buildings.erase(id); } //out.print("Sent building event.\n %d", __LINE__); }
static void manageTickEvent(color_ostream& out) { uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear + DFHack::World::ReadCurrentTick(); while ( !tickQueue.empty() ) { if ( tick < (*tickQueue.begin()).first ) break; EventHandler handle = (*tickQueue.begin()).second; tickQueue.erase(tickQueue.begin()); handle.eventHandler(out, (void*)tick); } }
static void manageInvasionEvent(color_ostream& out) { if (!df::global::ui) return; multimap<Plugin*,EventHandler> copy(handlers[EventType::INVASION].begin(), handlers[EventType::INVASION].end()); if ( df::global::ui->invasions.next_id <= nextInvasion ) return; nextInvasion = df::global::ui->invasions.next_id; for ( auto a = copy.begin(); a != copy.end(); a++ ) { EventHandler handle = (*a).second; handle.eventHandler(out, (void*)(nextInvasion-1)); } }
static void manageConstructionEvent(color_ostream& out) { if (!df::global::world) return; //unordered_set<df::construction*> constructionsNow(df::global::world->constructions.begin(), df::global::world->constructions.end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::CONSTRUCTION].begin(), handlers[EventType::CONSTRUCTION].end()); for ( auto a = constructions.begin(); a != constructions.end(); ) { df::construction& construction = (*a).second; if ( df::construction::find(construction.pos) != NULL ) { a++; continue; } //construction removed //out.print("Removed construction (%d,%d,%d)\n", construction.pos.x,construction.pos.y,construction.pos.z); for ( auto b = copy.begin(); b != copy.end(); b++ ) { EventHandler handle = (*b).second; handle.eventHandler(out, (void*)&construction); } a = constructions.erase(a); } //for ( auto a = constructionsNow.begin(); a != constructionsNow.end(); a++ ) { for ( auto a = df::global::world->constructions.begin(); a != df::global::world->constructions.end(); a++ ) { df::construction* construction = *a; bool b = constructions.find(construction->pos) != constructions.end(); constructions[construction->pos] = *construction; if ( b ) continue; //construction created //out.print("Created construction (%d,%d,%d)\n", construction->pos.x,construction->pos.y,construction->pos.z); for ( auto b = copy.begin(); b != copy.end(); b++ ) { EventHandler handle = (*b).second; handle.eventHandler(out, (void*)construction); } } }
static void manageReportEvent(color_ostream& out) { if (!df::global::world) return; multimap<Plugin*,EventHandler> copy(handlers[EventType::REPORT].begin(), handlers[EventType::REPORT].end()); std::vector<df::report*>& reports = df::global::world->status.reports; size_t a = df::report::binsearch_index(reports, lastReport, false); //this may or may not be needed: I don't know if binsearch_index goes earlier or later if it can't hit the target exactly while (a < reports.size() && reports[a]->id <= lastReport) { a++; } for ( ; a < reports.size(); a++ ) { df::report* report = reports[a]; for ( auto b = copy.begin(); b != copy.end(); b++ ) { EventHandler handle = (*b).second; handle.eventHandler(out, (void*)report->id); } lastReport = report->id; } }
static void manageUnitAttackEvent(color_ostream& out) { if (!df::global::world) return; multimap<Plugin*,EventHandler> copy(handlers[EventType::UNIT_ATTACK].begin(), handlers[EventType::UNIT_ATTACK].end()); std::vector<df::report*>& reports = df::global::world->status.reports; size_t a = df::report::binsearch_index(reports, lastReportUnitAttack, false); //this may or may not be needed: I don't know if binsearch_index goes earlier or later if it can't hit the target exactly while (a < reports.size() && reports[a]->id <= lastReportUnitAttack) { a++; } std::set<int32_t> strikeReports; for ( ; a < reports.size(); a++ ) { df::report* report = reports[a]; lastReportUnitAttack = report->id; if ( report->flags.bits.continuation ) continue; df::announcement_type type = report->type; if ( type == df::announcement_type::COMBAT_STRIKE_DETAILS ) { strikeReports.insert(report->id); } } if ( strikeReports.empty() ) return; updateReportToRelevantUnits(); map<int32_t, map<int32_t, int32_t> > alreadyDone; for ( auto a = strikeReports.begin(); a != strikeReports.end(); a++ ) { int32_t reportId = *a; df::report* report = df::report::find(reportId); if ( !report ) continue; //TODO: error std::string reportStr = report->text; for ( int32_t b = reportId+1; ; b++ ) { df::report* report2 = df::report::find(b); if ( !report2 ) break; if ( report2->type != df::announcement_type::COMBAT_STRIKE_DETAILS ) break; if ( !report2->flags.bits.continuation ) break; reportStr = reportStr + report2->text; } std::vector<int32_t>& relevantUnits = reportToRelevantUnits[report->id]; if ( relevantUnits.size() != 2 ) { continue; } df::unit* unit1 = df::unit::find(relevantUnits[0]); df::unit* unit2 = df::unit::find(relevantUnits[1]); df::unit_wound* wound1 = getWound(unit1,unit2); df::unit_wound* wound2 = getWound(unit2,unit1); if ( wound1 && !alreadyDone[unit1->id][unit2->id] ) { UnitAttackData data; data.attacker = unit1->id; data.defender = unit2->id; data.wound = wound1->id; alreadyDone[data.attacker][data.defender] = 1; for ( auto b = copy.begin(); b != copy.end(); b++ ) { EventHandler handle = (*b).second; handle.eventHandler(out, (void*)&data); } } if ( wound2 && !alreadyDone[unit1->id][unit2->id] ) { UnitAttackData data; data.attacker = unit2->id; data.defender = unit1->id; data.wound = wound2->id; alreadyDone[data.attacker][data.defender] = 1; for ( auto b = copy.begin(); b != copy.end(); b++ ) { EventHandler handle = (*b).second; handle.eventHandler(out, (void*)&data); } } if ( unit1->flags1.bits.dead ) { UnitAttackData data; data.attacker = unit2->id; data.defender = unit1->id; data.wound = -1; alreadyDone[data.attacker][data.defender] = 1; for ( auto b = copy.begin(); b != copy.end(); b++ ) { EventHandler handle = (*b).second; handle.eventHandler(out, (void*)&data); } } if ( unit2->flags1.bits.dead ) { UnitAttackData data; data.attacker = unit1->id; data.defender = unit2->id; data.wound = -1; alreadyDone[data.attacker][data.defender] = 1; for ( auto b = copy.begin(); b != copy.end(); b++ ) { EventHandler handle = (*b).second; handle.eventHandler(out, (void*)&data); } } if ( !wound1 && !wound2 ) { //if ( unit1->flags1.bits.dead || unit2->flags1.bits.dead ) // continue; if ( reportStr.find("severed part") ) continue; if ( Once::doOnce("EventManager neither wound") ) { out.print("%s, %d: neither wound: %s\n", __FILE__, __LINE__, reportStr.c_str()); } } } }
static void manageEquipmentEvent(color_ostream& out) { if (!df::global::world) return; multimap<Plugin*,EventHandler> copy(handlers[EventType::INVENTORY_CHANGE].begin(), handlers[EventType::INVENTORY_CHANGE].end()); unordered_map<int32_t, InventoryItem> itemIdToInventoryItem; unordered_set<int32_t> currentlyEquipped; for ( auto a = df::global::world->units.all.begin(); a != df::global::world->units.all.end(); a++ ) { itemIdToInventoryItem.clear(); currentlyEquipped.clear(); df::unit* unit = *a; /*if ( unit->flags1.bits.dead ) continue; */ auto oldEquipment = equipmentLog.find(unit->id); bool hadEquipment = oldEquipment != equipmentLog.end(); vector<InventoryItem>* temp; if ( hadEquipment ) { temp = &((*oldEquipment).second); } else { temp = new vector<InventoryItem>; } //vector<InventoryItem>& v = (*oldEquipment).second; vector<InventoryItem>& v = *temp; for ( auto b = v.begin(); b != v.end(); b++ ) { InventoryItem& i = *b; itemIdToInventoryItem[i.itemId] = i; } for ( size_t b = 0; b < unit->inventory.size(); b++ ) { df::unit_inventory_item* dfitem_new = unit->inventory[b]; currentlyEquipped.insert(dfitem_new->item->id); InventoryItem item_new(dfitem_new->item->id, *dfitem_new); auto c = itemIdToInventoryItem.find(dfitem_new->item->id); if ( c == itemIdToInventoryItem.end() ) { //new item equipped (probably just picked up) InventoryChangeData data(unit->id, NULL, &item_new); for ( auto h = copy.begin(); h != copy.end(); h++ ) { EventHandler handle = (*h).second; handle.eventHandler(out, (void*)&data); } continue; } InventoryItem item_old = (*c).second; df::unit_inventory_item& item0 = item_old.item; df::unit_inventory_item& item1 = item_new.item; if ( item0.mode == item1.mode && item0.body_part_id == item1.body_part_id && item0.wound_id == item1.wound_id ) continue; //some sort of change in how it's equipped InventoryChangeData data(unit->id, &item_old, &item_new); for ( auto h = copy.begin(); h != copy.end(); h++ ) { EventHandler handle = (*h).second; handle.eventHandler(out, (void*)&data); } } //check for dropped items for ( auto b = v.begin(); b != v.end(); b++ ) { InventoryItem i = *b; if ( currentlyEquipped.find(i.itemId) != currentlyEquipped.end() ) continue; //TODO: delete ptr if invalid InventoryChangeData data(unit->id, &i, NULL); for ( auto h = copy.begin(); h != copy.end(); h++ ) { EventHandler handle = (*h).second; handle.eventHandler(out, (void*)&data); } } if ( !hadEquipment ) delete temp; //update equipment vector<InventoryItem>& equipment = equipmentLog[unit->id]; equipment.clear(); for ( size_t b = 0; b < unit->inventory.size(); b++ ) { df::unit_inventory_item* dfitem = unit->inventory[b]; InventoryItem item(dfitem->item->id, *dfitem); equipment.push_back(item); } } }
static void manageInteractionEvent(color_ostream& out) { if (!df::global::world) return; multimap<Plugin*,EventHandler> copy(handlers[EventType::INTERACTION].begin(), handlers[EventType::INTERACTION].end()); std::vector<df::report*>& reports = df::global::world->status.reports; size_t a = df::report::binsearch_index(reports, lastReportInteraction, false); while (a < reports.size() && reports[a]->id <= lastReportInteraction) { a++; } if ( a < reports.size() ) updateReportToRelevantUnits(); df::report* lastAttackEvent = NULL; df::unit* lastAttacker = NULL; df::unit* lastDefender = NULL; unordered_map<int32_t,unordered_set<int32_t> > history; for ( ; a < reports.size(); a++ ) { df::report* report = reports[a]; lastReportInteraction = report->id; df::announcement_type type = report->type; if ( type != df::announcement_type::INTERACTION_ACTOR && type != df::announcement_type::INTERACTION_TARGET ) continue; if ( report->flags.bits.continuation ) continue; bool attack = type == df::announcement_type::INTERACTION_ACTOR; if ( attack ) { lastAttackEvent = report; lastAttacker = NULL; lastDefender = NULL; } vector<df::unit*> relevantUnits = gatherRelevantUnits(out, lastAttackEvent, report); InteractionData data = getAttacker(out, lastAttackEvent, lastAttacker, attack ? NULL : report, relevantUnits); if ( data.attacker < 0 ) continue; //out.print("%s,%d\n",__FILE__,__LINE__); //if ( !attack && lastAttacker && data.attacker == lastAttacker->id && lastDefender && data.defender == lastDefender->id ) // continue; //lazy way of preventing duplicates if ( attack && a+1 < reports.size() && reports[a+1]->type == df::announcement_type::INTERACTION_TARGET ) { //out.print("%s,%d\n",__FILE__,__LINE__); vector<df::unit*> relevants = gatherRelevantUnits(out, lastAttackEvent, reports[a+1]); InteractionData data2 = getAttacker(out, lastAttackEvent, lastAttacker, reports[a+1], relevants); if ( data.attacker == data2.attacker && (data.defender == -1 || data.defender == data2.defender) ) { //out.print("%s,%d\n",__FILE__,__LINE__); data = data2; a++; } } { #define HISTORY_ITEM 1 #if HISTORY_ITEM unordered_set<int32_t>& b = history[data.attacker]; if ( b.find(data.defender) != b.end() ) continue; history[data.attacker].insert(data.defender); //b.insert(data.defender); #else unordered_set<int32_t>& b = history[data.attackReport]; if ( b.find(data.defendReport) != b.end() ) continue; history[data.attackReport].insert(data.defendReport); //b.insert(data.defendReport); #endif } //out.print("%s,%d\n",__FILE__,__LINE__); lastAttacker = df::unit::find(data.attacker); lastDefender = df::unit::find(data.defender); //fire event for ( auto b = copy.begin(); b != copy.end(); b++ ) { EventHandler handle = (*b).second; handle.eventHandler(out, (void*)&data); } //TODO: deduce attacker from latest defend event first } }