/** ** Reappear unit on map. ** ** @param unit Unit to drop out. ** ** @return True if unit is unloaded. ** ** @bug FIXME: Place unit only on fields reachable from the transporter */ static int UnloadUnit(CUnit &transporter, CUnit &unit) { const int maxRange = 1; Vec2i pos; Assert(unit.Removed); if (!FindUnloadPosition(transporter, unit, transporter.tilePos, maxRange, &pos)) { return false; } unit.Boarded = 0; unit.Place(pos); transporter.BoardCount -= unit.Type->BoardSize; return true; }
/** ** Create a unit and place it on the map ** ** @param l Lua state. ** ** @return Returns the slot number of the made unit. */ static int CclCreateUnit(lua_State *l) { LuaCheckArgs(l, 3); lua_pushvalue(l, 1); CUnitType *unittype = CclGetUnitType(l); if (unittype == NULL) { LuaError(l, "Bad unittype"); } lua_pop(l, 1); Vec2i ipos; CclGetPos(l, &ipos.x, &ipos.y, 3); lua_pushvalue(l, 2); const int playerno = TriggerGetPlayer(l); lua_pop(l, 1); if (playerno == -1) { printf("CreateUnit: You cannot use \"any\" in create-unit, specify a player\n"); LuaError(l, "bad player"); return 0; } if (Players[playerno].Type == PlayerNobody) { printf("CreateUnit: player %d does not exist\n", playerno); LuaError(l, "bad player"); return 0; } CUnit *unit = MakeUnit(*unittype, &Players[playerno]); if (unit == NULL) { DebugPrint("Unable to allocate unit"); return 0; } else { if (UnitCanBeAt(*unit, ipos) || (unit->Type->Building && CanBuildUnitType(NULL, *unit->Type, ipos, 0))) { unit->Place(ipos); } else { const int heading = SyncRand() % 256; unit->tilePos = ipos; DropOutOnSide(*unit, heading, NULL); } UpdateForNewUnit(*unit, 0); lua_pushnumber(l, UnitNumber(*unit)); return 1; } }
/* virtual */ void CAnimation_SpawnUnit::Action(CUnit &unit, int &/*move*/, int /*scale*/) const { Assert(unit.Anim.Anim == this); const int offX = ParseAnimInt(unit, this->offXStr.c_str()); const int offY = ParseAnimInt(unit, this->offYStr.c_str()); const int range = ParseAnimInt(unit, this->rangeStr.c_str()); const int playerId = ParseAnimInt(unit, this->playerStr.c_str()); const SpawnUnit_Flags flags = (SpawnUnit_Flags)(ParseAnimFlags(unit, this->flagsStr.c_str())); CPlayer &player = Players[playerId]; const Vec2i pos(unit.tilePos.x + offX, unit.tilePos.y + offY); CUnitType *type = UnitTypeByIdent(this->unitTypeStr.c_str()); Assert(type); Vec2i resPos; DebugPrint("Creating a %s\n" _C_ type->Name.c_str()); FindNearestDrop(*type, pos, resPos, LookingW); if (SquareDistance(pos, resPos) <= square(range)) { CUnit *target = MakeUnit(*type, &player); if (target != NULL) { target->tilePos = resPos; target->Place(resPos); if (flags & SU_Summoned) { target->Summoned = 1; } if ((flags & SU_JoinToAIForce) && unit.Player->AiEnabled) { int force = unit.Player->Ai->Force.GetForce(unit); if (force != -1) { unit.Player->Ai->Force[force].Insert(*target); target->GroupId = unit.GroupId; CommandDefend(*target, unit, FlushCommands); } } //DropOutOnSide(*target, LookingW, NULL); } else { DebugPrint("Unable to allocate Unit"); } } }
/** ** Move a unit on map. ** ** @param l Lua state. ** ** @return Returns the slot number of the made placed. */ static int CclMoveUnit(lua_State *l) { LuaCheckArgs(l, 2); lua_pushvalue(l, 1); CUnit *unit = CclGetUnit(l); lua_pop(l, 1); Vec2i ipos; CclGetPos(l, &ipos.x, &ipos.y, 2); if (UnitCanBeAt(*unit, ipos)) { unit->Place(ipos); } else { const int heading = SyncRand() % 256; unit->tilePos = ipos; DropOutOnSide(*unit, heading, NULL); } lua_pushvalue(l, 1); return 1; }
/* virtual */ void COrder_Die::Execute(CUnit &unit) { // Show death animation if (AnimateActionDie(unit) == false) { // some units has no death animation unit.Anim.Unbreakable = 0; } if (unit.Anim.Unbreakable) { return ; } const CUnitType &type = *unit.Type; // Die sequence terminated, generate corpse. if (type.CorpseType == NULL) { unit.Remove(NULL); unit.Release(); return ; } const CUnitType &corpseType = *type.CorpseType; Assert(type.TileWidth >= corpseType.TileWidth && type.TileHeight >= corpseType.TileHeight); // Update sight for new corpse // We have to unmark BEFORE changing the type. // Always do that, since types can have different vision properties. unit.Remove(NULL); unit.Type = &corpseType; unit.Stats = &corpseType.Stats[unit.Player->Index]; UpdateUnitSightRange(unit); //Wyrmgus start // unit.Place(unit.tilePos); unit.Place(unit.tilePos, unit.MapLayer); //Wyrmgus end unit.Frame = 0; UnitUpdateHeading(unit); AnimateActionDie(unit); // with new corpse. }
/** ** Find the closest available drop zone for a transporter. ** Fail if transporter don't transport any unit.. ** ** @param transporter the transporter ** @param startPos start location for the search ** @param maxRange The maximum distance from initial position to search... ** @param resPos drop zone position ** ** @return 1 if a location was found, 0 otherwise */ static int ClosestFreeDropZone(CUnit &transporter, const Vec2i &startPos, int maxRange, Vec2i *resPos) { // Check there are units onboard if (!transporter.UnitInside) { return 0; } const bool isTransporterRemoved = transporter.Removed; const bool selected = transporter.Selected; if (!isTransporterRemoved) { // Remove transporter to avoid "collision" with itself. transporter.Remove(NULL); } const bool res = ClosestFreeDropZone_internal(transporter, startPos, maxRange, resPos); if (!isTransporterRemoved) { transporter.Place(transporter.tilePos); if (selected) { SelectUnit(transporter); SelectionChanged(); } } return res; }
bool COrder_Build::StartBuilding(CUnit &unit, CUnit &ontop) { const CUnitType &type = this->GetUnitType(); unit.Player->SubUnitType(type); CUnit *build = MakeUnit(const_cast<CUnitType &>(type), unit.Player); // If unable to make unit, stop, and report message if (build == NULL) { // FIXME: Should we retry this? unit.Player->Notify(NotifyYellow, unit.tilePos, _("Unable to create building %s"), type.Name.c_str()); if (unit.Player->AiEnabled) { AiCanNotBuild(unit, type); } return false; } build->Constructed = 1; build->CurrentSightRange = 0; // Building on top of something, may remove what is beneath it if (&ontop != &unit) { CBuildRestrictionOnTop *b; b = static_cast<CBuildRestrictionOnTop *>(OnTopDetails(*build, ontop.Type)); Assert(b); if (b->ReplaceOnBuild) { build->ResourcesHeld = ontop.ResourcesHeld; // We capture the value of what is beneath. build->Variable[GIVERESOURCE_INDEX].Value = ontop.Variable[GIVERESOURCE_INDEX].Value; build->Variable[GIVERESOURCE_INDEX].Max = ontop.Variable[GIVERESOURCE_INDEX].Max; build->Variable[GIVERESOURCE_INDEX].Enable = ontop.Variable[GIVERESOURCE_INDEX].Enable; ontop.Remove(NULL); // Destroy building beneath UnitLost(ontop); UnitClearOrders(ontop); ontop.Release(); } } // Must set action before placing, otherwise it will incorrectly mark radar delete build->CurrentOrder(); build->Orders[0] = COrder::NewActionBuilt(unit, *build); UpdateUnitSightRange(*build); // Must place after previous for map flags build->Place(this->goalPos); // HACK: the building is not ready yet build->Player->UnitTypesCount[type.Slot]--; if (build->Active) { build->Player->UnitTypesAiActiveCount[type.Slot]--; } // We need somebody to work on it. if (!type.BoolFlag[BUILDEROUTSIDE_INDEX].value) { UnitShowAnimation(unit, unit.Type->Animations->Still); unit.Remove(build); this->State = State_BuildFromInside; if (unit.Selected) { SelectedUnitChanged(); } } else { this->State = State_BuildFromOutside; this->BuildingUnit = build; unit.Direction = DirectionToHeading(build->tilePos - unit.tilePos); UnitUpdateHeading(unit); } return true; }
//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; }