From 0f23aba89db2d7d6d276cdc94c2e99cf43b6dcc5 Mon Sep 17 00:00:00 2001 From: Quietust Date: Wed, 11 Jul 2012 14:31:47 -0500 Subject: [PATCH 01/92] Sync with df-structures --- plugins/fixpositions.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/fixpositions.cpp b/plugins/fixpositions.cpp index 086848fec..bd8fb2791 100644 --- a/plugins/fixpositions.cpp +++ b/plugins/fixpositions.cpp @@ -80,9 +80,9 @@ command_result df_fixdiplomats (color_ostream &out, vector ¶meters) pos->flags.set(entity_position_flags::IS_DIPLOMAT); pos->flags.set(entity_position_flags::IS_LEADER); // not sure what these flags do, but the game sets them for a valid diplomat - pos->flags.set(entity_position_flags::anon_1); - pos->flags.set(entity_position_flags::anon_3); - pos->flags.set(entity_position_flags::anon_4); + pos->flags.set(entity_position_flags::unk_12); + pos->flags.set(entity_position_flags::unk_1a); + pos->flags.set(entity_position_flags::unk_1b); pos->name[0] = "Diplomat"; pos->name[1] = "Diplomats"; pos->precedence = 70; @@ -183,9 +183,9 @@ command_result df_fixmerchants (color_ostream &out, vector ¶meters) pos->flags.set(entity_position_flags::IS_DIPLOMAT); pos->flags.set(entity_position_flags::IS_LEADER); // not sure what these flags do, but the game sets them for a valid guild rep - pos->flags.set(entity_position_flags::anon_1); - pos->flags.set(entity_position_flags::anon_3); - pos->flags.set(entity_position_flags::anon_4); + pos->flags.set(entity_position_flags::unk_12); + pos->flags.set(entity_position_flags::unk_1a); + pos->flags.set(entity_position_flags::unk_1b); pos->name[0] = "Guild Representative"; pos->name[1] = "Guild Representatives"; pos->precedence = 40; From 2695d5509006efc63a3ab6c8f6e096bad6f9aad9 Mon Sep 17 00:00:00 2001 From: Quietust Date: Tue, 17 Jul 2012 10:27:30 -0500 Subject: [PATCH 02/92] Make indentation consistent (spaces), only display "not enabled" error if you actually try to do something --- plugins/autolabor.cpp | 1780 +++++++++++++++++++++-------------------- 1 file changed, 896 insertions(+), 884 deletions(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 3bae1e1b3..9317c595c 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -90,265 +90,265 @@ command_result autolabor (color_ostream &out, std::vector & parame DFHACK_PLUGIN("autolabor"); enum labor_mode { - DISABLE, - HAULERS, - AUTOMATIC, + DISABLE, + HAULERS, + AUTOMATIC, }; enum dwarf_state { - // Ready for a new task - IDLE, + // Ready for a new task + IDLE, - // Busy with a useful task - BUSY, + // Busy with a useful task + BUSY, - // In the military, can't work - MILITARY, + // In the military, can't work + MILITARY, - // Child or noble, can't work - CHILD, + // Child or noble, can't work + CHILD, - // Doing something that precludes working, may be busy for a while - OTHER + // Doing something that precludes working, may be busy for a while + OTHER }; const int NUM_STATE = 5; static const char *state_names[] = { - "IDLE", - "BUSY", - "MILITARY", - "CHILD", - "OTHER", + "IDLE", + "BUSY", + "MILITARY", + "CHILD", + "OTHER", }; static const dwarf_state dwarf_states[] = { - BUSY /* CarveFortification */, - BUSY /* DetailWall */, - BUSY /* DetailFloor */, - BUSY /* Dig */, - BUSY /* CarveUpwardStaircase */, - BUSY /* CarveDownwardStaircase */, - BUSY /* CarveUpDownStaircase */, - BUSY /* CarveRamp */, - BUSY /* DigChannel */, - BUSY /* FellTree */, - BUSY /* GatherPlants */, - BUSY /* RemoveConstruction */, - BUSY /* CollectWebs */, - BUSY /* BringItemToDepot */, - BUSY /* BringItemToShop */, - OTHER /* Eat */, - OTHER /* GetProvisions */, - OTHER /* Drink */, - OTHER /* Drink2 */, - OTHER /* FillWaterskin */, - OTHER /* FillWaterskin2 */, - OTHER /* Sleep */, - BUSY /* CollectSand */, - BUSY /* Fish */, - BUSY /* Hunt */, - OTHER /* HuntVermin */, - BUSY /* Kidnap */, - BUSY /* BeatCriminal */, - BUSY /* StartingFistFight */, - BUSY /* CollectTaxes */, - BUSY /* GuardTaxCollector */, - BUSY /* CatchLiveLandAnimal */, - BUSY /* CatchLiveFish */, - BUSY /* ReturnKill */, - BUSY /* CheckChest */, - BUSY /* StoreOwnedItem */, - BUSY /* PlaceItemInTomb */, - BUSY /* StoreItemInStockpile */, - BUSY /* StoreItemInBag */, - BUSY /* StoreItemInHospital */, - BUSY /* StoreItemInChest */, - BUSY /* StoreItemInCabinet */, - BUSY /* StoreWeapon */, - BUSY /* StoreArmor */, - BUSY /* StoreItemInBarrel */, - BUSY /* StoreItemInBin */, - BUSY /* SeekArtifact */, - BUSY /* SeekInfant */, - OTHER /* AttendParty */, - OTHER /* GoShopping */, - OTHER /* GoShopping2 */, - BUSY /* Clean */, - OTHER /* Rest */, - BUSY /* PickupEquipment */, - BUSY /* DumpItem */, - OTHER /* StrangeMoodCrafter */, - OTHER /* StrangeMoodJeweller */, - OTHER /* StrangeMoodForge */, - OTHER /* StrangeMoodMagmaForge */, - OTHER /* StrangeMoodBrooding */, - OTHER /* StrangeMoodFell */, - OTHER /* StrangeMoodCarpenter */, - OTHER /* StrangeMoodMason */, - OTHER /* StrangeMoodBowyer */, - OTHER /* StrangeMoodTanner */, - OTHER /* StrangeMoodWeaver */, - OTHER /* StrangeMoodGlassmaker */, - OTHER /* StrangeMoodMechanics */, - BUSY /* ConstructBuilding */, - BUSY /* ConstructDoor */, - BUSY /* ConstructFloodgate */, - BUSY /* ConstructBed */, - BUSY /* ConstructThrone */, - BUSY /* ConstructCoffin */, - BUSY /* ConstructTable */, - BUSY /* ConstructChest */, - BUSY /* ConstructBin */, - BUSY /* ConstructArmorStand */, - BUSY /* ConstructWeaponRack */, - BUSY /* ConstructCabinet */, - BUSY /* ConstructStatue */, - BUSY /* ConstructBlocks */, - BUSY /* MakeRawGlass */, - BUSY /* MakeCrafts */, - BUSY /* MintCoins */, - BUSY /* CutGems */, - BUSY /* CutGlass */, - BUSY /* EncrustWithGems */, - BUSY /* EncrustWithGlass */, - BUSY /* DestroyBuilding */, - BUSY /* SmeltOre */, - BUSY /* MeltMetalObject */, - BUSY /* ExtractMetalStrands */, - BUSY /* PlantSeeds */, - BUSY /* HarvestPlants */, - BUSY /* TrainHuntingAnimal */, - BUSY /* TrainWarAnimal */, - BUSY /* MakeWeapon */, - BUSY /* ForgeAnvil */, - BUSY /* ConstructCatapultParts */, - BUSY /* ConstructBallistaParts */, - BUSY /* MakeArmor */, - BUSY /* MakeHelm */, - BUSY /* MakePants */, - BUSY /* StudWith */, - BUSY /* ButcherAnimal */, - BUSY /* PrepareRawFish */, - BUSY /* MillPlants */, - BUSY /* BaitTrap */, - BUSY /* MilkCreature */, - BUSY /* MakeCheese */, - BUSY /* ProcessPlants */, - BUSY /* ProcessPlantsBag */, - BUSY /* ProcessPlantsVial */, - BUSY /* ProcessPlantsBarrel */, - BUSY /* PrepareMeal */, - BUSY /* WeaveCloth */, - BUSY /* MakeGloves */, - BUSY /* MakeShoes */, - BUSY /* MakeShield */, - BUSY /* MakeCage */, - BUSY /* MakeChain */, - BUSY /* MakeFlask */, - BUSY /* MakeGoblet */, - BUSY /* MakeInstrument */, - BUSY /* MakeToy */, - BUSY /* MakeAnimalTrap */, - BUSY /* MakeBarrel */, - BUSY /* MakeBucket */, - BUSY /* MakeWindow */, - BUSY /* MakeTotem */, - BUSY /* MakeAmmo */, - BUSY /* DecorateWith */, - BUSY /* MakeBackpack */, - BUSY /* MakeQuiver */, - BUSY /* MakeBallistaArrowHead */, - BUSY /* AssembleSiegeAmmo */, - BUSY /* LoadCatapult */, - BUSY /* LoadBallista */, - BUSY /* FireCatapult */, - BUSY /* FireBallista */, - BUSY /* ConstructMechanisms */, - BUSY /* MakeTrapComponent */, - BUSY /* LoadCageTrap */, - BUSY /* LoadStoneTrap */, - BUSY /* LoadWeaponTrap */, - BUSY /* CleanTrap */, - BUSY /* CastSpell */, - BUSY /* LinkBuildingToTrigger */, - BUSY /* PullLever */, - BUSY /* BrewDrink */, - BUSY /* ExtractFromPlants */, - BUSY /* ExtractFromRawFish */, - BUSY /* ExtractFromLandAnimal */, - BUSY /* TameVermin */, - BUSY /* TameAnimal */, - BUSY /* ChainAnimal */, - BUSY /* UnchainAnimal */, - BUSY /* UnchainPet */, - BUSY /* ReleaseLargeCreature */, - BUSY /* ReleasePet */, - BUSY /* ReleaseSmallCreature */, - BUSY /* HandleSmallCreature */, - BUSY /* HandleLargeCreature */, - BUSY /* CageLargeCreature */, - BUSY /* CageSmallCreature */, - BUSY /* RecoverWounded */, - BUSY /* DiagnosePatient */, - BUSY /* ImmobilizeBreak */, - BUSY /* DressWound */, - BUSY /* CleanPatient */, - BUSY /* Surgery */, - BUSY /* Suture */, - BUSY /* SetBone */, - BUSY /* PlaceInTraction */, - BUSY /* DrainAquarium */, - BUSY /* FillAquarium */, - BUSY /* FillPond */, - BUSY /* GiveWater */, - BUSY /* GiveFood */, - BUSY /* GiveWater2 */, - BUSY /* GiveFood2 */, - BUSY /* RecoverPet */, - BUSY /* PitLargeAnimal */, - BUSY /* PitSmallAnimal */, - BUSY /* SlaughterAnimal */, - BUSY /* MakeCharcoal */, - BUSY /* MakeAsh */, - BUSY /* MakeLye */, - BUSY /* MakePotashFromLye */, - BUSY /* FertilizeField */, - BUSY /* MakePotashFromAsh */, - BUSY /* DyeThread */, - BUSY /* DyeCloth */, - BUSY /* SewImage */, - BUSY /* MakePipeSection */, - BUSY /* OperatePump */, - OTHER /* ManageWorkOrders */, - OTHER /* UpdateStockpileRecords */, - OTHER /* TradeAtDepot */, - BUSY /* ConstructHatchCover */, - BUSY /* ConstructGrate */, - BUSY /* RemoveStairs */, - BUSY /* ConstructQuern */, - BUSY /* ConstructMillstone */, - BUSY /* ConstructSplint */, - BUSY /* ConstructCrutch */, - BUSY /* ConstructTractionBench */, - BUSY /* CleanSelf */, - BUSY /* BringCrutch */, - BUSY /* ApplyCast */, - BUSY /* CustomReaction */, - BUSY /* ConstructSlab */, - BUSY /* EngraveSlab */, - BUSY /* ShearCreature */, - BUSY /* SpinThread */, - BUSY /* PenLargeAnimal */, - BUSY /* PenSmallAnimal */, - BUSY /* MakeTool */, - BUSY /* CollectClay */, - BUSY /* InstallColonyInHive */, - BUSY /* CollectHiveProducts */, - OTHER /* CauseTrouble */, - OTHER /* DrinkBlood */, - OTHER /* ReportCrime */, - OTHER /* ExecuteCriminal */, + BUSY /* CarveFortification */, + BUSY /* DetailWall */, + BUSY /* DetailFloor */, + BUSY /* Dig */, + BUSY /* CarveUpwardStaircase */, + BUSY /* CarveDownwardStaircase */, + BUSY /* CarveUpDownStaircase */, + BUSY /* CarveRamp */, + BUSY /* DigChannel */, + BUSY /* FellTree */, + BUSY /* GatherPlants */, + BUSY /* RemoveConstruction */, + BUSY /* CollectWebs */, + BUSY /* BringItemToDepot */, + BUSY /* BringItemToShop */, + OTHER /* Eat */, + OTHER /* GetProvisions */, + OTHER /* Drink */, + OTHER /* Drink2 */, + OTHER /* FillWaterskin */, + OTHER /* FillWaterskin2 */, + OTHER /* Sleep */, + BUSY /* CollectSand */, + BUSY /* Fish */, + BUSY /* Hunt */, + OTHER /* HuntVermin */, + BUSY /* Kidnap */, + BUSY /* BeatCriminal */, + BUSY /* StartingFistFight */, + BUSY /* CollectTaxes */, + BUSY /* GuardTaxCollector */, + BUSY /* CatchLiveLandAnimal */, + BUSY /* CatchLiveFish */, + BUSY /* ReturnKill */, + BUSY /* CheckChest */, + BUSY /* StoreOwnedItem */, + BUSY /* PlaceItemInTomb */, + BUSY /* StoreItemInStockpile */, + BUSY /* StoreItemInBag */, + BUSY /* StoreItemInHospital */, + BUSY /* StoreItemInChest */, + BUSY /* StoreItemInCabinet */, + BUSY /* StoreWeapon */, + BUSY /* StoreArmor */, + BUSY /* StoreItemInBarrel */, + BUSY /* StoreItemInBin */, + BUSY /* SeekArtifact */, + BUSY /* SeekInfant */, + OTHER /* AttendParty */, + OTHER /* GoShopping */, + OTHER /* GoShopping2 */, + BUSY /* Clean */, + OTHER /* Rest */, + BUSY /* PickupEquipment */, + BUSY /* DumpItem */, + OTHER /* StrangeMoodCrafter */, + OTHER /* StrangeMoodJeweller */, + OTHER /* StrangeMoodForge */, + OTHER /* StrangeMoodMagmaForge */, + OTHER /* StrangeMoodBrooding */, + OTHER /* StrangeMoodFell */, + OTHER /* StrangeMoodCarpenter */, + OTHER /* StrangeMoodMason */, + OTHER /* StrangeMoodBowyer */, + OTHER /* StrangeMoodTanner */, + OTHER /* StrangeMoodWeaver */, + OTHER /* StrangeMoodGlassmaker */, + OTHER /* StrangeMoodMechanics */, + BUSY /* ConstructBuilding */, + BUSY /* ConstructDoor */, + BUSY /* ConstructFloodgate */, + BUSY /* ConstructBed */, + BUSY /* ConstructThrone */, + BUSY /* ConstructCoffin */, + BUSY /* ConstructTable */, + BUSY /* ConstructChest */, + BUSY /* ConstructBin */, + BUSY /* ConstructArmorStand */, + BUSY /* ConstructWeaponRack */, + BUSY /* ConstructCabinet */, + BUSY /* ConstructStatue */, + BUSY /* ConstructBlocks */, + BUSY /* MakeRawGlass */, + BUSY /* MakeCrafts */, + BUSY /* MintCoins */, + BUSY /* CutGems */, + BUSY /* CutGlass */, + BUSY /* EncrustWithGems */, + BUSY /* EncrustWithGlass */, + BUSY /* DestroyBuilding */, + BUSY /* SmeltOre */, + BUSY /* MeltMetalObject */, + BUSY /* ExtractMetalStrands */, + BUSY /* PlantSeeds */, + BUSY /* HarvestPlants */, + BUSY /* TrainHuntingAnimal */, + BUSY /* TrainWarAnimal */, + BUSY /* MakeWeapon */, + BUSY /* ForgeAnvil */, + BUSY /* ConstructCatapultParts */, + BUSY /* ConstructBallistaParts */, + BUSY /* MakeArmor */, + BUSY /* MakeHelm */, + BUSY /* MakePants */, + BUSY /* StudWith */, + BUSY /* ButcherAnimal */, + BUSY /* PrepareRawFish */, + BUSY /* MillPlants */, + BUSY /* BaitTrap */, + BUSY /* MilkCreature */, + BUSY /* MakeCheese */, + BUSY /* ProcessPlants */, + BUSY /* ProcessPlantsBag */, + BUSY /* ProcessPlantsVial */, + BUSY /* ProcessPlantsBarrel */, + BUSY /* PrepareMeal */, + BUSY /* WeaveCloth */, + BUSY /* MakeGloves */, + BUSY /* MakeShoes */, + BUSY /* MakeShield */, + BUSY /* MakeCage */, + BUSY /* MakeChain */, + BUSY /* MakeFlask */, + BUSY /* MakeGoblet */, + BUSY /* MakeInstrument */, + BUSY /* MakeToy */, + BUSY /* MakeAnimalTrap */, + BUSY /* MakeBarrel */, + BUSY /* MakeBucket */, + BUSY /* MakeWindow */, + BUSY /* MakeTotem */, + BUSY /* MakeAmmo */, + BUSY /* DecorateWith */, + BUSY /* MakeBackpack */, + BUSY /* MakeQuiver */, + BUSY /* MakeBallistaArrowHead */, + BUSY /* AssembleSiegeAmmo */, + BUSY /* LoadCatapult */, + BUSY /* LoadBallista */, + BUSY /* FireCatapult */, + BUSY /* FireBallista */, + BUSY /* ConstructMechanisms */, + BUSY /* MakeTrapComponent */, + BUSY /* LoadCageTrap */, + BUSY /* LoadStoneTrap */, + BUSY /* LoadWeaponTrap */, + BUSY /* CleanTrap */, + BUSY /* CastSpell */, + BUSY /* LinkBuildingToTrigger */, + BUSY /* PullLever */, + BUSY /* BrewDrink */, + BUSY /* ExtractFromPlants */, + BUSY /* ExtractFromRawFish */, + BUSY /* ExtractFromLandAnimal */, + BUSY /* TameVermin */, + BUSY /* TameAnimal */, + BUSY /* ChainAnimal */, + BUSY /* UnchainAnimal */, + BUSY /* UnchainPet */, + BUSY /* ReleaseLargeCreature */, + BUSY /* ReleasePet */, + BUSY /* ReleaseSmallCreature */, + BUSY /* HandleSmallCreature */, + BUSY /* HandleLargeCreature */, + BUSY /* CageLargeCreature */, + BUSY /* CageSmallCreature */, + BUSY /* RecoverWounded */, + BUSY /* DiagnosePatient */, + BUSY /* ImmobilizeBreak */, + BUSY /* DressWound */, + BUSY /* CleanPatient */, + BUSY /* Surgery */, + BUSY /* Suture */, + BUSY /* SetBone */, + BUSY /* PlaceInTraction */, + BUSY /* DrainAquarium */, + BUSY /* FillAquarium */, + BUSY /* FillPond */, + BUSY /* GiveWater */, + BUSY /* GiveFood */, + BUSY /* GiveWater2 */, + BUSY /* GiveFood2 */, + BUSY /* RecoverPet */, + BUSY /* PitLargeAnimal */, + BUSY /* PitSmallAnimal */, + BUSY /* SlaughterAnimal */, + BUSY /* MakeCharcoal */, + BUSY /* MakeAsh */, + BUSY /* MakeLye */, + BUSY /* MakePotashFromLye */, + BUSY /* FertilizeField */, + BUSY /* MakePotashFromAsh */, + BUSY /* DyeThread */, + BUSY /* DyeCloth */, + BUSY /* SewImage */, + BUSY /* MakePipeSection */, + BUSY /* OperatePump */, + OTHER /* ManageWorkOrders */, + OTHER /* UpdateStockpileRecords */, + OTHER /* TradeAtDepot */, + BUSY /* ConstructHatchCover */, + BUSY /* ConstructGrate */, + BUSY /* RemoveStairs */, + BUSY /* ConstructQuern */, + BUSY /* ConstructMillstone */, + BUSY /* ConstructSplint */, + BUSY /* ConstructCrutch */, + BUSY /* ConstructTractionBench */, + BUSY /* CleanSelf */, + BUSY /* BringCrutch */, + BUSY /* ApplyCast */, + BUSY /* CustomReaction */, + BUSY /* ConstructSlab */, + BUSY /* EngraveSlab */, + BUSY /* ShearCreature */, + BUSY /* SpinThread */, + BUSY /* PenLargeAnimal */, + BUSY /* PenSmallAnimal */, + BUSY /* MakeTool */, + BUSY /* CollectClay */, + BUSY /* InstallColonyInHive */, + BUSY /* CollectHiveProducts */, + OTHER /* CauseTrouble */, + OTHER /* DrinkBlood */, + OTHER /* ReportCrime */, + OTHER /* ExecuteCriminal */, BUSY /* TrainAnimal */, BUSY /* CarveTrack */, BUSY /* PushTrackVehicle */, @@ -360,106 +360,106 @@ struct labor_info { PersistentDataItem config; - bool is_exclusive; - int active_dwarfs; + bool is_exclusive; + int active_dwarfs; - labor_mode mode() { return (labor_mode) config.ival(0); } - void set_mode(labor_mode mode) { config.ival(0) = mode; } + labor_mode mode() { return (labor_mode) config.ival(0); } + void set_mode(labor_mode mode) { config.ival(0) = mode; } - int minimum_dwarfs() { return config.ival(1); } - void set_minimum_dwarfs(int minimum_dwarfs) { config.ival(1) = minimum_dwarfs; } + int minimum_dwarfs() { return config.ival(1); } + void set_minimum_dwarfs(int minimum_dwarfs) { config.ival(1) = minimum_dwarfs; } - int maximum_dwarfs() { return config.ival(2); } - void set_maximum_dwarfs(int maximum_dwarfs) { config.ival(2) = maximum_dwarfs; } + int maximum_dwarfs() { return config.ival(2); } + void set_maximum_dwarfs(int maximum_dwarfs) { config.ival(2) = maximum_dwarfs; } }; struct labor_default { - labor_mode mode; - bool is_exclusive; - int minimum_dwarfs; - int maximum_dwarfs; - int active_dwarfs; + labor_mode mode; + bool is_exclusive; + int minimum_dwarfs; + int maximum_dwarfs; + int active_dwarfs; }; static std::vector labor_infos; static const struct labor_default default_labor_infos[] = { - /* MINE */ {AUTOMATIC, true, 2, 200, 0}, - /* HAUL_STONE */ {HAULERS, false, 1, 200, 0}, - /* HAUL_WOOD */ {HAULERS, false, 1, 200, 0}, - /* HAUL_BODY */ {HAULERS, false, 1, 200, 0}, - /* HAUL_FOOD */ {HAULERS, false, 1, 200, 0}, - /* HAUL_REFUSE */ {HAULERS, false, 1, 200, 0}, - /* HAUL_ITEM */ {HAULERS, false, 1, 200, 0}, - /* HAUL_FURNITURE */ {HAULERS, false, 1, 200, 0}, - /* HAUL_ANIMAL */ {HAULERS, false, 1, 200, 0}, - /* CLEAN */ {HAULERS, false, 1, 200, 0}, - /* CUTWOOD */ {AUTOMATIC, true, 1, 200, 0}, - /* CARPENTER */ {AUTOMATIC, false, 1, 200, 0}, - /* DETAIL */ {AUTOMATIC, false, 1, 200, 0}, - /* MASON */ {AUTOMATIC, false, 1, 200, 0}, - /* ARCHITECT */ {AUTOMATIC, false, 1, 200, 0}, - /* ANIMALTRAIN */ {AUTOMATIC, false, 1, 200, 0}, - /* ANIMALCARE */ {AUTOMATIC, false, 1, 200, 0}, - /* DIAGNOSE */ {AUTOMATIC, false, 1, 200, 0}, - /* SURGERY */ {AUTOMATIC, false, 1, 200, 0}, - /* BONE_SETTING */ {AUTOMATIC, false, 1, 200, 0}, - /* SUTURING */ {AUTOMATIC, false, 1, 200, 0}, - /* DRESSING_WOUNDS */ {AUTOMATIC, false, 1, 200, 0}, - /* FEED_WATER_CIVILIANS */ {AUTOMATIC, false, 200, 200, 0}, - /* RECOVER_WOUNDED */ {HAULERS, false, 1, 200, 0}, - /* BUTCHER */ {AUTOMATIC, false, 1, 200, 0}, - /* TRAPPER */ {AUTOMATIC, false, 1, 200, 0}, - /* DISSECT_VERMIN */ {AUTOMATIC, false, 1, 200, 0}, - /* LEATHER */ {AUTOMATIC, false, 1, 200, 0}, - /* TANNER */ {AUTOMATIC, false, 1, 200, 0}, - /* BREWER */ {AUTOMATIC, false, 1, 200, 0}, - /* ALCHEMIST */ {AUTOMATIC, false, 1, 200, 0}, - /* SOAP_MAKER */ {AUTOMATIC, false, 1, 200, 0}, - /* WEAVER */ {AUTOMATIC, false, 1, 200, 0}, - /* CLOTHESMAKER */ {AUTOMATIC, false, 1, 200, 0}, - /* MILLER */ {AUTOMATIC, false, 1, 200, 0}, - /* PROCESS_PLANT */ {AUTOMATIC, false, 1, 200, 0}, - /* MAKE_CHEESE */ {AUTOMATIC, false, 1, 200, 0}, - /* MILK */ {AUTOMATIC, false, 1, 200, 0}, - /* COOK */ {AUTOMATIC, false, 1, 200, 0}, - /* PLANT */ {AUTOMATIC, false, 1, 200, 0}, - /* HERBALIST */ {AUTOMATIC, false, 1, 200, 0}, - /* FISH */ {AUTOMATIC, false, 1, 1, 0}, - /* CLEAN_FISH */ {AUTOMATIC, false, 1, 200, 0}, - /* DISSECT_FISH */ {AUTOMATIC, false, 1, 200, 0}, - /* HUNT */ {AUTOMATIC, true, 1, 1, 0}, - /* SMELT */ {AUTOMATIC, false, 1, 200, 0}, - /* FORGE_WEAPON */ {AUTOMATIC, false, 1, 200, 0}, - /* FORGE_ARMOR */ {AUTOMATIC, false, 1, 200, 0}, - /* FORGE_FURNITURE */ {AUTOMATIC, false, 1, 200, 0}, - /* METAL_CRAFT */ {AUTOMATIC, false, 1, 200, 0}, - /* CUT_GEM */ {AUTOMATIC, false, 1, 200, 0}, - /* ENCRUST_GEM */ {AUTOMATIC, false, 1, 200, 0}, - /* WOOD_CRAFT */ {AUTOMATIC, false, 1, 200, 0}, - /* STONE_CRAFT */ {AUTOMATIC, false, 1, 200, 0}, - /* BONE_CARVE */ {AUTOMATIC, false, 1, 200, 0}, - /* GLASSMAKER */ {AUTOMATIC, false, 1, 200, 0}, - /* EXTRACT_STRAND */ {AUTOMATIC, false, 1, 200, 0}, - /* SIEGECRAFT */ {AUTOMATIC, false, 1, 200, 0}, - /* SIEGEOPERATE */ {AUTOMATIC, false, 1, 200, 0}, - /* BOWYER */ {AUTOMATIC, false, 1, 200, 0}, - /* MECHANIC */ {AUTOMATIC, false, 1, 200, 0}, - /* POTASH_MAKING */ {AUTOMATIC, false, 1, 200, 0}, - /* LYE_MAKING */ {AUTOMATIC, false, 1, 200, 0}, - /* DYER */ {AUTOMATIC, false, 1, 200, 0}, - /* BURN_WOOD */ {AUTOMATIC, false, 1, 200, 0}, - /* OPERATE_PUMP */ {AUTOMATIC, false, 1, 200, 0}, - /* SHEARER */ {AUTOMATIC, false, 1, 200, 0}, - /* SPINNER */ {AUTOMATIC, false, 1, 200, 0}, - /* POTTERY */ {AUTOMATIC, false, 1, 200, 0}, - /* GLAZING */ {AUTOMATIC, false, 1, 200, 0}, - /* PRESSING */ {AUTOMATIC, false, 1, 200, 0}, - /* BEEKEEPING */ {AUTOMATIC, false, 1, 1, 0}, // reduce risk of stuck beekeepers (see http://www.bay12games.com/dwarves/mantisbt/view.php?id=3981) - /* WAX_WORKING */ {AUTOMATIC, false, 1, 200, 0}, - /* PUSH_HAUL_VEHICLES */ {HAULERS, false, 1, 200, 0} + /* MINE */ {AUTOMATIC, true, 2, 200, 0}, + /* HAUL_STONE */ {HAULERS, false, 1, 200, 0}, + /* HAUL_WOOD */ {HAULERS, false, 1, 200, 0}, + /* HAUL_BODY */ {HAULERS, false, 1, 200, 0}, + /* HAUL_FOOD */ {HAULERS, false, 1, 200, 0}, + /* HAUL_REFUSE */ {HAULERS, false, 1, 200, 0}, + /* HAUL_ITEM */ {HAULERS, false, 1, 200, 0}, + /* HAUL_FURNITURE */ {HAULERS, false, 1, 200, 0}, + /* HAUL_ANIMAL */ {HAULERS, false, 1, 200, 0}, + /* CLEAN */ {HAULERS, false, 1, 200, 0}, + /* CUTWOOD */ {AUTOMATIC, true, 1, 200, 0}, + /* CARPENTER */ {AUTOMATIC, false, 1, 200, 0}, + /* DETAIL */ {AUTOMATIC, false, 1, 200, 0}, + /* MASON */ {AUTOMATIC, false, 1, 200, 0}, + /* ARCHITECT */ {AUTOMATIC, false, 1, 200, 0}, + /* ANIMALTRAIN */ {AUTOMATIC, false, 1, 200, 0}, + /* ANIMALCARE */ {AUTOMATIC, false, 1, 200, 0}, + /* DIAGNOSE */ {AUTOMATIC, false, 1, 200, 0}, + /* SURGERY */ {AUTOMATIC, false, 1, 200, 0}, + /* BONE_SETTING */ {AUTOMATIC, false, 1, 200, 0}, + /* SUTURING */ {AUTOMATIC, false, 1, 200, 0}, + /* DRESSING_WOUNDS */ {AUTOMATIC, false, 1, 200, 0}, + /* FEED_WATER_CIVILIANS */ {AUTOMATIC, false, 200, 200, 0}, + /* RECOVER_WOUNDED */ {HAULERS, false, 1, 200, 0}, + /* BUTCHER */ {AUTOMATIC, false, 1, 200, 0}, + /* TRAPPER */ {AUTOMATIC, false, 1, 200, 0}, + /* DISSECT_VERMIN */ {AUTOMATIC, false, 1, 200, 0}, + /* LEATHER */ {AUTOMATIC, false, 1, 200, 0}, + /* TANNER */ {AUTOMATIC, false, 1, 200, 0}, + /* BREWER */ {AUTOMATIC, false, 1, 200, 0}, + /* ALCHEMIST */ {AUTOMATIC, false, 1, 200, 0}, + /* SOAP_MAKER */ {AUTOMATIC, false, 1, 200, 0}, + /* WEAVER */ {AUTOMATIC, false, 1, 200, 0}, + /* CLOTHESMAKER */ {AUTOMATIC, false, 1, 200, 0}, + /* MILLER */ {AUTOMATIC, false, 1, 200, 0}, + /* PROCESS_PLANT */ {AUTOMATIC, false, 1, 200, 0}, + /* MAKE_CHEESE */ {AUTOMATIC, false, 1, 200, 0}, + /* MILK */ {AUTOMATIC, false, 1, 200, 0}, + /* COOK */ {AUTOMATIC, false, 1, 200, 0}, + /* PLANT */ {AUTOMATIC, false, 1, 200, 0}, + /* HERBALIST */ {AUTOMATIC, false, 1, 200, 0}, + /* FISH */ {AUTOMATIC, false, 1, 1, 0}, + /* CLEAN_FISH */ {AUTOMATIC, false, 1, 200, 0}, + /* DISSECT_FISH */ {AUTOMATIC, false, 1, 200, 0}, + /* HUNT */ {AUTOMATIC, true, 1, 1, 0}, + /* SMELT */ {AUTOMATIC, false, 1, 200, 0}, + /* FORGE_WEAPON */ {AUTOMATIC, false, 1, 200, 0}, + /* FORGE_ARMOR */ {AUTOMATIC, false, 1, 200, 0}, + /* FORGE_FURNITURE */ {AUTOMATIC, false, 1, 200, 0}, + /* METAL_CRAFT */ {AUTOMATIC, false, 1, 200, 0}, + /* CUT_GEM */ {AUTOMATIC, false, 1, 200, 0}, + /* ENCRUST_GEM */ {AUTOMATIC, false, 1, 200, 0}, + /* WOOD_CRAFT */ {AUTOMATIC, false, 1, 200, 0}, + /* STONE_CRAFT */ {AUTOMATIC, false, 1, 200, 0}, + /* BONE_CARVE */ {AUTOMATIC, false, 1, 200, 0}, + /* GLASSMAKER */ {AUTOMATIC, false, 1, 200, 0}, + /* EXTRACT_STRAND */ {AUTOMATIC, false, 1, 200, 0}, + /* SIEGECRAFT */ {AUTOMATIC, false, 1, 200, 0}, + /* SIEGEOPERATE */ {AUTOMATIC, false, 1, 200, 0}, + /* BOWYER */ {AUTOMATIC, false, 1, 200, 0}, + /* MECHANIC */ {AUTOMATIC, false, 1, 200, 0}, + /* POTASH_MAKING */ {AUTOMATIC, false, 1, 200, 0}, + /* LYE_MAKING */ {AUTOMATIC, false, 1, 200, 0}, + /* DYER */ {AUTOMATIC, false, 1, 200, 0}, + /* BURN_WOOD */ {AUTOMATIC, false, 1, 200, 0}, + /* OPERATE_PUMP */ {AUTOMATIC, false, 1, 200, 0}, + /* SHEARER */ {AUTOMATIC, false, 1, 200, 0}, + /* SPINNER */ {AUTOMATIC, false, 1, 200, 0}, + /* POTTERY */ {AUTOMATIC, false, 1, 200, 0}, + /* GLAZING */ {AUTOMATIC, false, 1, 200, 0}, + /* PRESSING */ {AUTOMATIC, false, 1, 200, 0}, + /* BEEKEEPING */ {AUTOMATIC, false, 1, 1, 0}, // reduce risk of stuck beekeepers (see http://www.bay12games.com/dwarves/mantisbt/view.php?id=3981) + /* WAX_WORKING */ {AUTOMATIC, false, 1, 200, 0}, + /* PUSH_HAUL_VEHICLES */ {HAULERS, false, 1, 200, 0} }; static const int responsibility_penalties[] = { @@ -492,12 +492,12 @@ static const int responsibility_penalties[] = { struct dwarf_info_t { - int highest_skill; - int total_skill; - int mastery_penalty; - int assigned_jobs; - dwarf_state state; - bool has_exclusive_labor; + int highest_skill; + int total_skill; + int mastery_penalty; + int assigned_jobs; + dwarf_state state; + bool has_exclusive_labor; int noble_penalty; // penalty for assignment due to noble status bool medical; // this dwarf has medical responsibility bool trader; // this dwarf has trade responsibility @@ -521,14 +521,14 @@ static void setOptionEnabled(ConfigFlags flag, bool on) static void cleanup_state() { - labor_infos.clear(); + labor_infos.clear(); } static void reset_labor(df::enums::unit_labor::unit_labor labor) { - labor_infos[labor].set_minimum_dwarfs(default_labor_infos[labor].minimum_dwarfs); - labor_infos[labor].set_maximum_dwarfs(default_labor_infos[labor].maximum_dwarfs); - labor_infos[labor].set_mode(default_labor_infos[labor].mode); + labor_infos[labor].set_minimum_dwarfs(default_labor_infos[labor].minimum_dwarfs); + labor_infos[labor].set_maximum_dwarfs(default_labor_infos[labor].maximum_dwarfs); + labor_infos[labor].set_mode(default_labor_infos[labor].mode); } static void init_state() @@ -539,42 +539,42 @@ static void init_state() if (config.isValid() && config.ival(0) == -1) config.ival(0) = 0; - enable_autolabor = isOptionEnabled(CF_ENABLED); + enable_autolabor = isOptionEnabled(CF_ENABLED); - if (!enable_autolabor) - return; + if (!enable_autolabor) + return; - // Load labors from save - labor_infos.resize(ARRAY_COUNT(default_labor_infos)); + // Load labors from save + labor_infos.resize(ARRAY_COUNT(default_labor_infos)); - std::vector items; + std::vector items; pworld->GetPersistentData(&items, "autolabor/labors/", true); - for (auto p = items.begin(); p != items.end(); p++) - { - string key = p->key(); - df::enums::unit_labor::unit_labor labor = (df::enums::unit_labor::unit_labor) atoi(key.substr(strlen("autolabor/labors/")).c_str()); - if (labor >= 0 && labor <= labor_infos.size()) - { - labor_infos[labor].config = *p; - labor_infos[labor].is_exclusive = default_labor_infos[labor].is_exclusive; - labor_infos[labor].active_dwarfs = 0; - } - } - - // Add default labors for those not in save + for (auto p = items.begin(); p != items.end(); p++) + { + string key = p->key(); + df::enums::unit_labor::unit_labor labor = (df::enums::unit_labor::unit_labor) atoi(key.substr(strlen("autolabor/labors/")).c_str()); + if (labor >= 0 && labor <= labor_infos.size()) + { + labor_infos[labor].config = *p; + labor_infos[labor].is_exclusive = default_labor_infos[labor].is_exclusive; + labor_infos[labor].active_dwarfs = 0; + } + } + + // Add default labors for those not in save for (int i = 0; i < ARRAY_COUNT(default_labor_infos); i++) { - if (labor_infos[i].config.isValid()) - continue; + if (labor_infos[i].config.isValid()) + continue; - std::stringstream name; - name << "autolabor/labors/" << i; + std::stringstream name; + name << "autolabor/labors/" << i; - labor_infos[i].config = pworld->AddPersistentData(name.str()); + labor_infos[i].config = pworld->AddPersistentData(name.str()); - labor_infos[i].is_exclusive = default_labor_infos[i].is_exclusive; - labor_infos[i].active_dwarfs = 0; - reset_labor((df::enums::unit_labor::unit_labor) i); + labor_infos[i].is_exclusive = default_labor_infos[i].is_exclusive; + labor_infos[i].active_dwarfs = 0; + reset_labor((df::enums::unit_labor::unit_labor) i); } } @@ -592,8 +592,8 @@ static void enable_plugin(color_ostream &out) enable_autolabor = true; out << "Enabling the plugin." << endl; - cleanup_state(); - init_state(); + cleanup_state(); + init_state(); } DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) @@ -608,49 +608,49 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector []\n" - " Set number of dwarves assigned to a labor.\n" - " autolabor haulers\n" - " Set a labor to be handled by hauler dwarves.\n" - " autolabor disable\n" - " Turn off autolabor for a specific labor.\n" - " autolabor reset\n" - " Return a labor to the default handling.\n" - " autolabor reset-all\n" - " Return all labors to the default handling.\n" - " autolabor list\n" - " List current status of all labors.\n" - " autolabor status\n" - " Show basic status information.\n" - "Function:\n" - " When enabled, autolabor periodically checks your dwarves and enables or\n" - " disables labors. It tries to keep as many dwarves as possible busy but\n" - " also tries to have dwarves specialize in specific skills.\n" - " Warning: autolabor will override any manual changes you make to labors\n" - " while it is enabled.\n" - "Examples:\n" - " autolabor MINE 2\n" - " Keep at least 2 dwarves with mining enabled.\n" - " autolabor CUT_GEM 1 1\n" - " Keep exactly 1 dwarf with gemcutting enabled.\n" - " autolabor FEED_WATER_CIVILIANS haulers\n" - " Have haulers feed and water wounded dwarves.\n" - " autolabor CUTWOOD disable\n" - " Turn off autolabor for wood cutting.\n" + " autolabor disable\n" + " Enables or disables the plugin.\n" + " autolabor []\n" + " Set number of dwarves assigned to a labor.\n" + " autolabor haulers\n" + " Set a labor to be handled by hauler dwarves.\n" + " autolabor disable\n" + " Turn off autolabor for a specific labor.\n" + " autolabor reset\n" + " Return a labor to the default handling.\n" + " autolabor reset-all\n" + " Return all labors to the default handling.\n" + " autolabor list\n" + " List current status of all labors.\n" + " autolabor status\n" + " Show basic status information.\n" + "Function:\n" + " When enabled, autolabor periodically checks your dwarves and enables or\n" + " disables labors. It tries to keep as many dwarves as possible busy but\n" + " also tries to have dwarves specialize in specific skills.\n" + " Warning: autolabor will override any manual changes you make to labors\n" + " while it is enabled.\n" + "Examples:\n" + " autolabor MINE 2\n" + " Keep at least 2 dwarves with mining enabled.\n" + " autolabor CUT_GEM 1 1\n" + " Keep exactly 1 dwarf with gemcutting enabled.\n" + " autolabor FEED_WATER_CIVILIANS haulers\n" + " Have haulers feed and water wounded dwarves.\n" + " autolabor CUTWOOD disable\n" + " Turn off autolabor for wood cutting.\n" )); - init_state(); + init_state(); return CR_OK; } DFhackCExport command_result plugin_shutdown ( color_ostream &out ) { - cleanup_state(); + cleanup_state(); - return CR_OK; + return CR_OK; } // sorting objects @@ -704,7 +704,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { - static int step_count = 0; + static int step_count = 0; // check run conditions if(!world || !world->map.block_index || !enable_autolabor) { @@ -712,31 +712,31 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) return CR_OK; } - if (++step_count < 60) - return CR_OK; - step_count = 0; + if (++step_count < 60) + return CR_OK; + step_count = 0; uint32_t race = ui->race_id; uint32_t civ = ui->civ_id; - std::vector dwarfs; + std::vector dwarfs; - bool has_butchers = false; - bool has_fishery = false; + bool has_butchers = false; + bool has_fishery = false; bool trader_requested = false; - for (int i = 0; i < world->buildings.all.size(); ++i) - { - df::building *build = world->buildings.all[i]; - auto type = build->getType(); - if (df::enums::building_type::Workshop == type) - { - auto subType = build->getSubtype(); - if (df::enums::workshop_type::Butchers == subType) - has_butchers = true; - if (df::enums::workshop_type::Fishery == subType) - has_fishery = true; - } + for (int i = 0; i < world->buildings.all.size(); ++i) + { + df::building *build = world->buildings.all[i]; + auto type = build->getType(); + if (df::enums::building_type::Workshop == type) + { + auto subType = build->getSubtype(); + if (df::enums::workshop_type::Butchers == subType) + has_butchers = true; + if (df::enums::workshop_type::Fishery == subType) + has_fishery = true; + } else if (df::enums::building_type::TradeDepot == type) { df::building_tradedepotst* depot = (df::building_tradedepotst*) build; @@ -744,29 +744,29 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if (print_debug) out.print("Trade depot found and trader requested, trader will be excluded from all labors.\n"); } - } + } for (int i = 0; i < world->units.all.size(); ++i) { df::unit* cre = world->units.all[i]; - if (cre->race == race && cre->civ_id == civ && !cre->flags1.bits.marauder && !cre->flags1.bits.diplomat && !cre->flags1.bits.merchant && - !cre->flags1.bits.dead && !cre->flags1.bits.forest) { - dwarfs.push_back(cre); + if (cre->race == race && cre->civ_id == civ && !cre->flags1.bits.marauder && !cre->flags1.bits.diplomat && !cre->flags1.bits.merchant && + !cre->flags1.bits.dead && !cre->flags1.bits.forest) { + dwarfs.push_back(cre); } } - int n_dwarfs = dwarfs.size(); + int n_dwarfs = dwarfs.size(); - if (n_dwarfs == 0) - return CR_OK; + if (n_dwarfs == 0) + return CR_OK; - std::vector dwarf_info(n_dwarfs); + std::vector dwarf_info(n_dwarfs); - // Find total skill and highest skill for each dwarf. More skilled dwarves shouldn't be used for minor tasks. + // Find total skill and highest skill for each dwarf. More skilled dwarves shouldn't be used for minor tasks. - for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) - { -// assert(dwarfs[dwarf]->status.souls.size() > 0); + for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) + { +// assert(dwarfs[dwarf]->status.souls.size() > 0); // assert fails can cause DF to crash, so don't do that if (dwarfs[dwarf]->status.souls.size() <= 0) @@ -780,10 +780,10 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) for (int i = 0; i < hf->entity_links.size(); i++) { df::histfig_entity_link* hfelink = hf->entity_links.at(i); if (hfelink->getType() == df::histfig_entity_link_type::POSITION) { - df::histfig_entity_link_positionst *epos = + df::histfig_entity_link_positionst *epos = (df::histfig_entity_link_positionst*) hfelink; df::historical_entity* entity = df::historical_entity::find(epos->entity_id); - if (!entity) + if (!entity) continue; df::entity_position_assignment* assignment = binsearch_in_vector(entity->positions.assignments, epos->assignment_id); if (!assignment) @@ -792,8 +792,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if (!position) continue; - for (int n = 0; n < 25; n++) - if (position->responsibilities[n]) + for (int n = 0; n < 25; n++) + if (position->responsibilities[n]) noble_penalty += responsibility_penalties[n]; if (position->responsibilities[df::entity_position_responsibility::HEALTH_MANAGEMENT]) @@ -806,420 +806,420 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) dwarf_info[dwarf].noble_penalty = noble_penalty; } - for (auto s = dwarfs[dwarf]->status.souls[0]->skills.begin(); s != dwarfs[dwarf]->status.souls[0]->skills.end(); s++) - { - df::job_skill skill = (*s)->id; + for (auto s = dwarfs[dwarf]->status.souls[0]->skills.begin(); s != dwarfs[dwarf]->status.souls[0]->skills.end(); s++) + { + df::job_skill skill = (*s)->id; - df::job_skill_class skill_class = ENUM_ATTR(job_skill, type, skill); + df::job_skill_class skill_class = ENUM_ATTR(job_skill, type, skill); - int skill_level = (*s)->rating; - int skill_experience = (*s)->experience; + int skill_level = (*s)->rating; + int skill_experience = (*s)->experience; - // Track total & highest skill among normal/medical skills. (We don't care about personal or social skills.) + // Track total & highest skill among normal/medical skills. (We don't care about personal or social skills.) - if (skill_class != df::enums::job_skill_class::Normal && skill_class != df::enums::job_skill_class::Medical) - continue; + if (skill_class != df::enums::job_skill_class::Normal && skill_class != df::enums::job_skill_class::Medical) + continue; - if (dwarf_info[dwarf].highest_skill < skill_level) - dwarf_info[dwarf].highest_skill = skill_level; - dwarf_info[dwarf].total_skill += skill_level; - } - } + if (dwarf_info[dwarf].highest_skill < skill_level) + dwarf_info[dwarf].highest_skill = skill_level; + dwarf_info[dwarf].total_skill += skill_level; + } + } - // Calculate a base penalty for using each dwarf for a task he isn't good at. + // Calculate a base penalty for using each dwarf for a task he isn't good at. - for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) - { - dwarf_info[dwarf].mastery_penalty -= 40 * dwarf_info[dwarf].highest_skill; - dwarf_info[dwarf].mastery_penalty -= 10 * dwarf_info[dwarf].total_skill; + for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) + { + dwarf_info[dwarf].mastery_penalty -= 40 * dwarf_info[dwarf].highest_skill; + dwarf_info[dwarf].mastery_penalty -= 10 * dwarf_info[dwarf].total_skill; dwarf_info[dwarf].mastery_penalty -= dwarf_info[dwarf].noble_penalty; - for (int labor = ENUM_FIRST_ITEM(unit_labor); labor <= ENUM_LAST_ITEM(unit_labor); labor++) - { - if (labor == df::enums::unit_labor::NONE) - continue; + for (int labor = ENUM_FIRST_ITEM(unit_labor); labor <= ENUM_LAST_ITEM(unit_labor); labor++) + { + if (labor == df::enums::unit_labor::NONE) + continue; /* - assert(labor >= 0); - assert(labor < ARRAY_COUNT(labor_infos)); - */ - - if (labor_infos[labor].is_exclusive && dwarfs[dwarf]->status.labors[labor]) - dwarf_info[dwarf].mastery_penalty -= 100; - } - } - - // Find the activity state for each dwarf. It's important to get this right - a dwarf who we think is IDLE but - // can't work will gum everything up. In the future I might add code to auto-detect slacker dwarves. - - state_count.clear(); - state_count.resize(NUM_STATE); - - for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) - { - bool is_on_break = false; - - for (auto p = dwarfs[dwarf]->status.misc_traits.begin(); p < dwarfs[dwarf]->status.misc_traits.end(); p++) - { - // 7 / 0x7 = Newly arrived migrant, will not work yet - // 17 / 0x11 = On break - if ((*p)->id == 0x07 || (*p)->id == 0x11) - is_on_break = true; - } - - if (dwarfs[dwarf]->profession == df::enums::profession::BABY || - dwarfs[dwarf]->profession == df::enums::profession::CHILD || - dwarfs[dwarf]->profession == df::enums::profession::DRUNK) - { - dwarf_info[dwarf].state = CHILD; - } - else if (ENUM_ATTR(profession, military, dwarfs[dwarf]->profession)) - dwarf_info[dwarf].state = MILITARY; - else if (dwarfs[dwarf]->job.current_job == NULL) - { - if (is_on_break) - dwarf_info[dwarf].state = OTHER; - else if (dwarfs[dwarf]->specific_refs.size() > 0) - dwarf_info[dwarf].state = OTHER; - else - dwarf_info[dwarf].state = IDLE; - } - else - { - int job = dwarfs[dwarf]->job.current_job->job_type; + assert(labor >= 0); + assert(labor < ARRAY_COUNT(labor_infos)); + */ + + if (labor_infos[labor].is_exclusive && dwarfs[dwarf]->status.labors[labor]) + dwarf_info[dwarf].mastery_penalty -= 100; + } + } + + // Find the activity state for each dwarf. It's important to get this right - a dwarf who we think is IDLE but + // can't work will gum everything up. In the future I might add code to auto-detect slacker dwarves. + + state_count.clear(); + state_count.resize(NUM_STATE); + + for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) + { + bool is_on_break = false; + + for (auto p = dwarfs[dwarf]->status.misc_traits.begin(); p < dwarfs[dwarf]->status.misc_traits.end(); p++) + { + // 7 / 0x7 = Newly arrived migrant, will not work yet + // 17 / 0x11 = On break + if ((*p)->id == 0x07 || (*p)->id == 0x11) + is_on_break = true; + } + + if (dwarfs[dwarf]->profession == df::enums::profession::BABY || + dwarfs[dwarf]->profession == df::enums::profession::CHILD || + dwarfs[dwarf]->profession == df::enums::profession::DRUNK) + { + dwarf_info[dwarf].state = CHILD; + } + else if (ENUM_ATTR(profession, military, dwarfs[dwarf]->profession)) + dwarf_info[dwarf].state = MILITARY; + else if (dwarfs[dwarf]->job.current_job == NULL) + { + if (is_on_break) + dwarf_info[dwarf].state = OTHER; + else if (dwarfs[dwarf]->specific_refs.size() > 0) + dwarf_info[dwarf].state = OTHER; + else + dwarf_info[dwarf].state = IDLE; + } + else + { + int job = dwarfs[dwarf]->job.current_job->job_type; /* - assert(job >= 0); - assert(job < ARRAY_COUNT(dwarf_states)); - */ + assert(job >= 0); + assert(job < ARRAY_COUNT(dwarf_states)); + */ if (job >= 0 && job < ARRAY_COUNT(dwarf_states)) dwarf_info[dwarf].state = dwarf_states[job]; - else + else { out.print("Dwarf %i \"%s\" has unknown job %i\n", dwarf, dwarfs[dwarf]->name.first_name.c_str(), job); dwarf_info[dwarf].state = OTHER; } - } + } - state_count[dwarf_info[dwarf].state]++; + state_count[dwarf_info[dwarf].state]++; - if (print_debug) - out.print("Dwarf %i \"%s\": penalty %i, state %s\n", dwarf, dwarfs[dwarf]->name.first_name.c_str(), dwarf_info[dwarf].mastery_penalty, state_names[dwarf_info[dwarf].state]); - } + if (print_debug) + out.print("Dwarf %i \"%s\": penalty %i, state %s\n", dwarf, dwarfs[dwarf]->name.first_name.c_str(), dwarf_info[dwarf].mastery_penalty, state_names[dwarf_info[dwarf].state]); + } - // Generate labor -> skill mapping + // Generate labor -> skill mapping - df::job_skill labor_to_skill[ENUM_LAST_ITEM(unit_labor) + 1]; - for (int i = 0; i <= ENUM_LAST_ITEM(unit_labor); i++) - labor_to_skill[i] = df::enums::job_skill::NONE; + df::job_skill labor_to_skill[ENUM_LAST_ITEM(unit_labor) + 1]; + for (int i = 0; i <= ENUM_LAST_ITEM(unit_labor); i++) + labor_to_skill[i] = df::enums::job_skill::NONE; - FOR_ENUM_ITEMS(job_skill, skill) - { - int labor = ENUM_ATTR(job_skill, labor, skill); - if (labor != df::enums::unit_labor::NONE) - { + FOR_ENUM_ITEMS(job_skill, skill) + { + int labor = ENUM_ATTR(job_skill, labor, skill); + if (labor != df::enums::unit_labor::NONE) + { /* - assert(labor >= 0); - assert(labor < ARRAY_COUNT(labor_to_skill)); - */ + assert(labor >= 0); + assert(labor < ARRAY_COUNT(labor_to_skill)); + */ - labor_to_skill[labor] = skill; - } - } + labor_to_skill[labor] = skill; + } + } - std::vector labors; + std::vector labors; - FOR_ENUM_ITEMS(unit_labor, labor) - { - if (labor == df::enums::unit_labor::NONE) - continue; + FOR_ENUM_ITEMS(unit_labor, labor) + { + if (labor == df::enums::unit_labor::NONE) + continue; /* - assert(labor >= 0); - assert(labor < ARRAY_COUNT(labor_infos)); - */ + assert(labor >= 0); + assert(labor < ARRAY_COUNT(labor_infos)); + */ - labor_infos[labor].active_dwarfs = 0; + labor_infos[labor].active_dwarfs = 0; - labors.push_back(labor); - } + labors.push_back(labor); + } laborinfo_sorter lasorter; - std::sort(labors.begin(), labors.end(), lasorter); + std::sort(labors.begin(), labors.end(), lasorter); - // Handle DISABLED skills (just bookkeeping) - for (auto lp = labors.begin(); lp != labors.end(); ++lp) - { - auto labor = *lp; + // Handle DISABLED skills (just bookkeeping) + for (auto lp = labors.begin(); lp != labors.end(); ++lp) + { + auto labor = *lp; - if (labor_infos[labor].mode() != DISABLE) - continue; + if (labor_infos[labor].mode() != DISABLE) + continue; - for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) - { - if (dwarfs[dwarf]->status.labors[labor]) - { - if (labor_infos[labor].is_exclusive) - dwarf_info[dwarf].has_exclusive_labor = true; + for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) + { + if (dwarfs[dwarf]->status.labors[labor]) + { + if (labor_infos[labor].is_exclusive) + dwarf_info[dwarf].has_exclusive_labor = true; - dwarf_info[dwarf].assigned_jobs++; - } - } - } + dwarf_info[dwarf].assigned_jobs++; + } + } + } - // Handle all skills except those marked HAULERS + // Handle all skills except those marked HAULERS - for (auto lp = labors.begin(); lp != labors.end(); ++lp) - { - auto labor = *lp; + for (auto lp = labors.begin(); lp != labors.end(); ++lp) + { + auto labor = *lp; /* - assert(labor >= 0); - assert(labor < ARRAY_COUNT(labor_infos)); - */ - - df::job_skill skill = labor_to_skill[labor]; - - if (labor_infos[labor].mode() != AUTOMATIC) - continue; - - int best_dwarf = 0; - int best_value = -10000; - - std::vector values(n_dwarfs); - std::vector candidates; - std::map dwarf_skill; - std::vector previously_enabled(n_dwarfs); - - auto mode = labor_infos[labor].mode(); - - // Find candidate dwarfs, and calculate a preference value for each dwarf - for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) - { - if (dwarf_info[dwarf].state == CHILD) - continue; - if (dwarf_info[dwarf].state == MILITARY) - continue; - - if (labor_infos[labor].is_exclusive && dwarf_info[dwarf].has_exclusive_labor) - continue; - - int value = dwarf_info[dwarf].mastery_penalty; - - if (skill != df::enums::job_skill::NONE) - { - int skill_level = 0; - int skill_experience = 0; - - for (auto s = dwarfs[dwarf]->status.souls[0]->skills.begin(); s < dwarfs[dwarf]->status.souls[0]->skills.end(); s++) - { - if ((*s)->id == skill) - { - skill_level = (*s)->rating; - skill_experience = (*s)->experience; - break; - } - } - - dwarf_skill[dwarf] = skill_level; - - value += skill_level * 100; - value += skill_experience / 20; - if (skill_level > 0 || skill_experience > 0) - value += 200; - if (skill_level >= 15) - value += 1000 * (skill_level - 14); - } - else - { - dwarf_skill[dwarf] = 0; - } - - if (dwarfs[dwarf]->status.labors[labor]) - { - value += 5; - if (labor_infos[labor].is_exclusive) - value += 350; - } - - values[dwarf] = value; - - candidates.push_back(dwarf); - - } - - // Sort candidates by preference value + assert(labor >= 0); + assert(labor < ARRAY_COUNT(labor_infos)); + */ + + df::job_skill skill = labor_to_skill[labor]; + + if (labor_infos[labor].mode() != AUTOMATIC) + continue; + + int best_dwarf = 0; + int best_value = -10000; + + std::vector values(n_dwarfs); + std::vector candidates; + std::map dwarf_skill; + std::vector previously_enabled(n_dwarfs); + + auto mode = labor_infos[labor].mode(); + + // Find candidate dwarfs, and calculate a preference value for each dwarf + for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) + { + if (dwarf_info[dwarf].state == CHILD) + continue; + if (dwarf_info[dwarf].state == MILITARY) + continue; + + if (labor_infos[labor].is_exclusive && dwarf_info[dwarf].has_exclusive_labor) + continue; + + int value = dwarf_info[dwarf].mastery_penalty; + + if (skill != df::enums::job_skill::NONE) + { + int skill_level = 0; + int skill_experience = 0; + + for (auto s = dwarfs[dwarf]->status.souls[0]->skills.begin(); s < dwarfs[dwarf]->status.souls[0]->skills.end(); s++) + { + if ((*s)->id == skill) + { + skill_level = (*s)->rating; + skill_experience = (*s)->experience; + break; + } + } + + dwarf_skill[dwarf] = skill_level; + + value += skill_level * 100; + value += skill_experience / 20; + if (skill_level > 0 || skill_experience > 0) + value += 200; + if (skill_level >= 15) + value += 1000 * (skill_level - 14); + } + else + { + dwarf_skill[dwarf] = 0; + } + + if (dwarfs[dwarf]->status.labors[labor]) + { + value += 5; + if (labor_infos[labor].is_exclusive) + value += 350; + } + + values[dwarf] = value; + + candidates.push_back(dwarf); + + } + + // Sort candidates by preference value values_sorter ivs(values); - std::sort(candidates.begin(), candidates.end(), ivs); - - // Disable the labor on everyone - for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) - { - if (dwarf_info[dwarf].state == CHILD) - continue; - - previously_enabled[dwarf] = dwarfs[dwarf]->status.labors[labor]; - dwarfs[dwarf]->status.labors[labor] = false; - } - - int min_dwarfs = labor_infos[labor].minimum_dwarfs(); - int max_dwarfs = labor_infos[labor].maximum_dwarfs(); - - // Special - don't assign hunt without a butchers, or fish without a fishery - if (df::enums::unit_labor::HUNT == labor && !has_butchers) - min_dwarfs = max_dwarfs = 0; - if (df::enums::unit_labor::FISH == labor && !has_fishery) - min_dwarfs = max_dwarfs = 0; - - bool want_idle_dwarf = true; - if (state_count[IDLE] < 2) - want_idle_dwarf = false; - - /* - * Assign dwarfs to this labor. We assign at least the minimum number of dwarfs, in - * order of preference, and then assign additional dwarfs that meet any of these conditions: - * - The dwarf is idle and there are no idle dwarves assigned to this labor - * - The dwarf has nonzero skill associated with the labor - * - The labor is mining, hunting, or woodcutting and the dwarf currently has it enabled. - * We stop assigning dwarfs when we reach the maximum allowed. - * Note that only idle and busy dwarfs count towards the number of dwarfs. "Other" dwarfs - * (sleeping, eating, on break, etc.) will have labors assigned, but will not be counted. - * Military and children/nobles will not have labors assigned. + std::sort(candidates.begin(), candidates.end(), ivs); + + // Disable the labor on everyone + for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) + { + if (dwarf_info[dwarf].state == CHILD) + continue; + + previously_enabled[dwarf] = dwarfs[dwarf]->status.labors[labor]; + dwarfs[dwarf]->status.labors[labor] = false; + } + + int min_dwarfs = labor_infos[labor].minimum_dwarfs(); + int max_dwarfs = labor_infos[labor].maximum_dwarfs(); + + // Special - don't assign hunt without a butchers, or fish without a fishery + if (df::enums::unit_labor::HUNT == labor && !has_butchers) + min_dwarfs = max_dwarfs = 0; + if (df::enums::unit_labor::FISH == labor && !has_fishery) + min_dwarfs = max_dwarfs = 0; + + bool want_idle_dwarf = true; + if (state_count[IDLE] < 2) + want_idle_dwarf = false; + + /* + * Assign dwarfs to this labor. We assign at least the minimum number of dwarfs, in + * order of preference, and then assign additional dwarfs that meet any of these conditions: + * - The dwarf is idle and there are no idle dwarves assigned to this labor + * - The dwarf has nonzero skill associated with the labor + * - The labor is mining, hunting, or woodcutting and the dwarf currently has it enabled. + * We stop assigning dwarfs when we reach the maximum allowed. + * Note that only idle and busy dwarfs count towards the number of dwarfs. "Other" dwarfs + * (sleeping, eating, on break, etc.) will have labors assigned, but will not be counted. + * Military and children/nobles will not have labors assigned. * Dwarfs with the "health management" responsibility are always assigned DIAGNOSIS. - */ - for (int i = 0; i < candidates.size() && labor_infos[labor].active_dwarfs < max_dwarfs; i++) - { - int dwarf = candidates[i]; - - assert(dwarf >= 0); - assert(dwarf < n_dwarfs); - - bool preferred_dwarf = false; - if (want_idle_dwarf && dwarf_info[dwarf].state == IDLE) - preferred_dwarf = true; - if (dwarf_skill[dwarf] > 0) - preferred_dwarf = true; - if (previously_enabled[dwarf] && labor_infos[labor].is_exclusive) - preferred_dwarf = true; + */ + for (int i = 0; i < candidates.size() && labor_infos[labor].active_dwarfs < max_dwarfs; i++) + { + int dwarf = candidates[i]; + + assert(dwarf >= 0); + assert(dwarf < n_dwarfs); + + bool preferred_dwarf = false; + if (want_idle_dwarf && dwarf_info[dwarf].state == IDLE) + preferred_dwarf = true; + if (dwarf_skill[dwarf] > 0) + preferred_dwarf = true; + if (previously_enabled[dwarf] && labor_infos[labor].is_exclusive) + preferred_dwarf = true; if (dwarf_info[dwarf].medical && labor == df::unit_labor::DIAGNOSE) preferred_dwarf = true; if (dwarf_info[dwarf].trader && trader_requested) continue; - if (labor_infos[labor].active_dwarfs >= min_dwarfs && !preferred_dwarf) - continue; + if (labor_infos[labor].active_dwarfs >= min_dwarfs && !preferred_dwarf) + continue; - if (!dwarfs[dwarf]->status.labors[labor]) - dwarf_info[dwarf].assigned_jobs++; + if (!dwarfs[dwarf]->status.labors[labor]) + dwarf_info[dwarf].assigned_jobs++; - dwarfs[dwarf]->status.labors[labor] = true; + dwarfs[dwarf]->status.labors[labor] = true; - if (labor_infos[labor].is_exclusive) - { - dwarf_info[dwarf].has_exclusive_labor = true; - // all the exclusive labors require equipment so this should force the dorf to reequip if needed - dwarfs[dwarf]->military.pickup_flags.bits.update = 1; - } + if (labor_infos[labor].is_exclusive) + { + dwarf_info[dwarf].has_exclusive_labor = true; + // all the exclusive labors require equipment so this should force the dorf to reequip if needed + dwarfs[dwarf]->military.pickup_flags.bits.update = 1; + } - if (print_debug) - out.print("Dwarf %i \"%s\" assigned %s: value %i\n", dwarf, dwarfs[dwarf]->name.first_name.c_str(), ENUM_KEY_STR(unit_labor, labor).c_str(), values[dwarf]); + if (print_debug) + out.print("Dwarf %i \"%s\" assigned %s: value %i\n", dwarf, dwarfs[dwarf]->name.first_name.c_str(), ENUM_KEY_STR(unit_labor, labor).c_str(), values[dwarf]); - if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY) - labor_infos[labor].active_dwarfs++; + if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY) + labor_infos[labor].active_dwarfs++; - if (dwarf_info[dwarf].state == IDLE) - want_idle_dwarf = false; - } - } + if (dwarf_info[dwarf].state == IDLE) + want_idle_dwarf = false; + } + } - // Set about 1/3 of the dwarfs as haulers. The haulers have all HAULER labors enabled. Having a lot of haulers helps - // make sure that hauling jobs are handled quickly rather than building up. + // Set about 1/3 of the dwarfs as haulers. The haulers have all HAULER labors enabled. Having a lot of haulers helps + // make sure that hauling jobs are handled quickly rather than building up. - int num_haulers = state_count[IDLE] + state_count[BUSY] / 3; - if (num_haulers < 1) - num_haulers = 1; + int num_haulers = state_count[IDLE] + state_count[BUSY] / 3; + if (num_haulers < 1) + num_haulers = 1; - std::vector hauler_ids; - for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) - { + std::vector hauler_ids; + for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) + { if (dwarf_info[dwarf].trader && trader_requested) continue; - if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY) - hauler_ids.push_back(dwarf); - } + if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY) + hauler_ids.push_back(dwarf); + } dwarfinfo_sorter sorter(dwarf_info); - // Idle dwarves come first, then we sort from least-skilled to most-skilled. - std::sort(hauler_ids.begin(), hauler_ids.end(), sorter); + // Idle dwarves come first, then we sort from least-skilled to most-skilled. + std::sort(hauler_ids.begin(), hauler_ids.end(), sorter); - // don't set any haulers if everyone is off drinking or something - if (hauler_ids.size() == 0) { - num_haulers = 0; - } + // don't set any haulers if everyone is off drinking or something + if (hauler_ids.size() == 0) { + num_haulers = 0; + } - FOR_ENUM_ITEMS(unit_labor, labor) - { - if (labor == df::enums::unit_labor::NONE) - continue; + FOR_ENUM_ITEMS(unit_labor, labor) + { + if (labor == df::enums::unit_labor::NONE) + continue; /* - assert(labor >= 0); - assert(labor < ARRAY_COUNT(labor_infos)); - */ + assert(labor >= 0); + assert(labor < ARRAY_COUNT(labor_infos)); + */ - if (labor_infos[labor].mode() != HAULERS) - continue; + if (labor_infos[labor].mode() != HAULERS) + continue; + + for (int i = 0; i < num_haulers; i++) + { + assert(i < hauler_ids.size()); - for (int i = 0; i < num_haulers; i++) - { - assert(i < hauler_ids.size()); + int dwarf = hauler_ids[i]; - int dwarf = hauler_ids[i]; + assert(dwarf >= 0); + assert(dwarf < n_dwarfs); + dwarfs[dwarf]->status.labors[labor] = true; + dwarf_info[dwarf].assigned_jobs++; - assert(dwarf >= 0); - assert(dwarf < n_dwarfs); - dwarfs[dwarf]->status.labors[labor] = true; - dwarf_info[dwarf].assigned_jobs++; + if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY) + labor_infos[labor].active_dwarfs++; - if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY) - labor_infos[labor].active_dwarfs++; + if (print_debug) + out.print("Dwarf %i \"%s\" assigned %s: hauler\n", dwarf, dwarfs[dwarf]->name.first_name.c_str(), ENUM_KEY_STR(unit_labor, labor).c_str()); + } - if (print_debug) - out.print("Dwarf %i \"%s\" assigned %s: hauler\n", dwarf, dwarfs[dwarf]->name.first_name.c_str(), ENUM_KEY_STR(unit_labor, labor).c_str()); - } + for (int i = num_haulers; i < hauler_ids.size(); i++) + { + assert(i < hauler_ids.size()); - for (int i = num_haulers; i < hauler_ids.size(); i++) - { - assert(i < hauler_ids.size()); + int dwarf = hauler_ids[i]; - int dwarf = hauler_ids[i]; - - assert(dwarf >= 0); - assert(dwarf < n_dwarfs); + assert(dwarf >= 0); + assert(dwarf < n_dwarfs); - dwarfs[dwarf]->status.labors[labor] = false; - } - } + dwarfs[dwarf]->status.labors[labor] = false; + } + } - print_debug = 0; + print_debug = 0; return CR_OK; } void print_labor (df::enums::unit_labor::unit_labor labor, color_ostream &out) { - string labor_name = ENUM_KEY_STR(unit_labor, labor); - out << labor_name << ": "; - for (int i = 0; i < 20 - (int)labor_name.length(); i++) - out << ' '; - if (labor_infos[labor].mode() == DISABLE) - out << "disabled" << endl; - else - { - if (labor_infos[labor].mode() == HAULERS) - out << "haulers"; - else - out << "minimum " << labor_infos[labor].minimum_dwarfs() << ", maximum " << labor_infos[labor].maximum_dwarfs(); - out << ", currently " << labor_infos[labor].active_dwarfs << " dwarfs" << endl; - } + string labor_name = ENUM_KEY_STR(unit_labor, labor); + out << labor_name << ": "; + for (int i = 0; i < 20 - (int)labor_name.length(); i++) + out << ' '; + if (labor_infos[labor].mode() == DISABLE) + out << "disabled" << endl; + else + { + if (labor_infos[labor].mode() == HAULERS) + out << "haulers"; + else + out << "minimum " << labor_infos[labor].minimum_dwarfs() << ", maximum " << labor_infos[labor].maximum_dwarfs(); + out << ", currently " << labor_infos[labor].active_dwarfs << " dwarfs" << endl; + } } command_result autolabor (color_ostream &out, std::vector & parameters) @@ -1231,9 +1231,9 @@ command_result autolabor (color_ostream &out, std::vector & parame return CR_FAILURE; } - if (parameters.size() == 1 && - (parameters[0] == "0" || parameters[0] == "enable" || - parameters[0] == "1" || parameters[0] == "disable")) + if (parameters.size() == 1 && + (parameters[0] == "0" || parameters[0] == "enable" || + parameters[0] == "1" || parameters[0] == "disable")) { bool enable = (parameters[0] == "1" || parameters[0] == "enable"); if (enable && !enable_autolabor) @@ -1252,118 +1252,130 @@ command_result autolabor (color_ostream &out, std::vector & parame return CR_OK; } - return CR_OK; + return CR_OK; } - - if (!enable_autolabor) - { - out << "Error: The plugin is not enabled." << endl; - return CR_FAILURE; - } - - if (parameters.size() == 2 || parameters.size() == 3) { - df::enums::unit_labor::unit_labor labor = df::enums::unit_labor::NONE; - - FOR_ENUM_ITEMS(unit_labor, test_labor) - { - if (parameters[0] == ENUM_KEY_STR(unit_labor, test_labor)) - labor = test_labor; - } - - if (labor == df::enums::unit_labor::NONE) - { - out.printerr("Could not find labor %s.\n", parameters[0].c_str()); - return CR_WRONG_USAGE; - } - - if (parameters[1] == "haulers") - { - labor_infos[labor].set_mode(HAULERS); - print_labor(labor, out); - return CR_OK; - } - if (parameters[1] == "disable") - { - labor_infos[labor].set_mode(DISABLE); - print_labor(labor, out); - return CR_OK; - } - if (parameters[1] == "reset") - { - reset_labor(labor); - print_labor(labor, out); - return CR_OK; - } - - int minimum = atoi (parameters[1].c_str()); - int maximum = 200; - if (parameters.size() == 3) - maximum = atoi (parameters[2].c_str()); - - if (maximum < minimum || maximum < 0 || minimum < 0) - { - out.printerr("Syntax: autolabor []\n", maximum, minimum); - return CR_WRONG_USAGE; - } - - labor_infos[labor].set_minimum_dwarfs(minimum); - labor_infos[labor].set_maximum_dwarfs(maximum); - labor_infos[labor].set_mode(AUTOMATIC); - print_labor(labor, out); - - return CR_OK; - } - else if (parameters.size() == 1 && parameters[0] == "reset-all") { - for (int i = 0; i < labor_infos.size(); i++) - { - reset_labor((df::enums::unit_labor::unit_labor) i); - } - out << "All labors reset." << endl; - return CR_OK; - } - else if (parameters.size() == 1 && parameters[0] == "list" || parameters[0] == "status") { - if (!enable_autolabor) - { - out << "autolabor not activated." << endl; - return CR_OK; - } - - bool need_comma = 0; - for (int i = 0; i < NUM_STATE; i++) - { - if (state_count[i] == 0) - continue; - if (need_comma) - out << ", "; - out << state_count[i] << ' ' << state_names[i]; - need_comma = 1; - } - out << endl; - - if (parameters[0] == "list") - { - FOR_ENUM_ITEMS(unit_labor, labor) - { - if (labor == df::enums::unit_labor::NONE) - continue; - - print_labor(labor, out); - } - } - - return CR_OK; - } - else if (parameters.size() == 1 && parameters[0] == "debug") { - print_debug = 1; - - return CR_OK; - } - else + + if (parameters.size() == 2 || parameters.size() == 3) { + if (!enable_autolabor) + { + out << "Error: The plugin is not enabled." << endl; + return CR_FAILURE; + } + + df::enums::unit_labor::unit_labor labor = df::enums::unit_labor::NONE; + + FOR_ENUM_ITEMS(unit_labor, test_labor) + { + if (parameters[0] == ENUM_KEY_STR(unit_labor, test_labor)) + labor = test_labor; + } + + if (labor == df::enums::unit_labor::NONE) + { + out.printerr("Could not find labor %s.\n", parameters[0].c_str()); + return CR_WRONG_USAGE; + } + + if (parameters[1] == "haulers") + { + labor_infos[labor].set_mode(HAULERS); + print_labor(labor, out); + return CR_OK; + } + if (parameters[1] == "disable") + { + labor_infos[labor].set_mode(DISABLE); + print_labor(labor, out); + return CR_OK; + } + if (parameters[1] == "reset") + { + reset_labor(labor); + print_labor(labor, out); + return CR_OK; + } + + int minimum = atoi (parameters[1].c_str()); + int maximum = 200; + if (parameters.size() == 3) + maximum = atoi (parameters[2].c_str()); + + if (maximum < minimum || maximum < 0 || minimum < 0) + { + out.printerr("Syntax: autolabor []\n", maximum, minimum); + return CR_WRONG_USAGE; + } + + labor_infos[labor].set_minimum_dwarfs(minimum); + labor_infos[labor].set_maximum_dwarfs(maximum); + labor_infos[labor].set_mode(AUTOMATIC); + print_labor(labor, out); + + return CR_OK; + } + else if (parameters.size() == 1 && parameters[0] == "reset-all") { + if (!enable_autolabor) + { + out << "Error: The plugin is not enabled." << endl; + return CR_FAILURE; + } + + for (int i = 0; i < labor_infos.size(); i++) + { + reset_labor((df::enums::unit_labor::unit_labor) i); + } + out << "All labors reset." << endl; + return CR_OK; + } + else if (parameters.size() == 1 && parameters[0] == "list" || parameters[0] == "status") { + if (!enable_autolabor) + { + out << "Error: The plugin is not enabled." << endl; + return CR_FAILURE; + } + + bool need_comma = 0; + for (int i = 0; i < NUM_STATE; i++) + { + if (state_count[i] == 0) + continue; + if (need_comma) + out << ", "; + out << state_count[i] << ' ' << state_names[i]; + need_comma = 1; + } + out << endl; + + if (parameters[0] == "list") + { + FOR_ENUM_ITEMS(unit_labor, labor) + { + if (labor == df::enums::unit_labor::NONE) + continue; + + print_labor(labor, out); + } + } + + return CR_OK; + } + else if (parameters.size() == 1 && parameters[0] == "debug") { + if (!enable_autolabor) + { + out << "Error: The plugin is not enabled." << endl; + return CR_FAILURE; + } + + print_debug = 1; + + return CR_OK; + } + else { out.print("Automatically assigns labors to dwarves.\n" "Activate with 'autolabor 1', deactivate with 'autolabor 0'.\n" "Current state: %d.\n", enable_autolabor); - return CR_OK; - } + return CR_OK; + } } From bf82b2d20dbb135dc81a877feb18cf456254b70b Mon Sep 17 00:00:00 2001 From: Quietust Date: Tue, 17 Jul 2012 10:35:20 -0500 Subject: [PATCH 03/92] Sync with df-structures --- library/modules/Buildings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index e0afc56ed..8ec60e55b 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -991,7 +991,7 @@ bool Buildings::deconstruct(df::building *bld) // Assume: no parties. unlinkRooms(bld); // Assume: not unit destroy target - vector_erase_at(ui->unk8.unk10, linear_index(ui->unk8.unk10, bld)); + vector_erase_at(ui->tax_collection.rooms, linear_index(ui->tax_collection.rooms, bld)); // Assume: not used in punishment // Assume: not used in non-own jobs // Assume: does not affect pathfinding From 9f53f6296d77db6dace5a9d429b621513b0fe106 Mon Sep 17 00:00:00 2001 From: Warmist Date: Wed, 18 Jul 2012 21:07:27 +0300 Subject: [PATCH 04/92] Removed Console module from dfusion. --- plugins/Dfusion/dfusion.cpp | 3 - plugins/Dfusion/include/lua_Console.h | 13 --- plugins/Dfusion/luafiles/common.lua | 2 +- plugins/Dfusion/luafiles/editor.lua | 2 +- plugins/Dfusion/luafiles/init.lua | 10 +- plugins/Dfusion/src/lua_Console.cpp | 131 -------------------------- 6 files changed, 5 insertions(+), 156 deletions(-) delete mode 100644 plugins/Dfusion/include/lua_Console.h delete mode 100644 plugins/Dfusion/src/lua_Console.cpp diff --git a/plugins/Dfusion/dfusion.cpp b/plugins/Dfusion/dfusion.cpp index 2b36a9747..78c3fa8d1 100644 --- a/plugins/Dfusion/dfusion.cpp +++ b/plugins/Dfusion/dfusion.cpp @@ -1,5 +1,4 @@ #include "Core.h" -#include "Console.h" #include "Export.h" #include "PluginManager.h" #include "MemAccess.h" @@ -12,7 +11,6 @@ #include "luamain.h" -#include "lua_Console.h" #include "lua_Process.h" #include "lua_Hexsearch.h" #include "lua_Misc.h" @@ -50,7 +48,6 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector -#include "luamain.h" - -namespace lua -{ - -void RegisterConsole(lua::state &st); - -} - -#endif \ No newline at end of file diff --git a/plugins/Dfusion/luafiles/common.lua b/plugins/Dfusion/luafiles/common.lua index 752d07cf6..7e621c4ea 100644 --- a/plugins/Dfusion/luafiles/common.lua +++ b/plugins/Dfusion/luafiles/common.lua @@ -8,7 +8,7 @@ DOUBLE=5 FLOAT=6 getline=function (inp) -return Console.lineedit(inp or "") +return dfhack.lineedit(inp or "") end io.stdin=nil diff --git a/plugins/Dfusion/luafiles/editor.lua b/plugins/Dfusion/luafiles/editor.lua index 1f004b2d5..06b07ce5e 100644 --- a/plugins/Dfusion/luafiles/editor.lua +++ b/plugins/Dfusion/luafiles/editor.lua @@ -127,7 +127,7 @@ function EditDF() tbl[i]={k,getTypename(v)} i=i+1 end - number=Console.lineedit("select item to edit (q to quit):") + number=dfhack.lineedit("select item to edit (q to quit):") if number and tonumber(number) then local entry=tbl[tonumber(number)] if entry==nil then diff --git a/plugins/Dfusion/luafiles/init.lua b/plugins/Dfusion/luafiles/init.lua index 6fa86d7ac..19f63d603 100644 --- a/plugins/Dfusion/luafiles/init.lua +++ b/plugins/Dfusion/luafiles/init.lua @@ -1,7 +1,3 @@ -Console.print = dfhack.print -Console.println = dfhack.println -Console.printerr = dfhack.printerr - function err(msg) --make local maybe... print(msg) print(debug.traceback()) @@ -30,13 +26,13 @@ function loadall(t1) --loads all non interactive plugin parts, so that later the end end function mainmenu(t1) - --Console.clear() + while true do print("No. Name Desc") for k,v in pairs(t1) do print(string.format("%3d %15s %s",k,v[1],v[2])) end - local q=Console.lineedit("Select plugin to run (q to quit):") + local q=dfhack.lineedit("Select plugin to run (q to quit):") if q=='q' then return end q=tonumber(q) if q~=nil then @@ -92,7 +88,7 @@ local f,err=load(table.concat(args,' ')) if f then f() else - Console.printerr(err) + dfhack.printerr(err) end if not INIT then diff --git a/plugins/Dfusion/src/lua_Console.cpp b/plugins/Dfusion/src/lua_Console.cpp deleted file mode 100644 index 1d52d6158..000000000 --- a/plugins/Dfusion/src/lua_Console.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "LuaTools.h" - -#include "lua_Console.h" - -#include - -//TODO error management. Using lua error? or something other? -static DFHack::color_ostream* GetConsolePtr(lua::state &st) -{ - return DFHack::Lua::GetOutput(st); -} - -static int lua_Console_clear(lua_State *S) -{ - lua::state st(S); - DFHack::color_ostream* c=GetConsolePtr(st); - c->clear(); - return 0; -} -static int lua_Console_gotoxy(lua_State *S) -{ - lua::state st(S); - DFHack::color_ostream* c=GetConsolePtr(st); - if(c->is_console()) - { - DFHack::Console* con=static_cast(c); - con->gotoxy(st.as(1,1),st.as(1,2)); - } - return 0; -} -static int lua_Console_color(lua_State *S) -{ - lua::state st(S); - DFHack::color_ostream* c=GetConsolePtr(st); - c->color( static_cast(st.as(-1,1)) ); - return 0; -} -static int lua_Console_reset_color(lua_State *S) -{ - lua::state st(S); - DFHack::color_ostream* c=GetConsolePtr(st); - c->reset_color(); - return 0; -} -static int lua_Console_cursor(lua_State *S) -{ - lua::state st(S); - DFHack::color_ostream* c=GetConsolePtr(st); - if(c->is_console()) - { - DFHack::Console* con=static_cast(c); - con->cursor(st.as(1)); - } - return 0; -} -static int lua_Console_msleep(lua_State *S) -{ - lua::state st(S); - DFHack::color_ostream* c=GetConsolePtr(st); - if(c->is_console()) - { - DFHack::Console* con=static_cast(c); - con->msleep(st.as(1)); - } - return 0; -} -static int lua_Console_get_columns(lua_State *S) -{ - lua::state st(S); - DFHack::color_ostream* c=GetConsolePtr(st); - if(c->is_console()) - { - DFHack::Console* con=static_cast(c); - st.push(con->get_columns()); - } - return 1; -} -static int lua_Console_get_rows(lua_State *S) -{ - lua::state st(S); - DFHack::color_ostream* c=GetConsolePtr(st); - if(c->is_console()) - { - DFHack::Console* con=static_cast(c); - st.push(con->get_rows()); - } - return 1; -} -static int lua_Console_lineedit(lua_State *S) -{ - lua::state st(S); - DFHack::color_ostream* c=GetConsolePtr(st); - if(c->is_console()) - { - DFHack::Console* con=static_cast(c); - string ret; - DFHack::CommandHistory hist; - int i=con->lineedit(st.as(1),ret,hist); - st.push(ret); - st.push(i); - return 2;// dunno if len is needed... - } - else - return 0; -} -const luaL_Reg lua_console_func[]= -{ - {"clear",lua_Console_clear}, - {"gotoxy",lua_Console_gotoxy}, - {"color",lua_Console_color}, - {"reset_color",lua_Console_reset_color}, - {"cursor",lua_Console_cursor}, - {"msleep",lua_Console_msleep}, - {"get_columns",lua_Console_get_columns}, - {"get_rows",lua_Console_get_rows}, - {"lineedit",lua_Console_lineedit}, - {NULL,NULL} -}; -void lua::RegisterConsole(lua::state &st) -{ - st.getglobal("Console"); - if(st.is()) - { - st.pop(); - st.newtable(); - } - - lua::RegFunctionsLocal(st, lua_console_func); - //TODO add color consts - st.setglobal("Console"); -} From d8cb6e2c4edc62c2c1080103c8191f1cd6bc0ce5 Mon Sep 17 00:00:00 2001 From: Donald Ruegsegger Date: Wed, 1 Aug 2012 19:52:50 -0500 Subject: [PATCH 05/92] Fixed seedwatch not working on reclaim Just needed to add a check for game_type::DWARF_RECLAIM in two places --- plugins/seedwatch.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/seedwatch.cpp b/plugins/seedwatch.cpp index ae9ff0e14..676c02ed5 100755 --- a/plugins/seedwatch.cpp +++ b/plugins/seedwatch.cpp @@ -111,7 +111,8 @@ command_result df_seedwatch(color_ostream &out, vector& parameters) w->ReadGameMode(gm);// FIXME: check return value // if game mode isn't fortress mode - if(gm.g_mode != game_mode::DWARF || gm.g_type != game_type::DWARF_MAIN) + if(gm.g_mode != game_mode::DWARF || + !(gm.g_type == game_type::DWARF_MAIN || gm.g_type == game_type::DWARF_RECLAIM)) { // just print the help printHelp(out); @@ -299,7 +300,8 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out) t_gamemodes gm; w->ReadGameMode(gm);// FIXME: check return value // if game mode isn't fortress mode - if(gm.g_mode != game_mode::DWARF || gm.g_type != game_type::DWARF_MAIN) + if(gm.g_mode != game_mode::DWARF || + !(gm.g_type == game_type::DWARF_MAIN || gm.g_type == game_type::DWARF_RECLAIM)) { // stop running. running = false; From 589be93fbfc4710779720a6df997c6233d0b93d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Thu, 2 Aug 2012 14:44:59 +0200 Subject: [PATCH 06/92] Sync with structures --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index b646637f8..9f91e7476 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit b646637f8eb901a95c82e60ccd4713e763e00179 +Subproject commit 9f91e74767b4d583b580d46e16143216ba62ae66 From 61195859e3544e17cd702676e6b7b8015684622a Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 3 Aug 2012 03:28:29 +0200 Subject: [PATCH 07/92] fix autolabor compilation error --- plugins/autolabor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 7dbdedeb6..512899d75 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -96,7 +96,7 @@ command_result autolabor (color_ostream &out, std::vector & parame // The name string provided must correspond to the filename - autolabor.plug.so or autolabor.plug.dll in this case DFHACK_PLUGIN("autolabor"); -void generate_labor_to_skill_map(); +static void generate_labor_to_skill_map(); enum labor_mode { DISABLE, From 160487f7eb7fcc019644102f595a73e334f463d7 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 3 Aug 2012 14:52:39 +0200 Subject: [PATCH 08/92] ruby: move all generated/downloaded stuff in the build/ directory --- .gitignore | 6 ------ plugins/ruby/CMakeLists.txt | 24 +++++++++++++----------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 85fc083e7..bdb9474bb 100644 --- a/.gitignore +++ b/.gitignore @@ -50,12 +50,6 @@ dfhack/python/PyDFHack.egg-info dfhack/python/build dfhack/python/dist -# Ruby binding binaries -plugins/ruby/libruby* -plugins/ruby/msvcrtruby*.tar.gz -plugins/ruby/ruby-autogen.rb -plugins/ruby/ruby-autogen.rb.rule - # CPack stuff build/CPack*Config.cmake diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index a9a85636c..0d9f1187d 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -1,29 +1,29 @@ OPTION(DL_RUBY "download libruby from the internet" ON) IF (DL_RUBY AND NOT APPLE) IF (UNIX) - FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/libruby187.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/libruby187.tar.gz + FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/libruby187.tar.gz ${CMAKE_CURRENT_BINARY_DIR}/libruby187.tar.gz EXPECTED_MD5 eb2adea59911f68e6066966c1352f291) EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar xzf libruby187.tar.gz - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - FILE(RENAME libruby1.8.so.1.8.7 libruby.so) - SET(RUBYLIB libruby.so) + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + FILE(RENAME ${CMAKE_CURRENT_BINARY_DIR}/libruby1.8.so.1.8.7 ${CMAKE_CURRENT_BINARY_DIR}/libruby.so) + SET(RUBYLIB ${CMAKE_CURRENT_BINARY_DIR}/libruby.so) ELSE (UNIX) - FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/msvcrtruby187.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/msvcrtruby187.tar.gz + FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/msvcrtruby187.tar.gz ${CMAKE_CURRENT_BINARY_DIR}/msvcrtruby187.tar.gz EXPECTED_MD5 9f4a1659ac3a5308f32d3a1937bbeeae) EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar xzf msvcrtruby187.tar.gz - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - FILE(RENAME msvcrt-ruby18.dll libruby.dll) - SET(RUBYLIB libruby.dll) + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + FILE(RENAME ${CMAKE_CURRENT_BINARY_DIR}/msvcrt-ruby18.dll ${CMAKE_CURRENT_BINARY_DIR}/libruby.dll) + SET(RUBYLIB ${CMAKE_CURRENT_BINARY_DIR}/libruby.dll) ENDIF(UNIX) ENDIF(DL_RUBY AND NOT APPLE) ADD_CUSTOM_COMMAND( - OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb - COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb + OUTPUT ruby-autogen.rb + COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ruby-autogen.rb DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl COMMENT ruby-autogen.rb ) -ADD_CUSTOM_TARGET(ruby-autogen-rb DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb) +ADD_CUSTOM_TARGET(ruby-autogen-rb DEPENDS ruby-autogen.rb) INCLUDE_DIRECTORIES("${dfhack_SOURCE_DIR}/depends/tthread") @@ -32,6 +32,8 @@ ADD_DEPENDENCIES(ruby ruby-autogen-rb) INSTALL(FILES ${RUBYLIB} DESTINATION ${DFHACK_LIBRARY_DESTINATION}) +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.rb DESTINATION hack/ruby) + INSTALL(DIRECTORY . DESTINATION hack/ruby FILES_MATCHING PATTERN "*.rb") From 81ea0345ad8f9347ce6ef14bdd779ee1bef438a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Fri, 3 Aug 2012 16:09:51 +0200 Subject: [PATCH 09/92] Fix cage-related bug in the sort plugin. --- depends/clsocket | 2 +- plugins/sort.cpp | 7 ++++++- plugins/stonesense | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/depends/clsocket b/depends/clsocket index c85e9fb35..d0b2d0750 160000 --- a/depends/clsocket +++ b/depends/clsocket @@ -1 +1 @@ -Subproject commit c85e9fb35d3510c5dcc367056cda3237d77a7add +Subproject commit d0b2d0750dc2d529a152eba4f3f519f69ff7eab0 diff --git a/plugins/sort.cpp b/plugins/sort.cpp index 5748a065b..ff51fc773 100644 --- a/plugins/sort.cpp +++ b/plugins/sort.cpp @@ -431,12 +431,17 @@ DEFINE_SORT_HANDLER(unit_sorters, dwarfmode, "/QueryBuilding/Some/Assign", scree sort_null_first(parameters); PARSE_SPEC("units", parameters); - if (compute_order(*pout, L, top, &order, *ui_building_assign_units)) { reorder_cursor(ui_building_item_cursor, order); reorder_vector(ui_building_assign_type, order); reorder_vector(ui_building_assign_units, order); + // this is for cages. cages need some extra sorting + if(ui_building_assign_items->size() == ui_building_assign_units->size()) + { + reorder_vector(ui_building_assign_items, order); + reorder_vector(ui_building_assign_is_marked, order); + } } } diff --git a/plugins/stonesense b/plugins/stonesense index 17b653665..5d4f06d78 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit 17b653665567a5f1df628217820f76bb0b9c70a5 +Subproject commit 5d4f06d785f8a9933679fe3caa12c18215e9674d From f400591080b29add7b64d196b4bf3911c2803ded Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 3 Aug 2012 16:53:54 +0200 Subject: [PATCH 10/92] ruby: use enums in single-bit values too --- plugins/ruby/codegen.pl | 6 +++--- plugins/ruby/ruby-autogen-defs.rb | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index cbe7c932a..593216d71 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -175,10 +175,10 @@ sub render_bitfield_fields { if ($name) { - if ($count == 1) { - push @lines_rb, "field(:$name, 0) { bit $shift }"; - } elsif ($enum) { + if ($enum) { push @lines_rb, "field(:$name, 0) { bits $shift, $count, $enum }"; + } elsif ($count == 1) { + push @lines_rb, "field(:$name, 0) { bit $shift }"; } else { push @lines_rb, "field(:$name, 0) { bits $shift, $count }"; } diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index a1cba4168..2e4948acb 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -34,8 +34,8 @@ module DFHack def float Float.new end - def bit(shift) - BitField.new(shift, 1) + def bit(shift, enum=nil) + BitField.new(shift, 1, enum) end def bits(shift, len, enum=nil) BitField.new(shift, len, enum) @@ -147,7 +147,7 @@ module DFHack out << '>' end def inspect_field(n, o, s) - if s.kind_of?(BitField) and s._len == 1 + if s.kind_of?(BitField) and s._len == 1 and not s._enum send(n) ? n.to_s : '' elsif s.kind_of?(Pointer) "#{n}=#{s._at(@_memaddr+o).inspect}" @@ -242,7 +242,7 @@ module DFHack def _get v = DFHack.memory_read_int32(@_memaddr) >> @_shift - if @_len == 1 + if @_len == 1 and not @_enum ((v & 1) == 0) ? false : true else v &= _mask @@ -252,7 +252,7 @@ module DFHack end def _set(v) - if @_len == 1 + if @_len == 1 and (not @_enum or v == false or v == true) # allow 'bit = 0' v = (v && v != 0 ? 1 : 0) end From c1bcd270e9488f668f0c38c4d8ac736f5368ee00 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 3 Aug 2012 16:54:40 +0200 Subject: [PATCH 11/92] ruby: add spawn_water/spawn_magma methods to MapTile --- plugins/ruby/map.rb | 48 ++++++++++++++++++++++++++++++++++++++++++ scripts/magmasource.rb | 16 +++----------- 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/plugins/ruby/map.rb b/plugins/ruby/map.rb index a0438670d..c99d5b88d 100644 --- a/plugins/ruby/map.rb +++ b/plugins/ruby/map.rb @@ -107,6 +107,31 @@ module DFHack Tiletype::Direction[tiletype] end + def shape_caption + TiletypeShape::Caption[shape] + end + + def shape_basic + TiletypeShape::BasicShape[shape] + end + + def shape_passablelow + TiletypeShape::PassableLow[shape] + end + + def shape_passablehigh + TiletypeShape::PassableHigh[shape] + end + + def shape_passableflow + TiletypeShape::PassableFlow[shape] + end + + def shape_walkable + TiletypeShape::Walkable[shape] + end + + # return all veins for current mapblock def all_veins mapblock.block_events.grep(BlockSquareEventMineralst) @@ -162,5 +187,28 @@ module DFHack def inspect "#" end + + def spawn_liquid(quantity, is_magma=false, flowing=true) + designation.flow_size = quantity + designation.liquid_type = (is_magma ? :Magma : :Water) + designation.flow_forbid = true if is_magma or quantity >= 4 + + if flowing + mapblock.flags.update_liquid = true + mapblock.flags.update_liquid_twice = true + + zf = df.world.map.z_level_flags[z] + zf.update = true + zf.update_twice = true + end + end + + def spawn_water(quantity=7) + spawn_liquid(quantity) + end + + def spawn_magma(quantity=7) + spawn_liquid(quantity, true) + end end end diff --git a/scripts/magmasource.rb b/scripts/magmasource.rb index 8525d51e0..e97080834 100644 --- a/scripts/magmasource.rb +++ b/scripts/magmasource.rb @@ -12,26 +12,16 @@ when 'here' end $magma_sources.each { |x, y, z| - if tile = df.map_tile_at(x, y, z) and DFHack::TiletypeShape::PassableFlow[tile.shape] + if tile = df.map_tile_at(x, y, z) and tile.shape_passableflow des = tile.designation - des.flow_size += 1 if des.flow_size < 7 - des.liquid_type = 1 - des.flow_forbid = true - - mf = tile.mapblock.flags - mf.update_liquid = true - mf.update_liquid_twice = true - - zf = df.world.map.z_level_flags[z] - zf.update = true - zf.update_twice = true + tile.spawn_magma(des.flow_size + 1) if des.flow_size < 7 end } } if df.cursor.x != -30000 if tile = df.map_tile_at(df.cursor) - if DFHack::TiletypeShape::PassableFlow[tile.shape] + if tile.shape_passableflow $magma_sources << [df.cursor.x, df.cursor.y, df.cursor.z] else puts "Impassable tile: I'm afraid I can't do that, Dave" From 64a8443b5a3c467b3855ebdb2dd291ede5ca91f6 Mon Sep 17 00:00:00 2001 From: jj Date: Fri, 3 Aug 2012 16:59:54 +0200 Subject: [PATCH 12/92] slayrace: add single-creature targetting, add magma column mode --- README.rst | 24 ++++++++++++++++++------ scripts/slayrace.rb | 32 +++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 724fa1fa3..6c463911a 100644 --- a/README.rst +++ b/README.rst @@ -1401,18 +1401,30 @@ Kills any unit of a given race. With no argument, lists the available races. +With the special argument 'him', targets only the selected creature. + Any non-dead non-caged unit of the specified race gets its ``blood_count`` -set to 0, which means immediate death at the next game tick. May not work -on vampires and other weird creatures. +set to 0, which means immediate death at the next game tick. For creatures +such as vampires, also set animal.vanish_countdown to 2. + +An alternate mode is selected by adding a 2nd argument to the command, +``magma``. In this case, a column of 7/7 magma is generated on top of the +targets until they die (Warning: do not call on magma-safe creatures. Also, +using this mode for birds is not recommanded.) + +Will target any unit on a revealed tile of the map, including ambushers. -Targets any unit on a revealed tile of the map, including ambushers. Ex: +Ex: :: slayrace gob -To kill a single creature in the same way, you can use the following line, -after selecting the unit with the 'v' cursor: +To kill a single creature, select the unit with the 'v' cursor and: +:: + slayrace him + +To purify all elves on the map with fire (may have side-effects): :: - rb_eval df.unit_find.body.blood_count = 0 + slayrace elve magma magmasource diff --git a/scripts/slayrace.rb b/scripts/slayrace.rb index 27b1ba3cc..b1fc24e35 100644 --- a/scripts/slayrace.rb +++ b/scripts/slayrace.rb @@ -1,6 +1,9 @@ # slay all creatures of a given race +# race = name of the race to eradicate, use 'him' to target only the selected creature race = $script_args[0] +# if the 2nd parameter is 'magma', magma rain for the targets instead of instant death +magma = ($script_args[1] == 'magma') checkunit = lambda { |u| u.body.blood_count != 0 and @@ -9,12 +12,39 @@ checkunit = lambda { |u| not df.map_designation_at(u).hidden } +slayit = lambda { |u| + if not magma + # just make them drop dead + u.body.blood_count = 0 + # some races dont mind having no blood, ensure they are still taken care of. + u.animal.vanish_countdown = 2 + else + # it's getting hot around here + # !!WARNING!! do not call on a magma-safe creature + ouh = df.onupdate_register(1) { + if u.flags1.dead + df.onupdate_unregister(ouh) + else + x, y, z = u.pos.x, u.pos.y, u.pos.z + z += 1 while tile = df.map_tile_at(x, y, z+1) and tile.shape_passableflow + df.map_tile_at(x, y, z).spawn_magma(7) + end + } + end +} + all_races = df.world.units.active.map { |u| u.race_tg.creature_id if checkunit[u] }.compact.uniq.sort if !race puts all_races +elsif race == 'him' + if him = df.unit_find + slayit[him] + else + puts "Choose target" + end else raw_race = df.match_rawname(race, all_races) raise 'invalid race' if not raw_race @@ -24,7 +54,7 @@ else count = 0 df.world.units.active.each { |u| if u.race == race_nr and checkunit[u] - u.body.blood_count = 0 + slayit[u] count += 1 end } From bb760bd376c0a0b4b622cbab0e1a7fca40598ed9 Mon Sep 17 00:00:00 2001 From: Valentin Ochs Date: Mon, 6 Aug 2012 16:55:55 +0200 Subject: [PATCH 13/92] Added quality constraints to workflow plugin --- plugins/workflow.cpp | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index dbf546070..c2875a1b8 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -20,6 +20,7 @@ #include "df/job_list_link.h" #include "df/dfhack_material_category.h" #include "df/item.h" +#include "df/item_quality.h" #include "df/items_other_id.h" #include "df/tool_uses.h" #include "df/general_ref.h" @@ -42,6 +43,7 @@ using std::string; using std::endl; using namespace DFHack; using namespace df::enums; +using namespace df::enums::item_quality; using df::global::world; using df::global::ui; @@ -283,6 +285,8 @@ struct ItemConstraint { int weight; std::vector jobs; + enum item_quality min_quality; + int item_amount, item_count, item_inuse; bool request_suspend, request_resume; @@ -293,7 +297,7 @@ struct ItemConstraint { public: ItemConstraint() : is_craft(false), weight(0), item_amount(0), item_count(0), item_inuse(0) - , is_active(false), cant_resume_reported(false) + , is_active(false), cant_resume_reported(false), min_quality(Ordinary) {} int goalCount() { return config.ival(0); } @@ -646,7 +650,7 @@ static ItemConstraint *get_constraint(color_ostream &out, const std::string &str std::vector tokens; split_string(&tokens, str, "/"); - if (tokens.size() > 3) + if (tokens.size() > 4) return NULL; int weight = 0; @@ -670,10 +674,10 @@ static ItemConstraint *get_constraint(color_ostream &out, const std::string &str out.printerr("Cannot decode material mask: %s\n", maskstr.c_str()); return NULL; } - + if (mat_mask.whole != 0) weight += 100; - + MaterialInfo material; std::string matstr = vector_get(tokens,2); if (!matstr.empty() && (!material.find(matstr) || !material.isValid())) { @@ -681,6 +685,22 @@ static ItemConstraint *get_constraint(color_ostream &out, const std::string &str return NULL; } + enum item_quality minqual = Ordinary; + std::string qualstr = vector_get(tokens, 3); + if(!qualstr.empty()) { + if(qualstr == "ordinary") minqual = Ordinary; + else if(qualstr == "wellcrafted") minqual = WellCrafted; + else if(qualstr == "finelycrafted") minqual = FinelyCrafted; + else if(qualstr == "superior") minqual = Superior; + else if(qualstr == "exceptional") minqual = Exceptional; + else if(qualstr == "masterful") minqual = Masterful; + else { + out.printerr("Cannot find quality: %s\nKnown qualities: ordinary, wellcrafted, finelycrafted, superior, exceptional, masterful\n", qualstr.c_str()); + return NULL; + } + + } + if (material.type >= 0) weight += (material.index >= 0 ? 5000 : 1000); @@ -694,7 +714,8 @@ static ItemConstraint *get_constraint(color_ostream &out, const std::string &str ItemConstraint *ct = constraints[i]; if (ct->is_craft == is_craft && ct->item == item && ct->material == material && - ct->mat_mask.whole == mat_mask.whole) + ct->mat_mask.whole == mat_mask.whole && + ct->min_quality == minqual) return ct; } @@ -703,6 +724,7 @@ static ItemConstraint *get_constraint(color_ostream &out, const std::string &str nct->item = item; nct->material = material; nct->mat_mask = mat_mask; + nct->min_quality = minqual; nct->weight = weight; if (cfg) @@ -1179,6 +1201,9 @@ static void map_job_items(color_ostream &out) (cv->item.subtype != -1 && cv->item.subtype != isubtype)) continue; } + if(item->getQuality() < cv->min_quality) { + continue; + } TMaterialCache::iterator it = cv->material_cache.find(matkey); From 2c703ffbca9bac26ba64aad7484e012da451ff52 Mon Sep 17 00:00:00 2001 From: Valentin Ochs Date: Mon, 6 Aug 2012 16:59:09 +0200 Subject: [PATCH 14/92] Remove unnecessary whitespace --- plugins/workflow.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index c2875a1b8..0b87b3590 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -698,7 +698,6 @@ static ItemConstraint *get_constraint(color_ostream &out, const std::string &str out.printerr("Cannot find quality: %s\nKnown qualities: ordinary, wellcrafted, finelycrafted, superior, exceptional, masterful\n", qualstr.c_str()); return NULL; } - } if (material.type >= 0) From 4039d2c0b7cddf458cb5ad4afc006ab66371416d Mon Sep 17 00:00:00 2001 From: Valentin Ochs Date: Wed, 8 Aug 2012 10:47:02 +0200 Subject: [PATCH 15/92] Don't disable autolabor when you input "autolabor 1|enable" and it's already enabled. --- plugins/autolabor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 512899d75..d23c66925 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -1348,7 +1348,7 @@ command_result autolabor (color_ostream &out, std::vector & parame { enable_plugin(out); } - else + else if(!enable) { if (enable_autolabor) { From 2abb9a898ca86c301db626db227714073e61a3a3 Mon Sep 17 00:00:00 2001 From: Valentin Ochs Date: Wed, 8 Aug 2012 10:50:07 +0200 Subject: [PATCH 16/92] One more autolabor cleanup --- plugins/autolabor.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index d23c66925..c3a2b313e 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -1348,16 +1348,12 @@ command_result autolabor (color_ostream &out, std::vector & parame { enable_plugin(out); } - else if(!enable) + else if(!enable && enable_autolabor) { - if (enable_autolabor) - { - enable_autolabor = false; - setOptionEnabled(CF_ENABLED, false); - } + enable_autolabor = false; + setOptionEnabled(CF_ENABLED, false); out << "The plugin is disabled." << endl; - return CR_OK; } return CR_OK; From 7a03f93dbd4f1a3af2d8c367b4cba22d675d2a82 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 9 Aug 2012 14:40:14 +0200 Subject: [PATCH 17/92] ruby: add raw stl::string allocation, add _cpp_delete, tweak readme --- plugins/ruby/README | 48 ++++++++++++++---- plugins/ruby/ruby-autogen-defs.rb | 62 +++++++++++++++++++---- plugins/ruby/ruby.cpp | 83 ++++++++++++++++++++++++------- 3 files changed, 155 insertions(+), 38 deletions(-) diff --git a/plugins/ruby/README b/plugins/ruby/README index 97a4cbd30..8a473f332 100644 --- a/plugins/ruby/README +++ b/plugins/ruby/README @@ -23,7 +23,7 @@ All ruby code runs while the main DF process and other plugins are suspended. DFHack console -------------- -The ruby plugin defines 1 dfhack console command: +The ruby plugin defines one new dfhack console command: rb_eval ; evaluate a ruby expression and show the result in the console. Ex: rb_eval df.unit_find().name.first_name You can use single-quotes for strings ; avoid double-quotes that are parsed @@ -50,7 +50,7 @@ The script can access the console command arguments through the global variable '$script_args', which is an array of ruby Strings. The help string displayed in dfhack 'ls' command is the first line of the -script, if it is a comment (starts with '# '). +script, if it is a comment (ie starts with '# '). Ruby helper functions @@ -67,7 +67,9 @@ obj1 and 2 should respond to #pos and #x #y #z. Returns the MapBlock for the coordinates or nil. df.map_tile_at(pos) -Returns a MapTile, holds all information relative to the map tile. +Returns a MapTile, holding all informations wrt the map tile (read&write). +This class is a ruby specific extention, to facilitate interaction with the +DF map data. Check out hack/ruby/map.rb. df.each_map_block { |b| } df.each_map_block_z(zlevel) { |b| } @@ -142,9 +144,16 @@ The ruby classes defined in ruby-autogen.rb are accessors to the underlying df C++ objects in-memory. To allocate a new C++ object for use in DF, use the RubyClass.cpp_new method (see buildings.rb for exemples), works for Compounds only. +A special Compound DFHack::StlString is available for allocating a single c++ +stl::string, so that you can call vmethods that take a string pointer argument +(eg getName). + ex: s = DFHack::StlString.cpp_new ; df.building_find.getName(s) ; p s.str -Deallocation is not supported. You may manually call df.free if you know -what you are doing (maps directly to the native malloc/free) +Deallocation may work, using the compound method _cpp_delete. Use with caution, +may crash your DF session. It may be simpler to just leak the memory. +_cpp_delete will try to free all memory directly used by the compound, eg +strings and vectors. It will *not* call the class destructor, and will not free +stuff behind pointers. C++ std::string fields may be directly re-allocated using standard ruby strings, e.g. some_unit.name.nickname = 'moo' @@ -160,11 +169,13 @@ To delete an element, vector.delete_at(index) You can binary search an element in a vector for a given numeric field value: df.world.unit.all.binsearch(42, :id) -will find the element whose 'id' field is 42 (needs the vector to be initially +will find the entry whose 'id' field is 42 (needs the vector to be initially sorted by this field). The binsearch 2nd argument defaults to :id. Any numeric field defined as being an enum value will be converted to a ruby Symbol. This works for array indexes too. + ex: df.unit_find(:selected).status.labors[:HAUL_FOOD] = true + df.map_tile_at(df.cursor).designation.liquid_type = :Water Virtual method calls are supported for C++ objects, with a maximum of 4 arguments. Arguments / return value are interpreted as Compound/Enums as @@ -194,7 +205,7 @@ Change current unit profession Center the screen on unit ID '123' df.center_viewscreen(df.unit_find(123)) -Find an item at a given position, show its C++ classname +Find an item under the game cursor and show its C++ classname p df.item_find(df.cursor)._rtti_classname Find the raws name of the plant under cursor @@ -205,15 +216,29 @@ Dig a channel under the cursor df.map_designation_at(df.cursor).dig = :Channel df.map_block_at(df.cursor).flags.designated = true +Spawn 2/7 magma on the tile of the dwarf nicknamed 'hotfeet' + hot = df.unit_citizens.find { |u| u.name.nickname == 'hotfeet' } + df.map_tile_at(hot).spawn_magma(2) + Plugin compilation ------------------ -The plugin consists of the *.rb file including user comfort functions and -describing basic classes used by the autogenerated code, and ruby-autogen.rb, -the auto-generated code. +The plugin consists of the main ruby.cpp native plugin and the *.rb files. + +The native plugin handles only low-level ruby-to-df interaction (eg raw memory +read/write, and dfhack integration), and the .rb files hold end-user helper +functions. + +On dfhack start, the native plugin will initialize the ruby interpreter, and +load hack/ruby/ruby.rb. This one then loads all other .rb files. -autogen is output by codegen.pl from dfhack/library/include/df/codegen.out.xml +The DF internal structures are described in ruby-autogen.rb . +It is output by ruby/codegen.pl, from dfhack/library/include/df/codegen.out.xml +It contains architecture-specific data (eg DF internal structures field offsets, +which differ between Windows and Linux. Linux and Macosx are the same, as they +both use gcc). +It is stored inside the build directory (eg build/plugins/ruby/ruby-autogen.rb) For exemple, @@ -230,6 +255,7 @@ Will generate The syntax for the 'field' method in ruby-autogen.rb is: 1st argument = name of the method 2nd argument = offset of this field from the beginning of the current struct. + This field depends on the compiler used by Toady to generate DF. The block argument describes the type of the field: uint32, ptr to global... Primitive type access is done through native methods from ruby.cpp (vector length, diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index 2e4948acb..7165e2448 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -7,6 +7,7 @@ module DFHack def _at(addr) ; d = dup ; d._memaddr = addr ; d ; end def _get ; self ; end def _cpp_init ; end + def _cpp_delete ; end end class Compound < MemStruct @@ -114,6 +115,11 @@ module DFHack def _cpp_init _fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_init } end + def _cpp_delete + _fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_delete } + DFHack.free(@_memaddr) + @_memaddr = nil # turn future segfaults in harmless ruby exceptions + end def _set(h) case h when Hash; h.each { |k, v| send("#{k}=", v) } @@ -372,6 +378,9 @@ module DFHack def _cpp_init _length.times { |i| _tgat(i)._cpp_init } end + def _cpp_delete + _length.times { |i| _tgat(i)._cpp_delete } + end alias length _length alias size _length def _tgat(i) @@ -423,10 +432,10 @@ module DFHack DFHack.memory_vector32_ptrat(@_memaddr, idx) end def insert_at(idx, val) - DFHack.memory_vector32_insert(@_memaddr, idx, val) + DFHack.memory_vector32_insertat(@_memaddr, idx, val) end def delete_at(idx) - DFHack.memory_vector32_delete(@_memaddr, idx) + DFHack.memory_vector32_deleteat(@_memaddr, idx) end def _set(v) @@ -434,6 +443,12 @@ module DFHack v.each_with_index { |e, i| self[i] = e } # patch entries end + def self._cpp_new + new._at DFHack.memory_vector_new + end + def _cpp_delete + DFHack.memory_vector_delete(@_memaddr) + end def _cpp_init DFHack.memory_vector_init(@_memaddr) end @@ -496,10 +511,10 @@ module DFHack DFHack.memory_vector16_ptrat(@_memaddr, idx) end def insert_at(idx, val) - DFHack.memory_vector16_insert(@_memaddr, idx, val) + DFHack.memory_vector16_insertat(@_memaddr, idx, val) end def delete_at(idx) - DFHack.memory_vector16_delete(@_memaddr, idx) + DFHack.memory_vector16_deleteat(@_memaddr, idx) end end class StlVector8 < StlVector32 @@ -510,10 +525,10 @@ module DFHack DFHack.memory_vector8_ptrat(@_memaddr, idx) end def insert_at(idx, val) - DFHack.memory_vector8_insert(@_memaddr, idx, val) + DFHack.memory_vector8_insertat(@_memaddr, idx, val) end def delete_at(idx) - DFHack.memory_vector8_delete(@_memaddr, idx) + DFHack.memory_vector8_deleteat(@_memaddr, idx) end end class StlBitVector < StlVector32 @@ -522,10 +537,10 @@ module DFHack DFHack.memory_vectorbool_length(@_memaddr) end def insert_at(idx, val) - DFHack.memory_vectorbool_insert(@_memaddr, idx, val) + DFHack.memory_vectorbool_insertat(@_memaddr, idx, val) end def delete_at(idx) - DFHack.memory_vectorbool_delete(@_memaddr, idx) + DFHack.memory_vectorbool_deleteat(@_memaddr, idx) end def [](idx) idx += length if idx < 0 @@ -541,6 +556,12 @@ module DFHack DFHack.memory_vectorbool_setat(@_memaddr, idx, v) end end + def self._cpp_new + new._at DFHack.memory_vectorbool_new + end + def _cpp_delete + DFHack.memory_vectorbool_delete(@_memaddr) + end end class StlString < MemStruct def _get @@ -551,6 +572,12 @@ module DFHack DFHack.memory_write_stlstring(@_memaddr, v) end + def self._cpp_new + new._at DFHack.memory_stlstring_new + end + def _cpp_delete + DFHack.memory_stlstring_delete(@_memaddr) + end def _cpp_init DFHack.memory_stlstring_init(@_memaddr) end @@ -574,7 +601,7 @@ module DFHack def length DFHack.memory_bitarray_length(@_memaddr) end - # TODO _cpp_init + # TODO _cpp_init, _cpp_delete def size ; length ; end def resize(len) DFHack.memory_bitarray_resize(@_memaddr, len) @@ -608,7 +635,7 @@ module DFHack def length ; _length ; end def size ; _length ; end - # TODO _cpp_init + # TODO _cpp_init, _cpp_delete def _tgat(i) @_tg._at(_ptr + i*@_tglen) if i >= 0 and i < _length end @@ -702,6 +729,21 @@ module DFHack def self.sym(v) ; (!v || (v == 0)) ? false : true ; end end + class StlString < MemHack::Compound + field(:str, 0) { stl_string } + + def self.cpp_new(init=nil) + s = MemHack::StlString._cpp_new + s._set(init) if init + new._at(s._memaddr) + end + + def _cpp_delete + MemHack::StlString.new._at(@_memaddr+0)._cpp_delete + @_memaddr = nil + end + end + # cpp rtti name -> rb class @rtti_n2c = {} @rtti_c2n = {} diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 08ea13b9f..1391faa44 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -528,7 +528,7 @@ static VALUE rb_dfmalloc(VALUE self, VALUE len) if (!ptr) return Qnil; memset(ptr, 0, FIX2INT(len)); - return rb_uint2inum((long)ptr); + return rb_uint2inum((uint32_t)ptr); } static VALUE rb_dffree(VALUE self, VALUE ptr) @@ -599,6 +599,18 @@ static VALUE rb_dfmemory_write_float(VALUE self, VALUE addr, VALUE val) // stl::string +static VALUE rb_dfmemory_stlstring_new(VALUE self) +{ + std::string *ptr = new std::string; + return rb_uint2inum((uint32_t)ptr); +} +static VALUE rb_dfmemory_stlstring_delete(VALUE self, VALUE addr) +{ + std::string *ptr = (std::string*)rb_num2ulong(addr); + if (ptr) + delete ptr; + return Qtrue; +} static VALUE rb_dfmemory_stlstring_init(VALUE self, VALUE addr) { // XXX THIS IS TERRIBLE @@ -621,6 +633,18 @@ static VALUE rb_dfmemory_write_stlstring(VALUE self, VALUE addr, VALUE val) // vector access +static VALUE rb_dfmemory_vec_new(VALUE self) +{ + std::vector *ptr = new std::vector; + return rb_uint2inum((uint32_t)ptr); +} +static VALUE rb_dfmemory_vec_delete(VALUE self, VALUE addr) +{ + std::vector *ptr = (std::vector*)rb_num2ulong(addr); + if (ptr) + delete ptr; + return Qtrue; +} static VALUE rb_dfmemory_vec_init(VALUE self, VALUE addr) { std::vector *ptr = new std::vector; @@ -638,13 +662,13 @@ static VALUE rb_dfmemory_vec8_ptrat(VALUE self, VALUE addr, VALUE idx) std::vector *v = (std::vector*)rb_num2ulong(addr); return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx))); } -static VALUE rb_dfmemory_vec8_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) +static VALUE rb_dfmemory_vec8_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val)); return Qtrue; } -static VALUE rb_dfmemory_vec8_delete(VALUE self, VALUE addr, VALUE idx) +static VALUE rb_dfmemory_vec8_deleteat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->erase(v->begin()+FIX2INT(idx)); @@ -662,13 +686,13 @@ static VALUE rb_dfmemory_vec16_ptrat(VALUE self, VALUE addr, VALUE idx) std::vector *v = (std::vector*)rb_num2ulong(addr); return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx))); } -static VALUE rb_dfmemory_vec16_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) +static VALUE rb_dfmemory_vec16_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val)); return Qtrue; } -static VALUE rb_dfmemory_vec16_delete(VALUE self, VALUE addr, VALUE idx) +static VALUE rb_dfmemory_vec16_deleteat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->erase(v->begin()+FIX2INT(idx)); @@ -686,13 +710,13 @@ static VALUE rb_dfmemory_vec32_ptrat(VALUE self, VALUE addr, VALUE idx) std::vector *v = (std::vector*)rb_num2ulong(addr); return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx))); } -static VALUE rb_dfmemory_vec32_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) +static VALUE rb_dfmemory_vec32_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val)); return Qtrue; } -static VALUE rb_dfmemory_vec32_delete(VALUE self, VALUE addr, VALUE idx) +static VALUE rb_dfmemory_vec32_deleteat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->erase(v->begin()+FIX2INT(idx)); @@ -700,6 +724,24 @@ static VALUE rb_dfmemory_vec32_delete(VALUE self, VALUE addr, VALUE idx) } // vector +static VALUE rb_dfmemory_vecbool_new(VALUE self) +{ + std::vector *ptr = new std::vector; + return rb_uint2inum((uint32_t)ptr); +} +static VALUE rb_dfmemory_vecbool_delete(VALUE self, VALUE addr) +{ + std::vector *ptr = (std::vector*)rb_num2ulong(addr); + if (ptr) + delete ptr; + return Qtrue; +} +static VALUE rb_dfmemory_vecbool_init(VALUE self, VALUE addr) +{ + std::vector *ptr = new std::vector; + memcpy((void*)rb_num2ulong(addr), (void*)ptr, sizeof(*ptr)); + return Qtrue; +} static VALUE rb_dfmemory_vecbool_length(VALUE self, VALUE addr) { std::vector *v = (std::vector*)rb_num2ulong(addr); @@ -716,13 +758,13 @@ static VALUE rb_dfmemory_vecbool_setat(VALUE self, VALUE addr, VALUE idx, VALUE v->at(FIX2INT(idx)) = (BOOL_ISFALSE(val) ? 0 : 1); return Qtrue; } -static VALUE rb_dfmemory_vecbool_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) +static VALUE rb_dfmemory_vecbool_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->insert(v->begin()+FIX2INT(idx), (BOOL_ISFALSE(val) ? 0 : 1)); return Qtrue; } -static VALUE rb_dfmemory_vecbool_delete(VALUE self, VALUE addr, VALUE idx) +static VALUE rb_dfmemory_vecbool_deleteat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->erase(v->begin()+FIX2INT(idx)); @@ -834,27 +876,34 @@ static void ruby_bind_dfhack(void) { rb_define_singleton_method(rb_cDFHack, "memory_write_int32", RUBY_METHOD_FUNC(rb_dfmemory_write_int32), 2); rb_define_singleton_method(rb_cDFHack, "memory_write_float", RUBY_METHOD_FUNC(rb_dfmemory_write_float), 2); + rb_define_singleton_method(rb_cDFHack, "memory_stlstring_new", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_new), 0); + rb_define_singleton_method(rb_cDFHack, "memory_stlstring_delete", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_delete), 1); rb_define_singleton_method(rb_cDFHack, "memory_stlstring_init", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_init), 1); rb_define_singleton_method(rb_cDFHack, "memory_read_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_read_stlstring), 1); rb_define_singleton_method(rb_cDFHack, "memory_write_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_write_stlstring), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector_new", RUBY_METHOD_FUNC(rb_dfmemory_vec_new), 0); + rb_define_singleton_method(rb_cDFHack, "memory_vector_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec_delete), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector_init", RUBY_METHOD_FUNC(rb_dfmemory_vec_init), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector8_length", RUBY_METHOD_FUNC(rb_dfmemory_vec8_length), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector8_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_ptrat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector8_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec8_insert), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vector8_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec8_delete), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector8_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_insertat), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector8_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_deleteat), 2); rb_define_singleton_method(rb_cDFHack, "memory_vector16_length", RUBY_METHOD_FUNC(rb_dfmemory_vec16_length), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector16_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec16_ptrat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector16_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec16_insert), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vector16_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec16_delete), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector16_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vec16_insertat), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector16_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vec16_deleteat), 2); rb_define_singleton_method(rb_cDFHack, "memory_vector32_length", RUBY_METHOD_FUNC(rb_dfmemory_vec32_length), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector32_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec32_ptrat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector32_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec32_insert), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vector32_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec32_delete), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector32_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vec32_insertat), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector32_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vec32_deleteat), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_new", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_new), 0); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_delete", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_delete), 1); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_init", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_init), 1); rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_length", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_length), 1); rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_at", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_at), 2); rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_setat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_setat), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_insert", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_insert), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_delete", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_delete), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_insertat), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_deleteat), 2); rb_define_singleton_method(rb_cDFHack, "memory_bitarray_length", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_length), 1); rb_define_singleton_method(rb_cDFHack, "memory_bitarray_resize", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_resize), 2); rb_define_singleton_method(rb_cDFHack, "memory_bitarray_isset", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_isset), 2); From 8f4c39d3f5ef22ce4065741e1d0cccde20497493 Mon Sep 17 00:00:00 2001 From: jj Date: Thu, 9 Aug 2012 17:07:20 +0200 Subject: [PATCH 18/92] showmood: fix fetched/needed quantity calculation --- plugins/showmood.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/showmood.cpp b/plugins/showmood.cpp index 10d7b52c2..0b3fa8a99 100644 --- a/plugins/showmood.cpp +++ b/plugins/showmood.cpp @@ -12,6 +12,7 @@ #include "df/world.h" #include "df/job.h" #include "df/job_item.h" +#include "df/job_item_ref.h" #include "df/general_ref.h" #include "df/builtin_mats.h" #include "df/inorganic_raw.h" @@ -165,7 +166,10 @@ command_result df_showmood (color_ostream &out, vector & parameters) out.print("not yet claimed a workshop but will want"); out.print(" the following items:\n"); - int count_got = job->items.size(), got; + // total amount of stuff fetched so far + int count_got = 0; + for (size_t i = 0; i < job->items.size(); i++) + count_got += job->items[i]->item->getTotalDimension(); for (size_t i = 0; i < job->job_items.size(); i++) { @@ -269,7 +273,9 @@ command_result df_showmood (color_ostream &out, vector & parameters) } } - got = count_got; + // total amount of stuff fetched for this requirement + // XXX may fail with cloth/thread/bars if need 1 and fetch 2 + int got = count_got; if (got > item->quantity) got = item->quantity; out.print(", quantity %i (got %i)\n", item->quantity, got); From 942b245461606a902b6cfda38fcdbf9e3b6c3131 Mon Sep 17 00:00:00 2001 From: jj Date: Sat, 11 Aug 2012 20:08:33 +0200 Subject: [PATCH 19/92] ruby: add method to check if an item is free for use as construction material --- plugins/ruby/building.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/ruby/building.rb b/plugins/ruby/building.rb index 9a59411f9..ab029ac24 100644 --- a/plugins/ruby/building.rb +++ b/plugins/ruby/building.rb @@ -286,14 +286,23 @@ module DFHack job end + # check item flags to see if it is suitable for use as a building material + def building_isitemfree(i) + !i.flags.in_job and + !i.flags.in_inventory and + !i.flags.removed and + !i.flags.in_building and + !i.flags.owned and + !i.flags.forbid + end + # exemple usage def buildbed(pos=cursor) raise 'where to ?' if pos.x < 0 item = world.items.all.find { |i| i.kind_of?(ItemBedst) and - i.itemrefs.empty? and - !i.flags.in_job + building_isitemfree(i) } raise 'no free bed, build more !' if not item From 4c720c75046ccdefb7705a1d3742f22d75a314fe Mon Sep 17 00:00:00 2001 From: jj Date: Sat, 11 Aug 2012 20:11:00 +0200 Subject: [PATCH 20/92] ruby: add explicit paths everywhere for ruby-autogen --- plugins/ruby/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index 0d9f1187d..e10ee38bf 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -18,12 +18,12 @@ IF (DL_RUBY AND NOT APPLE) ENDIF(DL_RUBY AND NOT APPLE) ADD_CUSTOM_COMMAND( - OUTPUT ruby-autogen.rb - COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ruby-autogen.rb + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.rb + COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.rb DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl COMMENT ruby-autogen.rb ) -ADD_CUSTOM_TARGET(ruby-autogen-rb DEPENDS ruby-autogen.rb) +ADD_CUSTOM_TARGET(ruby-autogen-rb DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.rb) INCLUDE_DIRECTORIES("${dfhack_SOURCE_DIR}/depends/tthread") From cb04a082fd60499c4463809787b1f9b93b7b0f1b Mon Sep 17 00:00:00 2001 From: jj Date: Sat, 11 Aug 2012 21:41:47 +0200 Subject: [PATCH 21/92] move script/fixstuckdoors to script/fix/stuckdoors --- scripts/{fixstuckdoors.rb => fix/stuckdoors.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename scripts/{fixstuckdoors.rb => fix/stuckdoors.rb} (100%) diff --git a/scripts/fixstuckdoors.rb b/scripts/fix/stuckdoors.rb similarity index 100% rename from scripts/fixstuckdoors.rb rename to scripts/fix/stuckdoors.rb From 2362bb8eea2d45352fc42c995d8aa2d0fb2b4689 Mon Sep 17 00:00:00 2001 From: jj Date: Sat, 11 Aug 2012 22:25:08 +0200 Subject: [PATCH 22/92] ruby: add popup_announcement method --- plugins/ruby/ui.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/ruby/ui.rb b/plugins/ruby/ui.rb index 6d2b5c2cd..9dded66c2 100644 --- a/plugins/ruby/ui.rb +++ b/plugins/ruby/ui.rb @@ -68,5 +68,14 @@ module DFHack world.status.display_timer = 2000 end end + + # add an announcement to display in a game popup message + # (eg "the megabeast foobar arrived") + def popup_announcement(str, color=nil, bright=nil) + pop = PopupMessage.cpp_new(:text => str) + pop.color = color if color + pop.bright = bright if bright + world.status.popups << pop + end end end From 85f1a6fa6ac2248f3d4b867c70e220fbed55c651 Mon Sep 17 00:00:00 2001 From: jj Date: Sat, 11 Aug 2012 22:25:40 +0200 Subject: [PATCH 23/92] add fix/loyaltycascade script --- scripts/fix/loyaltycascade.rb | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 scripts/fix/loyaltycascade.rb diff --git a/scripts/fix/loyaltycascade.rb b/scripts/fix/loyaltycascade.rb new file mode 100644 index 000000000..632f8024e --- /dev/null +++ b/scripts/fix/loyaltycascade.rb @@ -0,0 +1,51 @@ +# script to fix loyalty cascade, when you order your militia to kill friendly units + +def fixunit(u) + return if u.race != df.ui.race_id or u.civ_id != df.ui.civ_id + links = u.hist_figure_tg.entity_links + fixed = false + + if i1 = links.index { |l| + l.kind_of?(DFHack::HistfigEntityLinkFormerMemberst) and + l.entity_id == df.ui.civ_id + } and i2 = links.index { |l| + l.kind_of?(DFHack::HistfigEntityLinkEnemyst) and + l.entity_id == df.ui.civ_id + } + fixed = true + i1, i2 = i2, i1 if i1 > i2 + links.delete_at i2 + links.delete_at i1 + links << DFHack::HistfigEntityLinkMemberst.cpp_new(:entity_id => df.ui.civ_id, :link_strength => 100) + df.add_announcement "fixloyalty: #{u.name} is now a member of #{df.ui.civ_tg.name} again" + end + + if i1 = links.index { |l| + l.kind_of?(DFHack::HistfigEntityLinkFormerMemberst) and + l.entity_id == df.ui.group_id + } and i2 = links.index { |l| + l.kind_of?(DFHack::HistfigEntityLinkEnemyst) and + l.entity_id == df.ui.group_id + } + fixed = true + i1, i2 = i2, i1 if i1 > i2 + links.delete_at i2 + links.delete_at i1 + links << DFHack::HistfigEntityLinkMemberst.cpp_new(:entity_id => df.ui.group_id, :link_strength => 100) + df.add_announcement "fixloyalty: #{u.name} is now a member of #{df.ui.group_tg.name} again" + end + + fixed +end + +fixed = 0 +df.unit_citizens.each { |u| + fixed += 1 if fixunit(u) +} + +if fixed > 0 + df.popup_announcement "Fixed a loyalty cascade, you should save and reload now" + puts "loyalty cascade fixed (#{fixed} dwarves), you should save and reload" +else + puts "no loyalty cascade found" +end From 030bd8ab571a631f955a3ae19813f6adb078e818 Mon Sep 17 00:00:00 2001 From: jj Date: Sun, 12 Aug 2012 00:24:34 +0200 Subject: [PATCH 24/92] fix/loyaltycascade: clear the enemy status cache, dont require a save/reload anymore --- plugins/ruby/ruby-autogen-defs.rb | 1 + scripts/fix/loyaltycascade.rb | 31 +++++++++++++++++++++---------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index 7165e2448..3507508e1 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -361,6 +361,7 @@ module DFHack def empty? ; length == 0 ; end def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end def index(e=nil, &b) ; (0...length).find { |i| b ? b[self[i]] : self[i] == e } ; end + def map! ; (0...length).each { |i| self[i] = yield(self[i]) } ; end def first ; self[0] ; end def last ; self[length-1] ; end end diff --git a/scripts/fix/loyaltycascade.rb b/scripts/fix/loyaltycascade.rb index 632f8024e..2f987bd55 100644 --- a/scripts/fix/loyaltycascade.rb +++ b/scripts/fix/loyaltycascade.rb @@ -1,10 +1,11 @@ # script to fix loyalty cascade, when you order your militia to kill friendly units -def fixunit(u) - return if u.race != df.ui.race_id or u.civ_id != df.ui.civ_id - links = u.hist_figure_tg.entity_links +def fixunit(unit) + return if unit.race != df.ui.race_id or unit.civ_id != df.ui.civ_id + links = unit.hist_figure_tg.entity_links fixed = false + # check if the unit is a civ renegade if i1 = links.index { |l| l.kind_of?(DFHack::HistfigEntityLinkFormerMemberst) and l.entity_id == df.ui.civ_id @@ -17,9 +18,10 @@ def fixunit(u) links.delete_at i2 links.delete_at i1 links << DFHack::HistfigEntityLinkMemberst.cpp_new(:entity_id => df.ui.civ_id, :link_strength => 100) - df.add_announcement "fixloyalty: #{u.name} is now a member of #{df.ui.civ_tg.name} again" + df.add_announcement "fixloyalty: #{unit.name} is now a member of #{df.ui.civ_tg.name} again" end + # check if the unit is a group renegade if i1 = links.index { |l| l.kind_of?(DFHack::HistfigEntityLinkFormerMemberst) and l.entity_id == df.ui.group_id @@ -32,20 +34,29 @@ def fixunit(u) links.delete_at i2 links.delete_at i1 links << DFHack::HistfigEntityLinkMemberst.cpp_new(:entity_id => df.ui.group_id, :link_strength => 100) - df.add_announcement "fixloyalty: #{u.name} is now a member of #{df.ui.group_tg.name} again" + df.add_announcement "fixloyalty: #{unit.name} is now a member of #{df.ui.group_tg.name} again" end + # fix the 'is an enemy' cache matrix (mark to be recalculated by the game when needed) + if fixed and unit.unknown8.enemy_status_slot != -1 + i = unit.unknown8.enemy_status_slot + unit.unknown8.enemy_status_slot = -1 + df.world.enemy_status_cache.slot_used[i] = false + df.world.enemy_status_cache.rel_map[i].map! { -1 } + df.world.enemy_status_cache.rel_map.each { |a| a[i] = -1 } + end + + # return true if we actually fixed the unit fixed end -fixed = 0 +count = 0 df.unit_citizens.each { |u| - fixed += 1 if fixunit(u) + count += 1 if fixunit(u) } -if fixed > 0 - df.popup_announcement "Fixed a loyalty cascade, you should save and reload now" - puts "loyalty cascade fixed (#{fixed} dwarves), you should save and reload" +if count > 0 + puts "loyalty cascade fixed (#{count} dwarves)" else puts "no loyalty cascade found" end From 61185d29cae36bf8bd33287acd019ec0db50ce50 Mon Sep 17 00:00:00 2001 From: jj Date: Sun, 12 Aug 2012 00:27:20 +0200 Subject: [PATCH 25/92] console-linux: silence minor gcc warnings --- library/Console-linux.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/library/Console-linux.cpp b/library/Console-linux.cpp index 6b4de736d..882d9527d 100644 --- a/library/Console-linux.cpp +++ b/library/Console-linux.cpp @@ -62,7 +62,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // George Vulov for MacOSX #ifndef __LINUX__ -#define TEMP_FAILURE_RETRY(expr) \ +#define TMP_FAILURE_RETRY(expr) \ ({ long int _res; \ do _res = (long int) (expr); \ while (_res == -1L && errno == EINTR); \ @@ -155,7 +155,7 @@ namespace DFHack FD_ZERO(&descriptor_set); FD_SET(STDIN_FILENO, &descriptor_set); FD_SET(exit_pipe[0], &descriptor_set); - int ret = TEMP_FAILURE_RETRY( + int ret = TMP_FAILURE_RETRY( select (FD_SETSIZE,&descriptor_set, NULL, NULL, NULL) ); if(ret == -1) @@ -165,7 +165,7 @@ namespace DFHack if (FD_ISSET(STDIN_FILENO, &descriptor_set)) { // read byte from stdin - ret = TEMP_FAILURE_RETRY( + ret = TMP_FAILURE_RETRY( read(STDIN_FILENO, &out, 1) ); if(ret == -1) @@ -245,7 +245,8 @@ namespace DFHack if(rawmode) { const char * clr = "\033c\033[3J\033[H"; - ::write(STDIN_FILENO,clr,strlen(clr)); + if (::write(STDIN_FILENO,clr,strlen(clr)) == -1) + ; } else { @@ -269,7 +270,8 @@ namespace DFHack { const char * colstr = getANSIColor(index); int lstr = strlen(colstr); - ::write(STDIN_FILENO,colstr,lstr); + if (::write(STDIN_FILENO,colstr,lstr) == -1) + ; } } /// Reset color to default @@ -656,7 +658,8 @@ bool Console::init(bool sharing) inited = false; return false; } - freopen("stdout.log", "w", stdout); + if (!freopen("stdout.log", "w", stdout)) + ; d = new Private(); // make our own weird streams so our IO isn't redirected d->dfout_C = fopen("/dev/tty", "w"); @@ -664,7 +667,8 @@ bool Console::init(bool sharing) clear(); d->supported_terminal = !isUnsupportedTerm() && isatty(STDIN_FILENO); // init the exit mechanism - pipe(d->exit_pipe); + if (pipe(d->exit_pipe) == -1) + ; FD_ZERO(&d->descriptor_set); FD_SET(STDIN_FILENO, &d->descriptor_set); FD_SET(d->exit_pipe[0], &d->descriptor_set); From eb524105653bd09a33eae2da6f21d1d1d5e6a530 Mon Sep 17 00:00:00 2001 From: jj Date: Sun, 12 Aug 2012 00:46:03 +0200 Subject: [PATCH 26/92] fix/loyaltycascade: reset enemy_status_cache.next_slot too --- scripts/fix/loyaltycascade.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/fix/loyaltycascade.rb b/scripts/fix/loyaltycascade.rb index 2f987bd55..6fad2947f 100644 --- a/scripts/fix/loyaltycascade.rb +++ b/scripts/fix/loyaltycascade.rb @@ -41,9 +41,11 @@ def fixunit(unit) if fixed and unit.unknown8.enemy_status_slot != -1 i = unit.unknown8.enemy_status_slot unit.unknown8.enemy_status_slot = -1 - df.world.enemy_status_cache.slot_used[i] = false - df.world.enemy_status_cache.rel_map[i].map! { -1 } - df.world.enemy_status_cache.rel_map.each { |a| a[i] = -1 } + cache = df.world.enemy_status_cache + cache.slot_used[i] = false + cache.rel_map[i].map! { -1 } + cache.rel_map.each { |a| a[i] = -1 } + cache.next_slot = i if cache.next_slot > i end # return true if we actually fixed the unit From 5a880d619c6e51093ff06bf7d8b99e672740bc76 Mon Sep 17 00:00:00 2001 From: "jj@jj" Date: Sun, 12 Aug 2012 22:13:01 +0200 Subject: [PATCH 27/92] ruby: add MapTile#dig, tweak unit_iscitizen --- plugins/ruby/README | 3 +-- plugins/ruby/map.rb | 5 ++++ plugins/ruby/unit.rb | 60 ++++++++++++++++++++++++++------------------ 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/plugins/ruby/README b/plugins/ruby/README index 8a473f332..c9a84fb37 100644 --- a/plugins/ruby/README +++ b/plugins/ruby/README @@ -213,8 +213,7 @@ Find the raws name of the plant under cursor p df.world.raws.plants.all[plant.mat_index].id Dig a channel under the cursor - df.map_designation_at(df.cursor).dig = :Channel - df.map_block_at(df.cursor).flags.designated = true + df.map_tile_at(df.cursor).dig(:Channel) Spawn 2/7 magma on the tile of the dwarf nicknamed 'hotfeet' hot = df.unit_citizens.find { |u| u.name.nickname == 'hotfeet' } diff --git a/plugins/ruby/map.rb b/plugins/ruby/map.rb index c99d5b88d..dccea7291 100644 --- a/plugins/ruby/map.rb +++ b/plugins/ruby/map.rb @@ -188,6 +188,11 @@ module DFHack "#" end + def dig(mode=:Default) + designation.dig = mode + mapblock.flags.designated = true + end + def spawn_liquid(quantity, is_magma=false, flowing=true) designation.flow_size = quantity designation.liquid_type = (is_magma ? :Magma : :Water) diff --git a/plugins/ruby/unit.rb b/plugins/ruby/unit.rb index ebcf249da..1a619c5ce 100644 --- a/plugins/ruby/unit.rb +++ b/plugins/ruby/unit.rb @@ -41,48 +41,60 @@ module DFHack # returns an Array of all units that are current fort citizen (dwarves, on map, not hostile) def unit_citizens - race = ui.race_id - civ = ui.civ_id world.units.active.find_all { |u| - u.race == race and u.civ_id == civ and !u.flags1.dead and !u.flags1.merchant and - !u.flags1.diplomat and !u.flags2.resident and !u.flags3.ghostly and - !u.curse.add_tags1.OPPOSED_TO_LIFE and !u.curse.add_tags1.CRAZED and - u.mood != :Berserk - # TODO check curse ; currently this should keep vampires, but may include werebeasts + unit_iscitizen(u) } end + def unit_iscitizen(u) + u.race == ui.race_id and u.civ_id == ui.civ_id and !u.flags1.dead and !u.flags1.merchant and + !u.flags1.diplomat and !u.flags2.resident and !u.flags3.ghostly and + !u.curse.add_tags1.OPPOSED_TO_LIFE and !u.curse.add_tags1.CRAZED and + u.mood != :Berserk + # TODO check curse ; currently this should keep vampires, but may include werebeasts + end + # list workers (citizen, not crazy / child / inmood / noble) def unit_workers - unit_citizens.find_all { |u| - u.mood == :None and - u.profession != :CHILD and - u.profession != :BABY and - # TODO MENIAL_WORK_EXEMPTION_SPOUSE - !unit_entitypositions(u).find { |pos| pos.flags[:MENIAL_WORK_EXEMPTION] } + world.units.active.find_all { |u| + unit_isworker(u) } end + def unit_isworker(u) + unit_iscitizen(u) and + u.mood == :None and + u.profession != :CHILD and + u.profession != :BABY and + # TODO MENIAL_WORK_EXEMPTION_SPOUSE + !unit_entitypositions(u).find { |pos| pos.flags[:MENIAL_WORK_EXEMPTION] } + end + # list currently idle workers def unit_idlers - unit_workers.find_all { |u| - # current_job includes eat/drink/sleep/pickupequip - !u.job.current_job and - # filter 'attend meeting' - not u.specific_refs.find { |s| s.type == :ACTIVITY } and - # filter soldiers (TODO check schedule) - u.military.squad_index == -1 and - # filter 'on break' - not u.status.misc_traits.find { |t| t.id == :OnBreak } + world.units.active.find_all { |u| + unit_isidler(u) } end + def unit_isidler(u) + unit_isworker(u) and + # current_job includes eat/drink/sleep/pickupequip + !u.job.current_job and + # filter 'attend meeting' + not u.specific_refs.find { |s| s.type == :ACTIVITY } and + # filter soldiers (TODO check schedule) + u.military.squad_index == -1 and + # filter 'on break' + not u.status.misc_traits.find { |t| t.id == :OnBreak } + end + def unit_entitypositions(unit) list = [] - return list if not hf = world.history.figures.binsearch(unit.hist_figure_id) + return list if not hf = unit.hist_figure_tg hf.entity_links.each { |el| next if el._rtti_classname != :histfig_entity_link_positionst - next if not ent = world.entities.all.binsearch(el.entity_id) + next if not ent = el.entity_tg next if not pa = ent.positions.assignments.binsearch(el.assignment_id) next if not pos = ent.positions.own.binsearch(pa.position_id) list << pos From 53a05365066ba47da2e047e4b8ef87d2a80405d4 Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 13 Aug 2012 00:46:27 +0200 Subject: [PATCH 28/92] ruby: typo in building_deconstruct --- plugins/ruby/building.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ruby/building.rb b/plugins/ruby/building.rb index ab029ac24..af152e198 100644 --- a/plugins/ruby/building.rb +++ b/plugins/ruby/building.rb @@ -279,9 +279,9 @@ module DFHack job = Job.cpp_new refbuildingholder = GeneralRefBuildingHolderst.cpp_new job.job_type = :DestroyBuilding - refbuildingholder.building_id = building.id + refbuildingholder.building_id = bld.id job.references << refbuildingholder - building.jobs << job + bld.jobs << job job_link job job end From e659d845ab63c1e354deafe351b9a86d5861168f Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 13 Aug 2012 02:06:56 +0200 Subject: [PATCH 29/92] ruby: fix LinkList.next= --- plugins/ruby/ruby-autogen-defs.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index 3507508e1..0cee6426f 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -666,9 +666,9 @@ module DFHack @_tg = tg end - field(:_ptr, 0) { number 32, false } - field(:_prev, 4) { number 32, false } - field(:_next, 8) { number 32, false } + field(:_ptr, 0) { pointer } + field(:_prev, 4) { pointer } + field(:_next, 8) { pointer } def item # With the current xml structure, currently _tg designate @@ -682,22 +682,24 @@ module DFHack def item=(v) #addr = _ptr - #raise 'null pointer' if addr == 0 + #raise 'null pointer' if not addr #@_tg.at(addr)._set(v) raise 'null pointer' end def prev addr = _prev - return if addr == 0 + return if not addr @_tg._at(addr)._get end def next addr = _next - return if addr == 0 + return if not addr @_tg._at(addr)._get end + alias next= _next= + alias prev= _prev= include Enumerable def each From 4eedd6f5f0f79347a45c191eceb73d44a0c16ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 13 Aug 2012 07:39:11 +0200 Subject: [PATCH 30/92] Fix workflow. --- plugins/workflow.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 0b87b3590..a077db05a 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -285,7 +285,7 @@ struct ItemConstraint { int weight; std::vector jobs; - enum item_quality min_quality; + item_quality::item_quality min_quality; int item_amount, item_count, item_inuse; bool request_suspend, request_resume; @@ -296,8 +296,7 @@ struct ItemConstraint { public: ItemConstraint() - : is_craft(false), weight(0), item_amount(0), item_count(0), item_inuse(0) - , is_active(false), cant_resume_reported(false), min_quality(Ordinary) + : is_craft(false), weight(0), min_quality(Ordinary),item_amount(0), item_count(0), item_inuse(0), is_active(false), cant_resume_reported(false) {} int goalCount() { return config.ival(0); } @@ -685,7 +684,7 @@ static ItemConstraint *get_constraint(color_ostream &out, const std::string &str return NULL; } - enum item_quality minqual = Ordinary; + item_quality::item_quality minqual = Ordinary; std::string qualstr = vector_get(tokens, 3); if(!qualstr.empty()) { if(qualstr == "ordinary") minqual = Ordinary; @@ -1504,13 +1503,14 @@ static command_result workflow_cmd(color_ostream &out, vector & paramet } df::building *workshop = NULL; - df::job *job = NULL; + //FIXME: unused variable! + //df::job *job = NULL; if (Gui::dwarfmode_hotkey(Core::getTopViewscreen()) && ui->main.mode == ui_sidebar_mode::QueryBuilding) { workshop = world->selected_building; - job = Gui::getSelectedWorkshopJob(out, true); + //job = Gui::getSelectedWorkshopJob(out, true); } std::string cmd = parameters.empty() ? "list" : parameters[0]; From cde97bba6f5fe09c3a46babd073e8e1d30be3cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 13 Aug 2012 07:42:02 +0200 Subject: [PATCH 31/92] Fix workflow some more --- plugins/workflow.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index a077db05a..162051242 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -43,7 +43,6 @@ using std::string; using std::endl; using namespace DFHack; using namespace df::enums; -using namespace df::enums::item_quality; using df::global::world; using df::global::ui; @@ -296,7 +295,8 @@ struct ItemConstraint { public: ItemConstraint() - : is_craft(false), weight(0), min_quality(Ordinary),item_amount(0), item_count(0), item_inuse(0), is_active(false), cant_resume_reported(false) + : is_craft(false), weight(0), min_quality(item_quality::Ordinary),item_amount(0), + item_count(0), item_inuse(0), is_active(false), cant_resume_reported(false) {} int goalCount() { return config.ival(0); } @@ -684,15 +684,15 @@ static ItemConstraint *get_constraint(color_ostream &out, const std::string &str return NULL; } - item_quality::item_quality minqual = Ordinary; + item_quality::item_quality minqual = item_quality::Ordinary; std::string qualstr = vector_get(tokens, 3); if(!qualstr.empty()) { - if(qualstr == "ordinary") minqual = Ordinary; - else if(qualstr == "wellcrafted") minqual = WellCrafted; - else if(qualstr == "finelycrafted") minqual = FinelyCrafted; - else if(qualstr == "superior") minqual = Superior; - else if(qualstr == "exceptional") minqual = Exceptional; - else if(qualstr == "masterful") minqual = Masterful; + if(qualstr == "ordinary") minqual = item_quality::Ordinary; + else if(qualstr == "wellcrafted") minqual = item_quality::WellCrafted; + else if(qualstr == "finelycrafted") minqual = item_quality::FinelyCrafted; + else if(qualstr == "superior") minqual = item_quality::Superior; + else if(qualstr == "exceptional") minqual = item_quality::Exceptional; + else if(qualstr == "masterful") minqual = item_quality::Masterful; else { out.printerr("Cannot find quality: %s\nKnown qualities: ordinary, wellcrafted, finelycrafted, superior, exceptional, masterful\n", qualstr.c_str()); return NULL; From 509d9570908e1befa537d10285942006d148b3a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Fri, 17 Aug 2012 13:02:31 +0200 Subject: [PATCH 32/92] Make protobuf behave with multiple build folders involved. --- depends/protobuf/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/depends/protobuf/CMakeLists.txt b/depends/protobuf/CMakeLists.txt index 570c77b18..24c4b275a 100644 --- a/depends/protobuf/CMakeLists.txt +++ b/depends/protobuf/CMakeLists.txt @@ -72,13 +72,13 @@ ELSE() SET(HASH_SET_CLASS hash_set) ENDIF() -CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" "${CMAKE_CURRENT_SOURCE_DIR}/config.h") +CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/config.h") SET(LIBPROTOBUF_LITE_HDRS google/protobuf/io/coded_stream.h google/protobuf/io/coded_stream_inl.h google/protobuf/stubs/common.h -config.h +${CMAKE_CURRENT_BINARY_DIR}/config.h google/protobuf/extension_set.h google/protobuf/generated_message_util.h google/protobuf/stubs/hash.h @@ -207,6 +207,7 @@ ENDIF() INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) SET(PROTOBUF_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) # Protobuf shared libraries From bcc41c081a43a68042c4757a6569cb690098d9b8 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 17 Aug 2012 14:32:04 +0400 Subject: [PATCH 33/92] Add a utility function for patching read-only memory. --- LUA_API.rst | 6 +++++ Lua API.html | 5 ++++ library/Core.cpp | 50 +++++++++++++++++++++++++++++++++++++ library/LuaApi.cpp | 12 +++++++++ library/include/MemAccess.h | 3 +++ 5 files changed, 76 insertions(+) diff --git a/LUA_API.rst b/LUA_API.rst index 2bb2c949e..449f92b46 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -1235,6 +1235,12 @@ and are only documented here for completeness: Returns a sequence of tables describing virtual memory ranges of the process. +* ``dfhack.internal.patchMemory(dest,src,count)`` + + Like memmove below, but works even if dest is read-only memory, e.g. code. + If destination overlaps a completely invalid memory region, or another error + occurs, returns false. + * ``dfhack.internal.memmove(dest,src,count)`` Wraps the standard memmove function. Accepts both numbers and refs as pointers. diff --git a/Lua API.html b/Lua API.html index 2c9a6a8df..7dfc7df00 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1409,6 +1409,11 @@ global environment, persistent between calls to the script.

  • dfhack.internal.getMemRanges()

    Returns a sequence of tables describing virtual memory ranges of the process.

  • +
  • dfhack.internal.patchMemory(dest,src,count)

    +

    Like memmove below, but works even if dest is read-only memory, e.g. code. +If destination overlaps a completely invalid memory region, or another error +occurs, returns false.

    +
  • dfhack.internal.memmove(dest,src,count)

    Wraps the standard memmove function. Accepts both numbers and refs as pointers.

  • diff --git a/library/Core.cpp b/library/Core.cpp index 826576b77..f129d9523 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1548,6 +1548,56 @@ void ClassNameCheck::getKnownClassNames(std::vector &names) names.push_back(*it); } +bool Process::patchMemory(void *target, const void* src, size_t count) +{ + uint8_t *sptr = (uint8_t*)target; + uint8_t *eptr = sptr + count; + + // Find the valid memory ranges + std::vector ranges; + getMemRanges(ranges); + + unsigned start = 0; + while (start < ranges.size() && ranges[start].end <= sptr) + start++; + if (start >= ranges.size() || ranges[start].start > sptr) + return false; + + unsigned end = start+1; + while (end < ranges.size() && ranges[end].start < eptr) + { + if (ranges[end].start != ranges[end-1].end) + return false; + end++; + } + if (ranges[end-1].end < eptr) + return false; + + // Verify current permissions + for (unsigned i = start; i < end; i++) + if (!ranges[i].valid || !(ranges[i].read || ranges[i].execute) || ranges[i].shared) + return false; + + // Apply writable permissions & update + bool ok = true; + + for (unsigned i = start; i < end && ok; i++) + { + t_memrange perms = ranges[i]; + perms.write = perms.read = true; + if (!setPermisions(perms, perms)) + ok = false; + } + + if (ok) + memmove(target, src, count); + + for (unsigned i = start; i < end && ok; i++) + setPermisions(ranges[i], ranges[i]); + + return ok; +} + /******************************************************************************* M O D U L E S *******************************************************************************/ diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index b0a085eca..108dba88f 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1124,6 +1124,17 @@ static int internal_getMemRanges(lua_State *L) return 1; } +static int internal_patchMemory(lua_State *L) +{ + void *dest = checkaddr(L, 1); + void *src = checkaddr(L, 2); + int size = luaL_checkint(L, 3); + if (size < 0) luaL_argerror(L, 1, "negative size"); + bool ok = Core::getInstance().p->patchMemory(dest, src, size); + lua_pushboolean(L, ok); + return 1; +} + static int internal_memmove(lua_State *L) { void *dest = checkaddr(L, 1); @@ -1214,6 +1225,7 @@ static const luaL_Reg dfhack_internal_funcs[] = { { "setAddress", internal_setAddress }, { "getVTable", internal_getVTable }, { "getMemRanges", internal_getMemRanges }, + { "patchMemory", internal_patchMemory }, { "memmove", internal_memmove }, { "memcmp", internal_memcmp }, { "memscan", internal_memscan }, diff --git a/library/include/MemAccess.h b/library/include/MemAccess.h index c51df3c64..0e5f618e2 100644 --- a/library/include/MemAccess.h +++ b/library/include/MemAccess.h @@ -283,6 +283,9 @@ namespace DFHack /// modify permisions of memory range bool setPermisions(const t_memrange & range,const t_memrange &trgrange); + + /// write a possibly read-only memory area + bool patchMemory(void *target, const void* src, size_t count); private: VersionInfo * my_descriptor; PlatformSpecific *d; From 236ffd578b66805fa45d65df79e099d00156bfff Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 17 Aug 2012 22:40:53 +0400 Subject: [PATCH 34/92] Add experimental support for interposing vmethods of known classes. The hairiest bit is the abuse of compiler-specific pointer-to-member internals in order to provide more or less transparent API. --- CMakeLists.txt | 3 + library/CMakeLists.txt | 2 + library/DataDefs.cpp | 8 + library/VTableInterpose.cpp | 238 ++++++++++++++++++++++++++++++ library/include/DataDefs.h | 11 ++ library/include/DataFuncs.h | 15 +- library/include/VTableInterpose.h | 158 ++++++++++++++++++++ plugins/devel/CMakeLists.txt | 1 + plugins/devel/vshook.cpp | 61 ++++++++ 9 files changed, 491 insertions(+), 6 deletions(-) create mode 100644 library/VTableInterpose.cpp create mode 100644 library/include/VTableInterpose.h create mode 100644 plugins/devel/vshook.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f77a6c3bb..dfb13cd5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,6 +111,9 @@ IF(UNIX) SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-g -Wall -Wno-unused-variable") SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -m32 -march=i686 -mtune=generic -std=c++0x") SET(CMAKE_C_FLAGS "-fvisibility=hidden -m32 -march=i686 -mtune=generic") +ELSEIF(MSVC) + # for msvc, tell it to always use 8-byte pointers to member functions to avoid confusion + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /vmg /vmm") ENDIF() # use shared libraries for protobuf diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index bbf22611b..cd3d52c8c 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -27,6 +27,7 @@ include/Core.h include/ColorText.h include/DataDefs.h include/DataIdentity.h +include/VTableInterpose.h include/LuaWrapper.h include/LuaTools.h include/Error.h @@ -53,6 +54,7 @@ SET(MAIN_SOURCES Core.cpp ColorText.cpp DataDefs.cpp +VTableInterpose.cpp LuaWrapper.cpp LuaTypes.cpp LuaTools.cpp diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index 7f0bacc9e..d6604cdb3 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -35,6 +35,7 @@ distribution. // must be last due to MS stupidity #include "DataDefs.h" #include "DataIdentity.h" +#include "VTableInterpose.h" #include "MiscUtils.h" @@ -214,6 +215,13 @@ virtual_identity::virtual_identity(size_t size, TAllocateFn alloc, { } +virtual_identity::~virtual_identity() +{ + // Remove interpose entries, so that they don't try accessing this object later + for (int i = interpose_list.size()-1; i >= 0; i--) + interpose_list[i]->remove(); +} + /* Vtable name to identity lookup. */ static std::map name_lookup; diff --git a/library/VTableInterpose.cpp b/library/VTableInterpose.cpp new file mode 100644 index 000000000..447070624 --- /dev/null +++ b/library/VTableInterpose.cpp @@ -0,0 +1,238 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "Internal.h" + +#include +#include +#include + +#include "MemAccess.h" +#include "Core.h" +#include "VersionInfo.h" +#include "VTableInterpose.h" + +#include "MiscUtils.h" + +using namespace DFHack; + +/* + * Code for accessing method pointers directly. Very compiler-specific. + */ + +#if defined(_MSC_VER) + +struct MSVC_MPTR { + void *method; + intptr_t this_shift; +}; + +bool DFHack::is_vmethod_pointer_(void *pptr) +{ + auto pobj = (MSVC_MPTR*)pptr; + if (!pobj->method) return false; + + // MSVC implements pointers to vmethods via thunks. + // This expects that they all follow a very specific pattern. + auto pval = (unsigned*)pobj->method; + switch (pval[0]) { + case 0x60FF018BU: // mov eax, [ecx]; jmp [eax+0x??] + case 0xA0FF018BU: // mov eax, [ecx]; jmp [eax+0x????????] + return true; + default: + return false; + } +} + +int DFHack::vmethod_pointer_to_idx_(void *pptr) +{ + auto pobj = (MSVC_MPTR*)pptr; + if (!pobj->method || pobj->this_shift != 0) return -1; + + auto pval = (unsigned*)pobj->method; + switch (pval[0]) { + case 0x60FF018BU: // mov eax, [ecx]; jmp [eax+0x??] + return ((int8_t)pval[1])/sizeof(void*); + case 0xA0FF018BU: // mov eax, [ecx]; jmp [eax+0x????????] + return ((int32_t)pval[1])/sizeof(void*); + default: + return -1; + } +} + +void* DFHack::method_pointer_to_addr_(void *pptr) +{ + if (is_vmethod_pointer_(pptr)) return NULL; + auto pobj = (MSVC_MPTR*)pptr; + return pobj->method; +} + +void DFHack::addr_to_method_pointer_(void *pptr, void *addr) +{ + auto pobj = (MSVC_MPTR*)pptr; + pobj->method = addr; + pobj->this_shift = 0; +} + +#elif defined(__GXX_ABI_VERSION) + +struct GCC_MPTR { + intptr_t method; + intptr_t this_shift; +}; + +bool DFHack::is_vmethod_pointer_(void *pptr) +{ + auto pobj = (GCC_MPTR*)pptr; + return (pobj->method & 1) != 0; +} + +int DFHack::vmethod_pointer_to_idx_(void *pptr) +{ + auto pobj = (GCC_MPTR*)pptr; + if ((pobj->method & 1) == 0 || pobj->this_shift != 0) + return -1; + return (pobj->method-1)/sizeof(void*); +} + +void* DFHack::method_pointer_to_addr_(void *pptr) +{ + auto pobj = (GCC_MPTR*)pptr; + if ((pobj->method & 1) != 0 || pobj->this_shift != 0) + return NULL; + return (void*)pobj->method; +} + +void DFHack::addr_to_method_pointer_(void *pptr, void *addr) +{ + auto pobj = (GCC_MPTR*)pptr; + pobj->method = (intptr_t)addr; + pobj->this_shift = 0; +} + +#else +#error Unknown compiler type +#endif + +void *virtual_identity::get_vmethod_ptr(int idx) +{ + assert(idx >= 0); + void **vtable = (void**)vtable_ptr; + if (!vtable) return NULL; + return vtable[idx]; +} + +bool virtual_identity::set_vmethod_ptr(int idx, void *ptr) +{ + assert(idx >= 0); + void **vtable = (void**)vtable_ptr; + if (!vtable) return NULL; + return Core::getInstance().p->patchMemory(&vtable[idx], &ptr, sizeof(void*)); +} + +void VMethodInterposeLinkBase::set_chain(void *chain) +{ + saved_chain = chain; + addr_to_method_pointer_(chain_mptr, chain); +} + +VMethodInterposeLinkBase::VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr) + : host(host), vmethod_idx(vmethod_idx), interpose_method(interpose_method), chain_mptr(chain_mptr), + saved_chain(NULL), next(NULL), prev(NULL) +{ +} + +VMethodInterposeLinkBase::~VMethodInterposeLinkBase() +{ + if (is_applied()) + remove(); +} + +bool VMethodInterposeLinkBase::apply() +{ + if (is_applied()) + return true; + if (!host->vtable_ptr) + return false; + + // Retrieve the current vtable entry + void *old_ptr = host->get_vmethod_ptr(vmethod_idx); + assert(old_ptr != NULL); + + // Check if there are other interpose entries for the same slot + VMethodInterposeLinkBase *old_link = NULL; + + for (int i = host->interpose_list.size()-1; i >= 0; i--) + { + if (host->interpose_list[i]->vmethod_idx != vmethod_idx) + continue; + + old_link = host->interpose_list[i]; + assert(old_link->next == NULL && old_ptr == old_link->interpose_method); + break; + } + + // Apply the new method ptr + if (!host->set_vmethod_ptr(vmethod_idx, interpose_method)) + return false; + + set_chain(old_ptr); + + // Link into the chain if any + if (old_link) + { + old_link->next = this; + prev = old_link; + } + + return true; +} + +void VMethodInterposeLinkBase::remove() +{ + if (!is_applied()) + return; + + // Remove from the list in the identity + for (int i = host->interpose_list.size()-1; i >= 0; i--) + if (host->interpose_list[i] == this) + vector_erase_at(host->interpose_list, i); + + // Remove from the chain + if (prev) + prev->next = next; + + if (next) + { + next->set_chain(saved_chain); + next->prev = prev; + } + else + { + host->set_vmethod_ptr(vmethod_idx, saved_chain); + } + + prev = next = NULL; + set_chain(NULL); +} diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 1d485156f..7903530dd 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -292,6 +292,8 @@ namespace DFHack typedef virtual_class *virtual_ptr; #endif + class DFHACK_EXPORT VMethodInterposeLinkBase; + class DFHACK_EXPORT virtual_identity : public struct_identity { static std::map known; @@ -299,6 +301,9 @@ namespace DFHack void *vtable_ptr; + friend class VMethodInterposeLinkBase; + std::vector interpose_list; + protected: virtual void doInit(Core *core); @@ -306,10 +311,14 @@ namespace DFHack bool can_allocate() { return struct_identity::can_allocate() && (vtable_ptr != NULL); } + void *get_vmethod_ptr(int index); + bool set_vmethod_ptr(int index, void *ptr); + public: virtual_identity(size_t size, TAllocateFn alloc, const char *dfhack_name, const char *original_name, virtual_identity *parent, const struct_field_info *fields); + ~virtual_identity(); virtual identity_type type() { return IDTYPE_CLASS; } @@ -337,6 +346,8 @@ namespace DFHack : (this == get(instance_ptr)); } + template static P get_vmethod_ptr(P selector); + public: bool can_instantiate() { return can_allocate(); } virtual_ptr instantiate() { return can_instantiate() ? (virtual_ptr)do_allocate() : NULL; } diff --git a/library/include/DataFuncs.h b/library/include/DataFuncs.h index 637a532f8..52039566c 100644 --- a/library/include/DataFuncs.h +++ b/library/include/DataFuncs.h @@ -75,28 +75,31 @@ namespace df { cur_lua_ostream_argument name(state); #define INSTANTIATE_RETURN_TYPE(FArgs) \ - template struct return_type { typedef RT type; }; \ - template struct return_type { typedef RT type; }; + template struct return_type { \ + typedef RT type; \ + static const bool is_method = false; \ + }; \ + template struct return_type { \ + typedef RT type; \ + typedef CT class_type; \ + static const bool is_method = true; \ + }; #define INSTANTIATE_WRAPPERS(Count, FArgs, Args, Loads) \ template struct function_wrapper { \ - static const bool is_method = false; \ static const int num_args = Count; \ static void execute(lua_State *state, int base, void (*cb) FArgs) { Loads; INVOKE_VOID(cb Args); } \ }; \ template struct function_wrapper { \ - static const bool is_method = false; \ static const int num_args = Count; \ static void execute(lua_State *state, int base, RT (*cb) FArgs) { Loads; INVOKE_RV(cb Args); } \ }; \ template struct function_wrapper { \ - static const bool is_method = true; \ static const int num_args = Count+1; \ static void execute(lua_State *state, int base, void (CT::*cb) FArgs) { \ LOAD_CLASS() Loads; INVOKE_VOID((self->*cb) Args); } \ }; \ template struct function_wrapper { \ - static const bool is_method = true; \ static const int num_args = Count+1; \ static void execute(lua_State *state, int base, RT (CT::*cb) FArgs) { \ LOAD_CLASS(); Loads; INVOKE_RV((self->*cb) Args); } \ diff --git a/library/include/VTableInterpose.h b/library/include/VTableInterpose.h new file mode 100644 index 000000000..59a5c2831 --- /dev/null +++ b/library/include/VTableInterpose.h @@ -0,0 +1,158 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#pragma once + +#include "DataFuncs.h" + +namespace DFHack +{ + template struct StaticAssert; + template<> struct StaticAssert {}; + +#define STATIC_ASSERT(condition) { StaticAssert<(condition)>(); } + + /* Wrapping around compiler-specific representation of pointers to methods. */ + +#if defined(_MSC_VER) +#define METHOD_POINTER_SIZE (sizeof(void*)*2) +#elif defined(__GXX_ABI_VERSION) +#define METHOD_POINTER_SIZE (sizeof(void*)*2) +#else +#error Unknown compiler type +#endif + +#define ASSERT_METHOD_POINTER(type) \ + STATIC_ASSERT(df::return_type::is_method && sizeof(type)==METHOD_POINTER_SIZE); + + DFHACK_EXPORT bool is_vmethod_pointer_(void*); + DFHACK_EXPORT int vmethod_pointer_to_idx_(void*); + DFHACK_EXPORT void* method_pointer_to_addr_(void*); + DFHACK_EXPORT void addr_to_method_pointer_(void*,void*); + + template bool is_vmethod_pointer(T ptr) { + ASSERT_METHOD_POINTER(T); + return is_vmethod_pointer_(&ptr); + } + template int vmethod_pointer_to_idx(T ptr) { + ASSERT_METHOD_POINTER(T); + return vmethod_pointer_to_idx_(&ptr); + } + template void *method_pointer_to_addr(T ptr) { + ASSERT_METHOD_POINTER(T); + return method_pointer_to_addr_(&ptr); + } + template T addr_to_method_pointer(void *addr) { + ASSERT_METHOD_POINTER(T); + T rv; + addr_to_method_pointer_(&rv, addr); + return rv; + } + + /* Access to vmethod pointers from the vtable. */ + + template + P virtual_identity::get_vmethod_ptr(P selector) + { + typedef typename df::return_type

    ::class_type host_class; + virtual_identity &identity = host_class::_identity; + int idx = vmethod_pointer_to_idx(selector); + return addr_to_method_pointer

    (identity.get_vmethod_ptr(idx)); + } + + /* VMethod interpose API + + Usage: + + struct my_hack : public someclass { + typedef someclass interpose_base; + + DEFINE_VMETHOD_INTERPOSE(void, foo, (int arg)) { + ... + INTERPOSE_NEXT(foo)(arg) // call the original + ... + } + }; + + IMPLEMENT_VMETHOD_INTERPOSE(my_hack, foo); + + void init() { + my_hack::interpose_foo.apply() + } + */ + +#define DEFINE_VMETHOD_INTERPOSE(rtype, name, args) \ + typedef rtype (interpose_base::*interpose_ptr_##name)args; \ + static DFHack::VMethodInterposeLink interpose_##name; \ + rtype interpose_fn_##name args + +#define IMPLEMENT_VMETHOD_INTERPOSE(class,name) \ + DFHack::VMethodInterposeLink \ + class::interpose_##name(&class::interpose_base::name, &class::interpose_fn_##name); + +#define INTERPOSE_NEXT(name) (this->*interpose_##name.chain) + + class DFHACK_EXPORT VMethodInterposeLinkBase { + /* + These link objects try to: + 1) Allow multiple hooks into the same vmethod + 2) Auto-remove hooks when a plugin is unloaded. + */ + + virtual_identity *host; // Class with the vtable + int vmethod_idx; + void *interpose_method; // Pointer to the code of the interposing method + void *chain_mptr; // Pointer to the chain field below + + void *saved_chain; // Previous pointer to the code + VMethodInterposeLinkBase *next, *prev; // Other hooks for the same method + + void set_chain(void *chain); + public: + VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr); + ~VMethodInterposeLinkBase(); + + bool is_applied() { return saved_chain != NULL; } + bool apply(); + void remove(); + }; + + template + class VMethodInterposeLink : public VMethodInterposeLinkBase { + public: + Ptr chain; + + operator Ptr () { return chain; } + + template + VMethodInterposeLink(Ptr target, Ptr2 src) + : VMethodInterposeLinkBase( + &Base::_identity, + vmethod_pointer_to_idx(target), + method_pointer_to_addr(src), + &chain + ) + { src = target; /* check compatibility */ } + }; +} diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index 5d1d585ab..8274accfb 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -17,3 +17,4 @@ DFHACK_PLUGIN(stockcheck stockcheck.cpp) DFHACK_PLUGIN(stripcaged stripcaged.cpp) DFHACK_PLUGIN(rprobe rprobe.cpp) DFHACK_PLUGIN(nestboxes nestboxes.cpp) +DFHACK_PLUGIN(vshook vshook.cpp) diff --git a/plugins/devel/vshook.cpp b/plugins/devel/vshook.cpp new file mode 100644 index 000000000..28f983624 --- /dev/null +++ b/plugins/devel/vshook.cpp @@ -0,0 +1,61 @@ +#include "Core.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "df/graphic.h" +#include "df/viewscreen_titlest.h" + +using std::vector; +using std::string; +using std::stack; +using namespace DFHack; + +using df::global::gps; + +DFHACK_PLUGIN("vshook"); + +struct title_hook : df::viewscreen_titlest { + typedef df::viewscreen_titlest interpose_base; + + DEFINE_VMETHOD_INTERPOSE(void, render, ()) + { + INTERPOSE_NEXT(render)(); + + if (gps) { + uint8_t *buf = gps->screen; + int32_t *stp = gps->screentexpos; + + for (const char *p = "DFHack " DFHACK_VERSION; *p; p++) { + *buf++ = *p; *buf++ = 7; *buf++ = 0; *buf++ = 1; + *stp++ = 0; + } + } + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(title_hook, render); + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + if (gps) + { + if (!title_hook::interpose_render.apply()) + out.printerr("Could not interpose viewscreen_titlest::render\n"); + } + + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + title_hook::interpose_render.remove(); + return CR_OK; +} From 01ba2a31fc2dd7597ab6e2db3f1496b123e10720 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 18 Aug 2012 11:48:07 +0400 Subject: [PATCH 35/92] Tweak the interpose API, and fix a couple of bugs. --- library/VTableInterpose.cpp | 2 ++ library/include/VTableInterpose.h | 20 ++++++++++++++++---- plugins/devel/vshook.cpp | 13 ++++++++----- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/library/VTableInterpose.cpp b/library/VTableInterpose.cpp index 447070624..f612ba8bd 100644 --- a/library/VTableInterpose.cpp +++ b/library/VTableInterpose.cpp @@ -161,6 +161,7 @@ VMethodInterposeLinkBase::VMethodInterposeLinkBase(virtual_identity *host, int v : host(host), vmethod_idx(vmethod_idx), interpose_method(interpose_method), chain_mptr(chain_mptr), saved_chain(NULL), next(NULL), prev(NULL) { + assert(vmethod_idx >= 0 && interpose_method != NULL); } VMethodInterposeLinkBase::~VMethodInterposeLinkBase() @@ -198,6 +199,7 @@ bool VMethodInterposeLinkBase::apply() return false; set_chain(old_ptr); + host->interpose_list.push_back(this); // Link into the chain if any if (old_link) diff --git a/library/include/VTableInterpose.h b/library/include/VTableInterpose.h index 59a5c2831..5eaeaed85 100644 --- a/library/include/VTableInterpose.h +++ b/library/include/VTableInterpose.h @@ -81,12 +81,18 @@ namespace DFHack return addr_to_method_pointer

    (identity.get_vmethod_ptr(idx)); } - /* VMethod interpose API + /* VMethod interpose API. + + This API allows replacing an entry in the original vtable + with code defined by DFHack, while retaining ability to + call the original code. The API can be safely used from + plugins, and multiple hooks for the same vmethod are + automatically chained in undefined order. Usage: - struct my_hack : public someclass { - typedef someclass interpose_base; + struct my_hack : df::someclass { + typedef df::someclass interpose_base; DEFINE_VMETHOD_INTERPOSE(void, foo, (int arg)) { ... @@ -98,7 +104,12 @@ namespace DFHack IMPLEMENT_VMETHOD_INTERPOSE(my_hack, foo); void init() { - my_hack::interpose_foo.apply() + if (!INTERPOSE_HOOK(my_hack, foo).apply()) + error(); + } + + void shutdown() { + INTERPOSE_HOOK(my_hack, foo).remove(); } */ @@ -112,6 +123,7 @@ namespace DFHack class::interpose_##name(&class::interpose_base::name, &class::interpose_fn_##name); #define INTERPOSE_NEXT(name) (this->*interpose_##name.chain) +#define INTERPOSE_HOOK(class, name) (class::interpose_##name) class DFHACK_EXPORT VMethodInterposeLinkBase { /* diff --git a/plugins/devel/vshook.cpp b/plugins/devel/vshook.cpp index 28f983624..319ff0e99 100644 --- a/plugins/devel/vshook.cpp +++ b/plugins/devel/vshook.cpp @@ -33,9 +33,12 @@ struct title_hook : df::viewscreen_titlest { uint8_t *buf = gps->screen; int32_t *stp = gps->screentexpos; - for (const char *p = "DFHack " DFHACK_VERSION; *p; p++) { - *buf++ = *p; *buf++ = 7; *buf++ = 0; *buf++ = 1; - *stp++ = 0; + for (const char *p = "DFHack " DFHACK_VERSION; + *p && buf < gps->screen_limit; + p++, buf += gps->dimy*4, stp += gps->dimy) + { + buf[0] = *p; buf[1] = 7; buf[2] = 0; buf[3] = 1; + *stp = 0; } } } @@ -47,7 +50,7 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector Date: Sat, 18 Aug 2012 11:52:38 +0400 Subject: [PATCH 36/92] Expose an API to claim the suspend lock from the Core. Previously it was hard-coded in Core::Update, but interposed vmethods may need this feature too. --- library/Core.cpp | 81 ++++++++++++++++++++----------- library/include/Core.h | 21 ++++++++ library/include/VTableInterpose.h | 3 ++ 3 files changed, 78 insertions(+), 27 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index f129d9523..bf0b3be70 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1027,35 +1027,41 @@ int Core::TileUpdate() return true; } -// should always be from simulation thread! -int Core::Update() +int Core::ClaimSuspend(bool force_base) { - if(errorstate) - return -1; + auto tid = this_thread::get_id(); + lock_guard lock(d->AccessMutex); - // Pretend this thread has suspended the core in the usual way + if (force_base || d->df_suspend_depth <= 0) { - lock_guard lock(d->AccessMutex); - assert(d->df_suspend_depth == 0); - d->df_suspend_thread = this_thread::get_id(); - d->df_suspend_depth = 1000; - } - // Initialize the core - bool first_update = false; - - if(!started) + d->df_suspend_thread = tid; + d->df_suspend_depth = 1000000; + return 1000000; + } + else { - first_update = true; - Init(); - if(errorstate) - return -1; - Lua::Core::Reset(con, "core init"); + assert(d->df_suspend_thread == tid); + return ++d->df_suspend_depth; } +} - color_ostream_proxy out(con); +void Core::DisclaimSuspend(int level) +{ + auto tid = this_thread::get_id(); + lock_guard lock(d->AccessMutex); + + assert(d->df_suspend_depth == level && d->df_suspend_thread == tid); + + if (level == 1000000) + d->df_suspend_depth = 0; + else + --d->df_suspend_depth; +} +void Core::doUpdate(color_ostream &out, bool first_update) +{ Lua::Core::Reset(out, "DF code execution"); if (first_update) @@ -1129,15 +1135,36 @@ int Core::Update() // Execute per-frame handlers onUpdate(out); - // Release the fake suspend lock + out << std::flush; +} + +// should always be from simulation thread! +int Core::Update() +{ + if(errorstate) + return -1; + + color_ostream_proxy out(con); + + // Pretend this thread has suspended the core in the usual way, + // and run various processing hooks. { - lock_guard lock(d->AccessMutex); + CoreSuspendClaimer suspend(true); - assert(d->df_suspend_depth == 1000); - d->df_suspend_depth = 0; - } + // Initialize the core + bool first_update = false; - out << std::flush; + if(!started) + { + first_update = true; + Init(); + if(errorstate) + return -1; + Lua::Core::Reset(con, "core init"); + } + + doUpdate(out, first_update); + } // wake waiting tools // do not allow more tools to join in while we process stuff here @@ -1158,7 +1185,7 @@ int Core::Update() // destroy condition delete nc; // check lua stack depth - Lua::Core::Reset(con, "suspend"); + Lua::Core::Reset(out, "suspend"); } return 0; diff --git a/library/include/Core.h b/library/include/Core.h index d25beef5f..e1f1cf3fb 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -174,6 +174,10 @@ namespace DFHack struct Private; Private *d; + friend class CoreSuspendClaimer; + int ClaimSuspend(bool force_base); + void DisclaimSuspend(int level); + bool Init(); int Update (void); int TileUpdate (void); @@ -181,6 +185,7 @@ namespace DFHack int DFH_SDL_Event(SDL::Event* event); bool ncurses_wgetch(int in, int & out); + void doUpdate(color_ostream &out, bool first_update); void onUpdate(color_ostream &out); void onStateChange(color_ostream &out, state_change_event event); @@ -249,4 +254,20 @@ namespace DFHack CoreSuspender(Core *core) : core(core) { core->Suspend(); } ~CoreSuspender() { core->Resume(); } }; + + /** Claims the current thread already has the suspend lock. + * Strictly for use in callbacks from DF. + */ + class CoreSuspendClaimer { + Core *core; + int level; + public: + CoreSuspendClaimer(bool base = false) : core(&Core::getInstance()) { + level = core->ClaimSuspend(base); + } + CoreSuspendClaimer(Core *core, bool base = false) : core(core) { + level = core->ClaimSuspend(base); + } + ~CoreSuspendClaimer() { core->DisclaimSuspend(level); } + }; } diff --git a/library/include/VTableInterpose.h b/library/include/VTableInterpose.h index 5eaeaed85..bb7a37ce8 100644 --- a/library/include/VTableInterpose.h +++ b/library/include/VTableInterpose.h @@ -95,6 +95,9 @@ namespace DFHack typedef df::someclass interpose_base; DEFINE_VMETHOD_INTERPOSE(void, foo, (int arg)) { + // If needed by the code, claim the suspend lock. + // DO NOT USE THE USUAL CoreSuspender, OR IT WILL DEADLOCK! + // CoreSuspendClaimer suspend; ... INTERPOSE_NEXT(foo)(arg) // call the original ... From c6694e386fa3a01bae34991a503b22fb4055e757 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 18 Aug 2012 14:34:20 +0400 Subject: [PATCH 37/92] Add rather inefficient type_identity wrapping for std::set. Accessing an element is O(N), as if it was walking a list. --- library/LuaTypes.cpp | 3 +++ library/include/DataDefs.h | 1 + library/include/DataIdentity.h | 40 ++++++++++++++++++++++++++++++++++ library/xml | 2 +- 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp index 8548c5d0a..53523c3fa 100644 --- a/library/LuaTypes.cpp +++ b/library/LuaTypes.cpp @@ -285,6 +285,9 @@ void container_identity::lua_item_read(lua_State *state, int fname_idx, void *pt void container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) { + if (is_readonly()) + field_error(state, fname_idx, "container is read-only", "write"); + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); void *pitem = item_pointer(id, ptr, idx); id->lua_write(state, fname_idx, pitem, val_index); diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 7903530dd..ccb29b0e7 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -28,6 +28,7 @@ distribution. #include #include #include +#include #include "Core.h" #include "BitArray.h" diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index dcd0ae979..0f5fd9e7c 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -115,6 +115,8 @@ namespace DFHack virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); + virtual bool is_readonly() { return false; } + virtual bool resize(void *ptr, int size) { return false; } virtual bool erase(void *ptr, int index) { return false; } virtual bool insert(void *ptr, int index, void *pitem) { return false; } @@ -343,6 +345,33 @@ namespace df } }; + template + class ro_stl_container_identity : public container_identity { + const char *name; + + public: + ro_stl_container_identity(const char *name, type_identity *item, enum_identity *ienum = NULL) + : container_identity(sizeof(T), &allocator_fn, item, ienum), name(name) + {} + + std::string getFullName(type_identity *item) { + return name + container_identity::getFullName(item); + } + + virtual bool is_readonly() { return true; } + virtual bool resize(void *ptr, int size) { return false; } + virtual bool erase(void *ptr, int size) { return false; } + virtual bool insert(void *ptr, int idx, void *item) { return false; } + + protected: + virtual int item_count(void *ptr, CountMode) { return ((T*)ptr)->size(); } + virtual void *item_pointer(type_identity *item, void *ptr, int idx) { + auto iter = (*(T*)ptr).begin(); + for (; idx > 0; idx--) ++iter; + return (void*)&*iter; + } + }; + class bit_array_identity : public bit_container_identity { public: /* @@ -517,6 +546,10 @@ namespace df static container_identity *get(); }; + template struct identity_traits > { + static container_identity *get(); + }; + template<> struct identity_traits > { static bit_array_identity identity; static bit_container_identity *get() { return &identity; } @@ -579,6 +612,13 @@ namespace df return &identity; } + template + inline container_identity *identity_traits >::get() { + typedef std::set container; + static ro_stl_container_identity identity("set", identity_traits::get()); + return &identity; + } + template inline bit_container_identity *identity_traits >::get() { static bit_array_identity identity(identity_traits::get()); diff --git a/library/xml b/library/xml index 9f91e7476..1eeaa0836 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 9f91e74767b4d583b580d46e16143216ba62ae66 +Subproject commit 1eeaa08360c39a9a2d811544c2443309adc1a8f1 From 8ed219d4e0cbea19c8a9489294a975b81946bba2 Mon Sep 17 00:00:00 2001 From: Quietust Date: Sat, 18 Aug 2012 19:21:40 -0500 Subject: [PATCH 38/92] Add "clean plants", currently just removes water from rain (and lets you set them on fire) --- plugins/cleaners.cpp | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/plugins/cleaners.cpp b/plugins/cleaners.cpp index 30befab2f..de204f611 100644 --- a/plugins/cleaners.cpp +++ b/plugins/cleaners.cpp @@ -114,6 +114,28 @@ command_result cleanunits (color_ostream &out) return CR_OK; } +command_result cleanplants (color_ostream &out) +{ + // Invoked from clean(), already suspended + int cleaned_plants = 0, cleaned_total = 0; + for (size_t i = 0; i < world->plants.all.size(); i++) + { + df::plant *plant = world->plants.all[i]; + + if (plant->contaminants.size()) + { + for (size_t j = 0; j < plant->contaminants.size(); j++) + delete plant->contaminants[j]; + cleaned_plants++; + cleaned_total += plant->contaminants.size(); + plant->contaminants.clear(); + } + } + if (cleaned_total) + out.print("Removed %d contaminants from %d plants.\n", cleaned_total, cleaned_plants); + return CR_OK; +} + command_result spotclean (color_ostream &out, vector & parameters) { // HOTKEY COMMAND: CORE ALREADY SUSPENDED @@ -153,6 +175,7 @@ command_result clean (color_ostream &out, vector & parameters) bool mud = false; bool units = false; bool items = false; + bool plants = false; for(size_t i = 0; i < parameters.size();i++) { if(parameters[i] == "map") @@ -161,11 +184,14 @@ command_result clean (color_ostream &out, vector & parameters) units = true; else if(parameters[i] == "items") items = true; + else if(parameters[i] == "plants") + plants = true; else if(parameters[i] == "all") { map = true; items = true; units = true; + plants = true; } else if(parameters[i] == "snow") snow = true; @@ -174,7 +200,7 @@ command_result clean (color_ostream &out, vector & parameters) else return CR_WRONG_USAGE; } - if(!map && !units && !items) + if(!map && !units && !items && !plants) return CR_WRONG_USAGE; CoreSuspender suspend; @@ -185,6 +211,8 @@ command_result clean (color_ostream &out, vector & parameters) cleanunits(out); if(items) cleanitems(out); + if(plants) + cleanplants(out); return CR_OK; } @@ -198,6 +226,7 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector Date: Sun, 19 Aug 2012 09:21:25 +0400 Subject: [PATCH 39/92] Extract the color enum from color_ostream to toplevel. --- library/Console-darwin.cpp | 2 +- library/Console-linux.cpp | 2 +- library/Console-windows.cpp | 2 +- library/Core.cpp | 6 ++--- library/LuaTools.cpp | 2 +- library/include/ColorText.h | 44 +++++++++++++++++++------------------ library/modules/Job.cpp | 2 +- plugins/advtools.cpp | 2 +- plugins/devel/kittens.cpp | 6 ++--- plugins/devel/memview.cpp | 2 +- plugins/workflow.cpp | 20 ++++++++--------- 11 files changed, 46 insertions(+), 44 deletions(-) diff --git a/library/Console-darwin.cpp b/library/Console-darwin.cpp index c547f8413..86cd657a1 100644 --- a/library/Console-darwin.cpp +++ b/library/Console-darwin.cpp @@ -275,7 +275,7 @@ namespace DFHack /// Reset color to default void reset_color(void) { - color(Console::COLOR_RESET); + color(COLOR_RESET); if(!rawmode) fflush(dfout_C); } diff --git a/library/Console-linux.cpp b/library/Console-linux.cpp index 882d9527d..f32fa1c2a 100644 --- a/library/Console-linux.cpp +++ b/library/Console-linux.cpp @@ -277,7 +277,7 @@ namespace DFHack /// Reset color to default void reset_color(void) { - color(Console::COLOR_RESET); + color(COLOR_RESET); if(!rawmode) fflush(dfout_C); } diff --git a/library/Console-windows.cpp b/library/Console-windows.cpp index d4d47303f..f0cdda387 100644 --- a/library/Console-windows.cpp +++ b/library/Console-windows.cpp @@ -179,7 +179,7 @@ namespace DFHack void color(int index) { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(hConsole, index == color_ostream::COLOR_RESET ? default_attributes : index); + SetConsoleTextAttribute(hConsole, index == COLOR_RESET ? default_attributes : index); } void reset_color( void ) diff --git a/library/Core.cpp b/library/Core.cpp index bf0b3be70..71c92afea 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -358,7 +358,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve continue; if (pcmd.isHotkeyCommand()) - con.color(Console::COLOR_CYAN); + con.color(COLOR_CYAN); con.print("%s: %s\n",pcmd.name.c_str(), pcmd.description.c_str()); con.reset_color(); if (!pcmd.usage.empty()) @@ -481,7 +481,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve { const PluginCommand & pcmd = (plug->operator[](j)); if (pcmd.isHotkeyCommand()) - con.color(Console::COLOR_CYAN); + con.color(COLOR_CYAN); con.print(" %-22s - %s\n",pcmd.name.c_str(), pcmd.description.c_str()); con.reset_color(); } @@ -519,7 +519,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve for(auto iter = out.begin();iter != out.end();iter++) { if ((*iter).recolor) - con.color(Console::COLOR_CYAN); + con.color(COLOR_CYAN); con.print(" %-22s- %s\n",(*iter).name.c_str(), (*iter).description.c_str()); con.reset_color(); } diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 28571a0f7..75dfe23f2 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -252,7 +252,7 @@ static int lua_dfhack_color(lua_State *S) { int cv = luaL_optint(S, 1, -1); - if (cv < -1 || cv > color_ostream::COLOR_MAX) + if (cv < -1 || cv > COLOR_MAX) luaL_argerror(S, 1, "invalid color value"); color_ostream *out = Lua::GetOutput(S); diff --git a/library/include/ColorText.h b/library/include/ColorText.h index 0cc286dcf..50d1f3623 100644 --- a/library/include/ColorText.h +++ b/library/include/ColorText.h @@ -41,30 +41,32 @@ namespace dfproto namespace DFHack { + enum color_value + { + COLOR_RESET = -1, + COLOR_BLACK = 0, + COLOR_BLUE, + COLOR_GREEN, + COLOR_CYAN, + COLOR_RED, + COLOR_MAGENTA, + COLOR_BROWN, + COLOR_GREY, + COLOR_DARKGREY, + COLOR_LIGHTBLUE, + COLOR_LIGHTGREEN, + COLOR_LIGHTCYAN, + COLOR_LIGHTRED, + COLOR_LIGHTMAGENTA, + COLOR_YELLOW, + COLOR_WHITE, + COLOR_MAX = COLOR_WHITE + }; + class DFHACK_EXPORT color_ostream : public std::ostream { public: - enum color_value - { - COLOR_RESET = -1, - COLOR_BLACK = 0, - COLOR_BLUE, - COLOR_GREEN, - COLOR_CYAN, - COLOR_RED, - COLOR_MAGENTA, - COLOR_BROWN, - COLOR_GREY, - COLOR_DARKGREY, - COLOR_LIGHTBLUE, - COLOR_LIGHTGREEN, - COLOR_LIGHTCYAN, - COLOR_LIGHTRED, - COLOR_LIGHTMAGENTA, - COLOR_YELLOW, - COLOR_WHITE, - COLOR_MAX = COLOR_WHITE - }; + typedef DFHack::color_value color_value; private: color_value cur_color; diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index 1207c97b3..54b4eb27e 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -189,7 +189,7 @@ void DFHack::Job::printJobDetails(color_ostream &out, df::job *job) { CHECK_NULL_POINTER(job); - out.color(job->flags.bits.suspend ? Console::COLOR_DARKGREY : Console::COLOR_GREY); + out.color(job->flags.bits.suspend ? COLOR_DARKGREY : COLOR_GREY); out << "Job " << job->id << ": " << ENUM_KEY_STR(job_type,job->job_type); if (job->flags.whole) out << " (" << bitfield_to_string(job->flags) << ")"; diff --git a/plugins/advtools.cpp b/plugins/advtools.cpp index 4823d362c..d674f5528 100644 --- a/plugins/advtools.cpp +++ b/plugins/advtools.cpp @@ -462,7 +462,7 @@ void joinCounts(std::map &counts) static void printCompanionHeader(color_ostream &out, size_t i, df::unit *unit) { - out.color(Console::COLOR_GREY); + out.color(COLOR_GREY); if (i < 28) out << char('a'+i); diff --git a/plugins/devel/kittens.cpp b/plugins/devel/kittens.cpp index 2e8e6eaba..b610d4742 100644 --- a/plugins/devel/kittens.cpp +++ b/plugins/devel/kittens.cpp @@ -257,7 +257,7 @@ command_result kittens (color_ostream &out, vector & parameters) }; con.cursor(false); con.clear(); - Console::color_value color = Console::COLOR_BLUE; + Console::color_value color = COLOR_BLUE; while(1) { if(shutdown_flag) @@ -282,7 +282,7 @@ command_result kittens (color_ostream &out, vector & parameters) con.flush(); con.msleep(60); ((int&)color) ++; - if(color > Console::COLOR_MAX) - color = Console::COLOR_BLUE; + if(color > COLOR_MAX) + color = COLOR_BLUE; } } diff --git a/plugins/devel/memview.cpp b/plugins/devel/memview.cpp index 5d8d6a9b2..757b475dd 100644 --- a/plugins/devel/memview.cpp +++ b/plugins/devel/memview.cpp @@ -73,7 +73,7 @@ void outputHex(uint8_t *buf,uint8_t *lbuf,size_t len,size_t start,color_ostream con.reset_color(); if(isAddr((uint32_t *)(buf+j+i),ranges)) - con.color(Console::COLOR_LIGHTRED); //coloring in the middle does not work + con.color(COLOR_LIGHTRED); //coloring in the middle does not work //TODO make something better? } if(lbuf[j+i]!=buf[j+i]) diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 162051242..639e7c56a 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -1380,15 +1380,15 @@ static void print_constraint(color_ostream &out, ItemConstraint *cv, bool no_job { Console::color_value color; if (cv->request_resume) - color = Console::COLOR_GREEN; + color = COLOR_GREEN; else if (cv->request_suspend) - color = Console::COLOR_CYAN; + color = COLOR_CYAN; else - color = Console::COLOR_DARKGREY; + color = COLOR_DARKGREY; out.color(color); out << prefix << "Constraint " << flush; - out.color(Console::COLOR_GREY); + out.color(COLOR_GREY); out << cv->config.val() << " " << flush; out.color(color); out << (cv->goalByCount() ? "count " : "amount ") @@ -1437,18 +1437,18 @@ static void print_constraint(color_ostream &out, ItemConstraint *cv, bool no_job { if (pj->want_resumed) { - out.color(Console::COLOR_YELLOW); + out.color(COLOR_YELLOW); out << start << " (delayed)" << endl; } else { - out.color(Console::COLOR_BLUE); + out.color(COLOR_BLUE); out << start << " (suspended)" << endl; } } else { - out.color(Console::COLOR_GREEN); + out.color(COLOR_GREEN); out << start << endl; } @@ -1472,11 +1472,11 @@ static void print_job(color_ostream &out, ProtectedJob *pj) isOptionEnabled(CF_AUTOMELT)) { if (meltable_count <= 0) - out.color(Console::COLOR_CYAN); + out.color(COLOR_CYAN); else if (pj->want_resumed && !pj->isActuallyResumed()) - out.color(Console::COLOR_YELLOW); + out.color(COLOR_YELLOW); else - out.color(Console::COLOR_GREEN); + out.color(COLOR_GREEN); out << " Meltable: " << meltable_count << " objects." << endl; out.reset_color(); } From b8ee52131bccd174f06c9124980bbcb8df7c80e4 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 19 Aug 2012 09:31:09 +0400 Subject: [PATCH 40/92] Add a module for painting tiles into the gps global. --- library/CMakeLists.txt | 2 + library/include/modules/Screen.h | 116 +++++++++++++++++ library/modules/Gui.cpp | 6 + library/modules/Screen.cpp | 215 +++++++++++++++++++++++++++++++ plugins/devel/vshook.cpp | 15 +-- 5 files changed, 342 insertions(+), 12 deletions(-) create mode 100644 library/include/modules/Screen.h create mode 100644 library/modules/Screen.cpp diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index cd3d52c8c..109a97e7c 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -119,6 +119,7 @@ include/modules/Maps.h include/modules/MapCache.h include/modules/Materials.h include/modules/Notes.h +include/modules/Screen.h include/modules/Translation.h include/modules/Vegetation.h include/modules/Vermin.h @@ -139,6 +140,7 @@ modules/kitchen.cpp modules/Maps.cpp modules/Materials.cpp modules/Notes.cpp +modules/Screen.cpp modules/Translation.cpp modules/Vegetation.cpp modules/Vermin.cpp diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h new file mode 100644 index 000000000..7c5c8ba5e --- /dev/null +++ b/library/include/modules/Screen.h @@ -0,0 +1,116 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#pragma once +#include "Export.h" +#include "Module.h" +#include "BitArray.h" +#include "ColorText.h" +#include + +#include "DataDefs.h" +#include "df/graphic.h" +#include "df/viewscreen.h" + +/** + * \defgroup grp_screen utilities for painting to the screen + * @ingroup grp_screen + */ + +namespace DFHack +{ + class Core; + + /** + * The Screen module + * \ingroup grp_modules + * \ingroup grp_screen + */ + namespace Screen + { + /// Data structure describing all properties a screen tile can have + struct Pen { + // Ordinary text symbol + char ch; + int8_t fg, bg; + bool bold; + + // Graphics tile + int tile; + enum TileMode { + AsIs, // Tile colors used without modification + CharColor, // The fg/bg pair is used + TileColor // The fields below are used + } tile_mode; + int8_t tile_fg, tile_bg; + + Pen(char ch = 0, int8_t fg = 7, int8_t bg = 0, int tile = 0, bool color_tile = false) + : ch(ch), fg(fg&7), bg(bg), bold(!!(fg&8)), + tile(tile), tile_mode(color_tile ? CharColor : AsIs), tile_fg(0), tile_bg(0) + {} + Pen(char ch, int8_t fg, int8_t bg, bool bold, int tile = 0, bool color_tile = false) + : ch(ch), fg(fg), bg(bg), bold(bold), + tile(tile), tile_mode(color_tile ? CharColor : AsIs), tile_fg(0), tile_bg(0) + {} + Pen(char ch, int8_t fg, int8_t bg, int tile, int8_t tile_fg, int8_t tile_bg) + : ch(ch), fg(fg&7), bg(bg), bold(!!(fg&8)), + tile(tile), tile_mode(TileColor), tile_fg(tile_fg), tile_bg(tile_bg) + {} + Pen(char ch, int8_t fg, int8_t bg, bool bold, int tile, int8_t tile_fg, int8_t tile_bg) + : ch(ch), fg(fg), bg(bg), bold(bold), + tile(tile), tile_mode(TileColor), tile_fg(tile_fg), tile_bg(tile_bg) + {} + }; + + DFHACK_EXPORT df::coord2d getMousePos(); + DFHACK_EXPORT df::coord2d getWindowSize(); + + /// Paint one screen tile with the given pen + DFHACK_EXPORT bool paintTile(const Pen &pen, int x, int y); + + /// Paint a string onto the screen. Ignores ch and tile of pen. + DFHACK_EXPORT bool paintString(const Pen &pen, int x, int y, const std::string &text); + + /// Fills a rectangle with one pen. Possibly more efficient than a loop over paintTile. + DFHACK_EXPORT bool fillRect(const Pen &pen, int x1, int y1, int x2, int y2); + + /// Find a loaded graphics tile from graphics raws. + DFHACK_EXPORT bool findGraphicsTile(const std::string &page, int x, int y, int *ptile, int *pgs = NULL); + + // Push and remove viewscreens + DFHACK_EXPORT bool show(df::viewscreen *screen, df::viewscreen *before = NULL); + DFHACK_EXPORT void dismiss(df::viewscreen *screen); + DFHACK_EXPORT bool isDismissed(df::viewscreen *screen); + } + + class DFHACK_EXPORT dfhack_viewscreen : public df::viewscreen { + public: + dfhack_viewscreen(); + virtual ~dfhack_viewscreen(); + + static bool is_instance(df::viewscreen *screen); + + virtual std::string getFocusString() = 0; + }; +} diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index cd44401fe..9d3ee96eb 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -42,6 +42,7 @@ using namespace std; using namespace DFHack; #include "modules/Job.h" +#include "modules/Screen.h" #include "DataDefs.h" #include "df/world.h" @@ -466,6 +467,11 @@ std::string Gui::getFocusString(df::viewscreen *top) return name; } + else if (dfhack_viewscreen::is_instance(top)) + { + auto name = static_cast(top)->getFocusString(); + return name.empty() ? "dfhack" : "dfhack/"+name; + } else { Core &core = Core::getInstance(); diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp new file mode 100644 index 000000000..f6155bd6f --- /dev/null +++ b/library/modules/Screen.cpp @@ -0,0 +1,215 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#include "Internal.h" + +#include +#include +#include +using namespace std; + +#include "modules/Screen.h" +#include "MemAccess.h" +#include "VersionInfo.h" +#include "Types.h" +#include "Error.h" +#include "ModuleFactory.h" +#include "Core.h" +#include "PluginManager.h" +#include "MiscUtils.h" +using namespace DFHack; + +#include "DataDefs.h" +#include "df/init.h" +#include "df/texture_handler.h" +#include "df/tile_page.h" +#include "df/interfacest.h" + +using namespace df::enums; +using df::global::init; +using df::global::gps; +using df::global::texture; +using df::global::gview; + +using Screen::Pen; + +df::coord2d Screen::getMousePos() +{ + if (!gps) return df::coord2d(); + + return df::coord2d(gps->mouse_x, gps->mouse_y); +} + +df::coord2d Screen::getWindowSize() +{ + if (!gps) return df::coord2d(); + + return df::coord2d(gps->dimx, gps->dimy); +} + +static void doSetTile(const Pen &pen, int index) +{ + auto screen = gps->screen + index*4; + screen[0] = uint8_t(pen.ch); + screen[1] = uint8_t(pen.fg) & 15; + screen[2] = uint8_t(pen.bg) & 15; + screen[3] = uint8_t(pen.bold) & 1; + gps->screentexpos[index] = pen.tile; + gps->screentexpos_addcolor[index] = (pen.tile_mode == Screen::Pen::CharColor); + gps->screentexpos_grayscale[index] = (pen.tile_mode == Screen::Pen::TileColor); + gps->screentexpos_cf[index] = pen.tile_fg; + gps->screentexpos_cbr[index] = pen.tile_bg; +} + +bool Screen::paintTile(const Pen &pen, int x, int y) +{ + if (!gps) return false; + + int dimx = gps->dimx, dimy = gps->dimy; + if (x < 0 || x >= dimx || y < 0 || y >= dimy) return false; + + doSetTile(pen, x*dimy + y); + return true; +} + +bool Screen::paintString(const Pen &pen, int x, int y, const std::string &text) +{ + if (!gps || y < 0 || y >= gps->dimy) return false; + + Pen tmp(pen); + bool ok = false; + + for (size_t i = -std::min(0,x); i < text.size(); i++) + { + if (x + i >= size_t(gps->dimx)) + break; + + tmp.ch = text[i]; + tmp.tile = (pen.tile ? pen.tile + uint8_t(text[i]) : 0); + paintTile(tmp, x+i, y); + ok = true; + } + + return ok; +} + +bool Screen::fillRect(const Pen &pen, int x1, int y1, int x2, int y2) +{ + if (!gps) return false; + + if (x1 < 0) x1 = 0; + if (y1 < 0) y1 = 0; + if (x2 >= gps->dimx) x2 = gps->dimx-1; + if (y2 >= gps->dimy) y2 = gps->dimy-1; + if (x1 > x2 || y1 > y2) return false; + + for (int x = x1; x <= x2; x++) + { + int index = x*gps->dimy; + + for (int y = y1; y <= y2; y++) + doSetTile(pen, index+y); + } + + return true; +} + +bool Screen::findGraphicsTile(const std::string &pagename, int x, int y, int *ptile, int *pgs) +{ + if (!gps || !texture || x < 0 || y < 0) return false; + + for (size_t i = 0; i < texture->page.size(); i++) + { + auto page = texture->page[i]; + if (!page->loaded || page->token != pagename) continue; + + if (x >= page->page_dim_x || y >= page->page_dim_y) + break; + int idx = y*page->page_dim_x + x; + if (size_t(idx) >= page->texpos.size()) + break; + + if (ptile) *ptile = page->texpos[idx]; + if (pgs) *pgs = page->texpos_gs[idx]; + return true; + } + + return false; +} + +bool Screen::show(df::viewscreen *screen, df::viewscreen *before) +{ + CHECK_NULL_POINTER(screen); + CHECK_INVALID_ARGUMENT(!screen->parent && !screen->child); + + if (!gps || !gview) return false; + + df::viewscreen *parent = &gview->view; + while (parent && parent->child != before) + parent = parent->child; + + if (!parent) return false; + + gps->force_full_display_count += 2; + + screen->child = parent->child; + screen->parent = parent; + parent->child = screen; + if (screen->child) + screen->child->parent = screen; + + return true; +} + +void Screen::dismiss(df::viewscreen *screen) +{ + CHECK_NULL_POINTER(screen); + + screen->breakdown_level = interface_breakdown_types::STOPSCREEN; +} + +bool Screen::isDismissed(df::viewscreen *screen) +{ + CHECK_NULL_POINTER(screen); + + return screen->breakdown_level != interface_breakdown_types::NONE; +} + +static std::set dfhack_screens; + +dfhack_viewscreen::dfhack_viewscreen() +{ + dfhack_screens.insert(this); +} + +dfhack_viewscreen::~dfhack_viewscreen() +{ + dfhack_screens.erase(this); +} + +bool dfhack_viewscreen::is_instance(df::viewscreen *screen) +{ + return dfhack_screens.count(screen) != 0; +} diff --git a/plugins/devel/vshook.cpp b/plugins/devel/vshook.cpp index 319ff0e99..ceec2d08d 100644 --- a/plugins/devel/vshook.cpp +++ b/plugins/devel/vshook.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -29,18 +30,8 @@ struct title_hook : df::viewscreen_titlest { { INTERPOSE_NEXT(render)(); - if (gps) { - uint8_t *buf = gps->screen; - int32_t *stp = gps->screentexpos; - - for (const char *p = "DFHack " DFHACK_VERSION; - *p && buf < gps->screen_limit; - p++, buf += gps->dimy*4, stp += gps->dimy) - { - buf[0] = *p; buf[1] = 7; buf[2] = 0; buf[3] = 1; - *stp = 0; - } - } + Screen::Pen pen(' ',COLOR_WHITE,COLOR_BLACK); + Screen::paintString(pen,0,0,"DFHack " DFHACK_VERSION); } }; From 30f71ff5106d271d04bfa26b976441cfa9b2abf6 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 19 Aug 2012 14:27:44 +0400 Subject: [PATCH 41/92] Implement support for lua-backed viewscreens. --- LUA_API.rst | 118 ++++++++++++++ Lua API.html | 153 +++++++++++++++--- library/LuaApi.cpp | 148 +++++++++++++++++ library/LuaTypes.cpp | 22 +++ library/PluginManager.cpp | 2 +- library/include/LuaTools.h | 10 ++ library/include/modules/Screen.h | 37 +++++ library/lua/dfhack.lua | 6 + library/modules/Screen.cpp | 262 +++++++++++++++++++++++++++++++ 9 files changed, 734 insertions(+), 24 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 449f92b46..a59be7430 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -1204,6 +1204,124 @@ Constructions module Returns *true, was_only_planned* if removed; or *false* if none found. +Screen API +---------- + +The screen module implements support for drawing to the tiled screen of the game. +Note that drawing only has any effect when done from callbacks, so it can only +be feasibly used in the core context. + +* ``dfhack.screen.getWindowSize()`` + + Returns *width, height* of the screen. + +* ``dfhack.screen.getMousePos()`` + + Returns *x,y* of the tile the mouse is over. + +* ``dfhack.screen.paintTile(pen,x,y[,char,tile])`` + + Paints a tile using given parameters. Pen is a table with following possible fields: + + ``ch`` + Provides the ordinary tile character. Can be overridden with the ``char`` function parameter. + ``fg`` + Foreground color for the ordinary tile. Defaults to 7. + ``bg`` + Background color for the ordinary tile. Defaults to 0. + ``bold`` + Bright/bold text flag. If *nil*, computed based on (fg & 8); fg is reset to 7 bits. + Otherwise should be *true/false*. + ``tile`` + Graphical tile id. Ignored unless [GRAPHICS:YES] in init.txt. + ``tile_color = true`` + Specifies that the tile should be shaded with *fg/bg*. + ``tile_fg, tile_bg`` + If specified, overrides *tile_color* and supplies shading colors directly. + + Returns *false* if coordinates out of bounds, or other error. + +* ``dfhack.screen.paintString(pen,x,y,text)`` + + Paints the string starting at *x,y*. Uses the string characters + in sequence to override the ``ch`` field of pen. + + Returns *true* if painting at least one character succeeded. + +* ``dfhack.screen.fillRect(pen,x1,y1,x2,y2)`` + + Fills the rectangle specified by the coordinates with the given pen. + Returns *true* if painting at least one character succeeded. + +* ``dfhack.screen.clear()`` + + Fills the screen with blank background. + +* ``dfhack.screen.invalidate()`` + + Requests repaint of the screen by setting a flag. Unlike other + functions in this section, this may be used at any time. + +In order to actually be able to paint to the screen, it is necessary +to create and register a viewscreen (basically a modal dialog) with +the game. Screens are managed with the following functions: + +* ``dfhack.screen.show(screen[,below])`` + + Displays the given screen, possibly placing it below a different one. + The screen must not be already shown. Returns *true* if success. + +* ``dfhack.screen.dismiss(screen)`` + + Marks the screen to be removed when the game enters its event loop. + +* ``dfhack.screen.isDismissed(screen)`` + + Checks if the screen is already marked for removal. + +Apart from a native viewscreen object, these functions accept a table +as a screen. In this case, ``show`` creates a new native viewscreen +that delegates all processing to methods stored in that table. + +**NOTE**: Lua-implemented screens are only supported in the core context. + +Supported callbacks and fields are: + +* ``screen._native`` + + Initialized by ``show`` with a reference to the backing viewscreen + object, and removed again when the object is deleted. + +* ``function screen:onDestroy()`` + + Called from the destructor when the viewscreen is deleted. + +* ``function screen:onRender()`` + + Called when the viewscreen should paint itself. This is the only context + where the above painting functions work correctly. + + If omitted, the screen is cleared; otherwise it should do that itself. + In order to make a see-through dialog, call ``self._native.parent:render()``. + +* ``function screen:onIdle()`` + + Called every frame when the screen is on top of the stack. + +* ``function screen:onHelp()`` + + Called when the help keybinding is activated (usually '?'). + +* ``function screen:onInput(keys)`` + + Called when keyboard or mouse events are available. + If any keys are pressed, the keys argument is a table mapping them to *true*. + Note that this refers to logical keybingings computed from real keys via + options; if multiple interpretations exist, the table will contain multiple keys. + + If this method is omitted, the screen is dismissed on receival of the ``LEAVESCREEN`` key. + + Internal API ------------ diff --git a/Lua API.html b/Lua API.html index 7dfc7df00..ebb8a14db 100644 --- a/Lua API.html +++ b/Lua API.html @@ -3,7 +3,7 @@ - + DFHack Lua API