/** ** Draw additional informations of a unit. ** ** @param unit Unit pointer of drawn unit. ** @param type Unit-type pointer. ** @param screenPos screen pixel (top left) position of unit. ** ** @todo FIXME: The different styles should become a function call. */ static void DrawInformations(const CUnit &unit, const CUnitType &type, const PixelPos &screenPos) { #if 0 && DEBUG // This is for showing vis counts and refs. char buf[10]; sprintf(buf, "%d%c%c%d", unit.VisCount[ThisPlayer->Index], unit.Seen.ByPlayer & (1 << ThisPlayer->Index) ? 'Y' : 'N', unit.Seen.Destroyed & (1 << ThisPlayer->Index) ? 'Y' : 'N', unit.Refs); CLabel(GetSmallFont()).Draw(screenPos.x + 10, screenPos.y + 10, buf); #endif const CUnitStats &stats = *unit.Stats; // For debug draw sight, react and attack range! if (IsOnlySelected(unit)) { const PixelPos center(screenPos + type.GetPixelSize() / 2); if (Preference.ShowSightRange) { const int value = stats.Variables[SIGHTRANGE_INDEX].Max; const int radius = value * PixelTileSize.x + (type.TileWidth - 1) * PixelTileSize.x / 2; if (value) { // Radius -1 so you can see all ranges Video.DrawCircleClip(ColorGreen, center.x, center.y, radius - 1); } } if (type.CanAttack) { if (Preference.ShowReactionRange) { const int value = (unit.Player->Type == PlayerPerson) ? type.ReactRangePerson : type.ReactRangeComputer; const int radius = value * PixelTileSize.x + (type.TileWidth - 1) * PixelTileSize.x / 2; if (value) { Video.DrawCircleClip(ColorBlue, center.x, center.y, radius); } } if (Preference.ShowAttackRange) { const int value = stats.Variables[ATTACKRANGE_INDEX].Max; const int radius = value * PixelTileSize.x + (type.TileWidth - 1) * PixelTileSize.x / 2; if (value) { // Radius +1 so you can see all ranges Video.DrawCircleClip(ColorGreen, center.x, center.y, radius - 1); } } } } // FIXME: johns: ugly check here, should be removed! if (unit.CurrentAction() != UnitActionDie && (unit.IsVisible(*ThisPlayer) || ReplayRevealMap)) { DrawDecoration(unit, type, screenPos); } }
void UpdateUnitStats(CUnitType &type, int reset) { if (reset) { for (int player = 0; player < PlayerMax; ++player) { type.Stats[player] = type.DefaultStat; } } // Non-solid units can always be entered and they don't block anything if (type.NonSolid) { if (type.Building) { type.MovementMask = MapFieldLandUnit | MapFieldSeaUnit | MapFieldBuilding | MapFieldCoastAllowed | MapFieldWaterAllowed | MapFieldNoBuilding | MapFieldUnpassable; type.FieldFlags = MapFieldNoBuilding; } else { type.MovementMask = 0; type.FieldFlags = 0; } return; } // As side effect we calculate the movement flags/mask here. switch (type.UnitType) { case UnitTypeLand: // on land type.MovementMask = MapFieldLandUnit | MapFieldSeaUnit | MapFieldBuilding | // already occuppied MapFieldCoastAllowed | MapFieldWaterAllowed | // can't move on this MapFieldUnpassable; break; case UnitTypeFly: // in air type.MovementMask = MapFieldAirUnit; // already occuppied break; case UnitTypeNaval: // on water if (type.CanTransport()) { type.MovementMask = MapFieldLandUnit | MapFieldSeaUnit | MapFieldBuilding | // already occuppied MapFieldLandAllowed; // can't move on this // Johns: MapFieldUnpassable only for land units? } else { type.MovementMask = MapFieldLandUnit | MapFieldSeaUnit | MapFieldBuilding | // already occuppied MapFieldCoastAllowed | MapFieldLandAllowed | // can't move on this MapFieldUnpassable; } break; default: DebugPrint("Where moves this unit?\n"); type.MovementMask = 0; break; } if (type.Building || type.ShoreBuilding) { // Shore building is something special. if (type.ShoreBuilding) { type.MovementMask = MapFieldLandUnit | MapFieldSeaUnit | MapFieldBuilding | // already occuppied MapFieldLandAllowed; // can't build on this } type.MovementMask |= MapFieldNoBuilding; // // A little chaos, buildings without HP can be entered. // The oil-patch is a very special case. // if (type.DefaultStat.Variables[HP_INDEX].Max) { type.FieldFlags = MapFieldBuilding; } else { type.FieldFlags = MapFieldNoBuilding; } } else { switch (type.UnitType) { case UnitTypeLand: // on land type.FieldFlags = MapFieldLandUnit; break; case UnitTypeFly: // in air type.FieldFlags = MapFieldAirUnit; break; case UnitTypeNaval: // on water type.FieldFlags = MapFieldSeaUnit; break; default: DebugPrint("Where moves this unit?\n"); type.FieldFlags = 0; break; } } }
//Wyrmgus start //static int TransformUnitIntoType(CUnit &unit, const CUnitType &newtype) int TransformUnitIntoType(CUnit &unit, const CUnitType &newtype) //Wyrmgus end { const CUnitType &oldtype = *unit.Type; if (&oldtype == &newtype) { // nothing to do return 1; } const Vec2i pos = unit.tilePos + oldtype.GetHalfTileSize() - newtype.GetHalfTileSize(); CUnit *container = unit.Container; //Wyrmgus start /* if (container) { MapUnmarkUnitSight(unit); } else { SaveSelection(); unit.Remove(NULL); if (!UnitTypeCanBeAt(newtype, pos)) { unit.Place(unit.tilePos); RestoreSelection(); // FIXME unit is not modified, try later ? return 0; } } */ if (!SaveGameLoading) { if (container) { MapUnmarkUnitSight(unit); } else { SaveSelection(); unit.Remove(NULL); if (!UnitTypeCanBeAt(newtype, pos)) { unit.Place(unit.tilePos); RestoreSelection(); // FIXME unit is not modified, try later ? return 0; } } } //Wyrmgus end CPlayer &player = *unit.Player; player.UnitTypesCount[oldtype.Slot]--; player.UnitTypesCount[newtype.Slot]++; if (unit.Active) { player.UnitTypesAiActiveCount[oldtype.Slot]--; player.UnitTypesAiActiveCount[newtype.Slot]++; } //Wyrmgus start if (unit.Character == NULL) { player.UnitTypesNonHeroCount[oldtype.Slot]--; player.UnitTypesNonHeroCount[newtype.Slot]++; } //Wyrmgus end player.Demand += newtype.Stats[player.Index].Variables[DEMAND_INDEX].Value - oldtype.Stats[player.Index].Variables[DEMAND_INDEX].Value; player.Supply += newtype.Stats[player.Index].Variables[SUPPLY_INDEX].Value - oldtype.Stats[player.Index].Variables[SUPPLY_INDEX].Value; // Change resource limit for (int i = 0; i < MaxCosts; ++i) { if (player.MaxResources[i] != -1) { player.MaxResources[i] += newtype.Stats[player.Index].Storing[i] - oldtype.Stats[player.Index].Storing[i]; player.SetResource(i, player.StoredResources[i], STORE_BUILDING); } } // adjust Variables with percent. const CUnitStats &newstats = newtype.Stats[player.Index]; //Wyrmgus start const CUnitStats &oldstats = oldtype.Stats[player.Index]; //Wyrmgus end for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); ++i) { //Wyrmgus start /* if (unit.Variable[i].Max && unit.Variable[i].Value) { unit.Variable[i].Value = newstats.Variables[i].Max * unit.Variable[i].Value / unit.Variable[i].Max; } else { unit.Variable[i].Value = newstats.Variables[i].Value; } if (i == KILL_INDEX || i == XP_INDEX) { unit.Variable[i].Value = unit.Variable[i].Max; } else { unit.Variable[i].Max = newstats.Variables[i].Max; unit.Variable[i].Increase = newstats.Variables[i].Increase; unit.Variable[i].Enable = newstats.Variables[i].Enable; } */ if (i == LEVEL_INDEX) { //if the unit's level changed in accordance to the difference between the levels of the two unit types, then its level change would be duplicated when leveling up, so let's skip the level variable here continue; } if (unit.Variable[i].Max && unit.Variable[i].Value) { unit.Variable[i].Value += newstats.Variables[i].Max - oldstats.Variables[i].Max; } else { unit.Variable[i].Value += newstats.Variables[i].Value - oldstats.Variables[i].Value; } if (i == KILL_INDEX || i == XP_INDEX) { unit.Variable[i].Value = unit.Variable[i].Max; } else { unit.Variable[i].Max += newstats.Variables[i].Max - oldstats.Variables[i].Max; unit.Variable[i].Increase += newstats.Variables[i].Increase - oldstats.Variables[i].Increase; unit.Variable[i].Enable = newstats.Variables[i].Enable; } //Wyrmgus end } //Wyrmgus start for (size_t i = 0; i < UnitTypes.size(); ++i) { if (newstats.UnitStock[i] != oldstats.UnitStock[i]) { unit.UnitStock[i] += newstats.UnitStock[i] - oldstats.UnitStock[i]; unit.UnitStock[i] = std::max(unit.UnitStock[i], 0); } } //Wyrmgus end //Wyrmgus start //change variation if upgrading (new unit type may have different variations) unit.ChooseVariation(&newtype); for (int i = 0; i < MaxImageLayers; ++i) { unit.ChooseVariation(&newtype, false, i); } //Wyrmgus end unit.Type = const_cast<CUnitType *>(&newtype); unit.Stats = &unit.Type->Stats[player.Index]; //Wyrmgus start //deequip the current equipment if they are incompatible with the new unit type for (int i = 0; i < MaxItemSlots; ++i) { for (size_t j = 0; j < unit.EquippedItems[i].size(); ++j) { if (!unit.CanEquipItemClass(unit.EquippedItems[i][j]->Type->ItemClass)) { unit.DeequipItem(*unit.EquippedItems[i][j]); } } } //Wyrmgus end //Wyrmgus start //change personal name if new unit type's civilization is different from old unit type's civilization if (unit.Character == NULL && !oldtype.Civilization.empty() && !newtype.Civilization.empty() && oldtype.Civilization != newtype.Civilization) { unit.UpdatePersonalName(); } //Wyrmgus end if (newtype.CanCastSpell && !unit.AutoCastSpell) { unit.AutoCastSpell = new char[SpellTypeTable.size()]; unit.SpellCoolDownTimers = new int[SpellTypeTable.size()]; memset(unit.AutoCastSpell, 0, SpellTypeTable.size() * sizeof(char)); memset(unit.SpellCoolDownTimers, 0, SpellTypeTable.size() * sizeof(int)); } UpdateForNewUnit(unit, 1); //Wyrmgus start /* // Update Possible sight range change UpdateUnitSightRange(unit); if (!container) { unit.Place(pos); RestoreSelection(); } else { MapMarkUnitSight(unit); } */ if (!SaveGameLoading) { // Update Possible sight range change UpdateUnitSightRange(unit); if (!container) { unit.Place(pos); RestoreSelection(); } else { MapMarkUnitSight(unit); //Wyrmgus start //if unit has a container, update the container's attack range, as the unit's range may have been changed with the upgrade container->UpdateContainerAttackRange(); //Wyrmgus end } } //Wyrmgus end //Wyrmgus start //update the unit's XP required, as its level or points may have changed unit.UpdateXPRequired(); //Wyrmgus end //Wyrmgus start /* // // Update possible changed buttons. // if (IsOnlySelected(unit) || &player == ThisPlayer) { // could affect the buttons of any selected unit SelectedUnitChanged(); } */ if (!SaveGameLoading) { // // Update possible changed buttons. // if (IsOnlySelected(unit) || &player == ThisPlayer) { // could affect the buttons of any selected unit SelectedUnitChanged(); } } //Wyrmgus end return 1; }
/** ** Transform a unit in another. ** ** @param unit unit to transform. ** @param newtype new type of the unit. ** ** @return 0 on error, 1 if nothing happens, 2 else. */ static int TransformUnitIntoType(CUnit &unit, const CUnitType &newtype) { const CUnitType &oldtype = *unit.Type; if (&oldtype == &newtype) { // nothing to do return 1; } const Vec2i pos = unit.tilePos + oldtype.GetHalfTileSize() - newtype.GetHalfTileSize(); CUnit *container = unit.Container; if (container) { MapUnmarkUnitSight(unit); } else { SaveSelection(); unit.Remove(NULL); if (!UnitTypeCanBeAt(newtype, pos)) { unit.Place(unit.tilePos); RestoreSelection(); // FIXME unit is not modified, try later ? return 0; } } CPlayer &player = *unit.Player; player.UnitTypesCount[oldtype.Slot]--; player.UnitTypesCount[newtype.Slot]++; player.Demand += newtype.Demand - oldtype.Demand; player.Supply += newtype.Supply - oldtype.Supply; // Change resource limit for (int i = 0; i < MaxCosts; ++i) { if (player.MaxResources[i] != -1) { player.MaxResources[i] += newtype.Stats[player.Index].Storing[i] - oldtype.Stats[player.Index].Storing[i]; player.SetResource(i, player.StoredResources[i], STORE_BUILDING); } } // adjust Variables with percent. const CUnitStats &newstats = newtype.Stats[player.Index]; for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); ++i) { if (unit.Variable[i].Max && unit.Variable[i].Value) { unit.Variable[i].Value = newstats.Variables[i].Max * unit.Variable[i].Value / unit.Variable[i].Max; } else { unit.Variable[i].Value = newstats.Variables[i].Value; } unit.Variable[i].Max = newstats.Variables[i].Max; unit.Variable[i].Increase = newstats.Variables[i].Increase; unit.Variable[i].Enable = newstats.Variables[i].Enable; } unit.Type = const_cast<CUnitType *>(&newtype); unit.Stats = &unit.Type->Stats[player.Index]; if (newtype.CanCastSpell && !unit.AutoCastSpell) { unit.AutoCastSpell = new char[SpellTypeTable.size()]; memset(unit.AutoCastSpell, 0, SpellTypeTable.size() * sizeof(char)); } UpdateForNewUnit(unit, 1); // Update Possible sight range change UpdateUnitSightRange(unit); if (!container) { unit.Place(pos); RestoreSelection(); } else { MapMarkUnitSight(unit); } // // Update possible changed buttons. // if (IsOnlySelected(unit) || &player == ThisPlayer) { // could affect the buttons of any selected unit SelectedUnitChanged(); } return 1; }