/**
**  Find the next idle worker, select it, and center on it
*/
void UiFindIdleWorker()
{
	if (ThisPlayer->FreeWorkers.empty()) {
		return;
	}
	CUnit *unit = ThisPlayer->FreeWorkers[0];
	if (LastIdleWorker) {
		const std::vector<CUnit *> &freeWorkers = ThisPlayer->FreeWorkers;
		std::vector<CUnit *>::const_iterator it = std::find(freeWorkers.begin(),
															freeWorkers.end(),
															LastIdleWorker);
		if (it != ThisPlayer->FreeWorkers.end()) {
			if (*it != ThisPlayer->FreeWorkers.back()) {
				unit = *(++it);
			}
		}
	}

	if (unit != NULL) {
		LastIdleWorker = unit;
		SelectSingleUnit(*unit);
		UI.StatusLine.Clear();
		UI.StatusLine.ClearCosts();
		CurrentButtonLevel = 0;
		PlayUnitSound(*Selected[0], VoiceSelected);
		SelectionChanged();
		UI.SelectedViewport->Center(unit->GetMapPixelPosCenter());
	}
}
Beispiel #2
0
/* virtual */ void COrder_Still::Execute(CUnit &unit)
{
	// If unit is not bunkered and removed, wait
	if (unit.Removed
		//Wyrmgus start
//		&& (unit.Container == nullptr || unit.Container->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value == false)) {
		&& (unit.Container == nullptr || !unit.Container->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value || !unit.Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value)) { // make both the unit and the transporter have the tag be necessary for the attack to be possible
			if (unit.Container != nullptr) {
				LeaveShelter(unit); // leave shelter if surrounded
			}
		//Wyrmgus end
		return ;
	}
	this->Finished = false;

	switch (this->State) {
		case SUB_STILL_STANDBY:
			//Wyrmgus start
//			UnitShowAnimation(unit, unit.Type->Animations->Still);
			if (unit.Variable[STUN_INDEX].Value == 0) { //only show the idle animation when still if the unit is not stunned
				UnitShowAnimation(unit, unit.GetAnimations()->Still);
			}
			if (SyncRand(100000) == 0) {
				PlayUnitSound(unit, VoiceIdle);
			}
			unit.StepCount = 0;
			//Wyrmgus end
			break;
		case SUB_STILL_ATTACK: // attacking unit in attack range.
			AnimateActionAttack(unit, *this);
			break;
	}
	if (unit.Anim.Unbreakable) { // animation can't be aborted here
		return;
	}
	//Wyrmgus start
	if (unit.Variable[STUN_INDEX].Value > 0) { //if unit is stunned, remain still
		return;
	}
	//Wyrmgus end
	this->State = SUB_STILL_STANDBY;
	this->Finished = (this->Action == UnitActionStill);
	if (this->Action == UnitActionStandGround || unit.Removed || unit.CanMove() == false) {
		if (unit.AutoCastSpell) {
			this->AutoCastStand(unit);
		}
		if (unit.IsAgressive()) {
			this->AutoAttackStand(unit);
		}
	} else {
		if (AutoCast(unit) || (unit.IsAgressive() && AutoAttack(unit))
			|| AutoRepair(unit)
			//Wyrmgus start
//			|| MoveRandomly(unit)) {
			|| MoveRandomly(unit) || PickUpItem(unit)) {
			//Wyrmgus end
		}
	}
}
/* virtual */ void CAnimation_RandomSound::Action(CUnit &unit, int &/*move*/, int /*scale*/) const
{
	Assert(unit.Anim.Anim == this);

	if (unit.IsVisible(*ThisPlayer) || ReplayRevealMap) {
		const size_t index = SyncRand() % this->sounds.size();
		PlayUnitSound(unit, this->sounds[index].Sound);
	}
}
Beispiel #4
0
/**
**  Find the next idle worker, select it, and center on it
*/
static void UiFindIdleWorker()
{
	if (ThisPlayer->FreeWorkers.empty()) {
		return;
	}
	CUnit *unit = ThisPlayer->FreeWorkers[0];

	if (unit != NULL) {
		SelectSingleUnit(*unit);
		UI.StatusLine.Clear();
		ClearCosts();
		CurrentButtonLevel = 0;
		PlayUnitSound(*Selected[0], VoiceSelected);
		SelectionChanged();
		UI.SelectedViewport->Center(unit->GetMapPixelPosCenter());
	}
}
/**
**	Unit trains unit!
**
**	@param unit	Unit that trains.
*/
global void HandleActionTrain(Unit* unit)
{
    Unit* nunit;
    UnitType* type;
    Player* player;

#if 0
    // JOHNS: should be checked by the user-interface
    if( &Players[unit->Player]==ThisPlayer ) {
	// FIXME: If so used you get millions of messages.
	if( ThisPlayer->Food<=ThisPlayer->Units
		&& unit->Command.Data.Train.Ticks ) {
	    SetMessage( "You need more farms!" );
	} else {
	    AiNeedMoreFarms(unit);
	}
    }
#endif

    player=unit->Player;
    unit->Command.Data.Train.Ticks+=SpeedTrain;
    // FIXME: Should count down
    if( unit->Command.Data.Train.Ticks
	    >=unit->Command.Data.Train.What[0]
		->Stats[player->Player].Costs[TimeCost] ) {

	//
	//	Check if enough food available.
	//
	if( player->Food<=player->NumUnits ) {

	    // FIXME: GameMessage
	    if( player==ThisPlayer ) {
		// FIXME: PlayVoice :), see task.txt
		SetMessage("You need more farms!");
	    } else {
		// FIXME: Callback for AI!
		// AiNeedMoreFarms(unit);
	    }

	    unit->Command.Data.Train.Ticks-=SpeedTrain;
	    unit->Reset=1;
	    unit->Wait=FRAMES_PER_SECOND/6;
	    return;
	}

	nunit=MakeUnit(&UnitTypes[unit->Command.Data.Train.What[0]->Type]
		,player);
	nunit->X=unit->X;
	nunit->Y=unit->Y;
	type=unit->Type;
	DropOutOnSide(nunit,HeadingW,type->TileWidth,type->TileHeight);

	// FIXME: GameMessage
	if( player==ThisPlayer ) {
	    SetMessage("Training complete");
	    PlayUnitSound(nunit,VoiceReady);
	} else {
	    AiTrainingComplete(unit,nunit);
	}

	unit->Reset=1;
	unit->Wait=1;
        
	if ( --unit->Command.Data.Train.Count ) {
	    int z;
	    for( z = 0; z < MAX_UNIT_TRAIN-1; z++ ) {
		unit->Command.Data.Train.What[z] =
			unit->Command.Data.Train.What[z+1];
	    }
	    unit->Command.Data.Train.Ticks=0;
	} else {
	    unit->Command.Action=UnitActionStill;
	}

	nunit->Command=unit->PendCommand;

	if( IsSelected(unit) ) {
	    UpdateBottomPanel();
	    MustRedraw|=RedrawPanels;
	}

	return;
    }

    if( IsSelected(unit) ) {
	MustRedraw|=RedrawTopPanel;
    }

    unit->Reset=1;
    unit->Wait=FRAMES_PER_SECOND/6;
}
Beispiel #6
0
/**
**  Unit moves! Generic function called from other actions.
**
**  @param unit  Pointer to unit.
**
**  @return      >0 remaining path length, 0 wait for path, -1
**               reached goal, -2 can't reach the goal.
*/
int DoActionMove(CUnit &unit)
{
	Vec2i posd; // movement in tile.
	int d;

	Assert(unit.CanMove());

	if (!unit.Moving && (unit.Type->Animations->Move != unit.Anim.CurrAnim || !unit.Anim.Wait)) {
		Assert(!unit.Anim.Unbreakable);

		// FIXME: So units flying up and down are not affected.
		unit.IX = 0;
		unit.IY = 0;

		UnmarkUnitFieldFlags(unit);
		d = NextPathElement(unit, &posd.x, &posd.y);
		MarkUnitFieldFlags(unit);
		switch (d) {
			case PF_UNREACHABLE: // Can't reach, stop
				if (unit.Player->AiEnabled) {
					AiCanNotMove(unit);
				}
				unit.Moving = 0;
				return d;
			case PF_REACHED: // Reached goal, stop
				unit.Moving = 0;
				return d;
			case PF_WAIT: // No path, wait
				// Reset frame to still frame while we wait
				// FIXME: Unit doesn't animate.
				unit.Frame = unit.Type->StillFrame;
				UnitUpdateHeading(unit);
				unit.Wait = 10;

				unit.Moving = 0;
				return d;
			default: // On the way moving
				unit.Moving = 1;
				break;
		}

		if (unit.Type->UnitType == UnitTypeNaval) { // Boat (un)docking?
			const CMapField &mf_cur = *Map.Field(unit.Offset);
			const CMapField &mf_next = *Map.Field(unit.tilePos + posd);

			if (mf_cur.WaterOnMap() && mf_next.CoastOnMap()) {
				PlayUnitSound(unit, VoiceDocking);
			} else if (mf_cur.CoastOnMap() && mf_next.WaterOnMap()) {
				PlayUnitSound(unit, VoiceDocking); // undocking
			}
		}
		Vec2i pos = unit.tilePos + posd;
		unit.MoveToXY(pos);

		// Remove unit from the current selection
		if (unit.Selected && !Map.Field(pos)->playerInfo.IsTeamVisible(*ThisPlayer)) {
			if (NumSelected == 1) { //  Remove building cursor
				CancelBuildingMode();
			}
			if (!ReplayRevealMap) {
				UnSelectUnit(unit);
				SelectionChanged();
			}
		}

		unit.IX = -posd.x * PixelTileSize.x;
		unit.IY = -posd.y * PixelTileSize.y;
		unit.Frame = unit.Type->StillFrame;
		UnitHeadingFromDeltaXY(unit, posd);
	} else {
		posd.x = Heading2X[unit.Direction / NextDirection];
		posd.y = Heading2Y[unit.Direction / NextDirection];
		d = unit.pathFinderData->output.Length + 1;
	}

	unit.pathFinderData->output.Cycles++;//reset have to be manualy controled by caller.
	int move = UnitShowAnimationScaled(unit, unit.Type->Animations->Move, Map.Field(unit.Offset)->Cost);

	unit.IX += posd.x * move;
	unit.IY += posd.y * move;

	// Finished move animation, set Moving to 0 so we recalculate the path
	// next frame
	// FIXME: this is broken for subtile movement
	if (!unit.Anim.Unbreakable && !unit.IX && !unit.IY) {
		unit.Moving = 0;
	}
	return d;
}
Beispiel #7
0
/**
**	Called when right button is pressed
**
**	@param x	X map tile position.
**	@param y	Y map tile position.
*/
global void DoRightButton(int x,int y)
{
    int i;
    Unit* dest;
    Unit* unit;
    UnitType* type;
    int action;
    int acknowledged;

    //
    // No unit selected
    //
    if( !NumSelected ) {
        return;
    }

    //
    // Unit selected isn't owned by the player.
    // You can't select your own units + foreign unit(s).
    //
    if( Selected[0]->Player!=ThisPlayer ) {
        return;
    }

    acknowledged=0;
    for( i=0; i<NumSelected; ++i ) {
        unit=Selected[i];
        DebugCheck( !unit );
        type=unit->Type;
        if( !acknowledged ) {
            PlayUnitSound(unit,VoiceAcknowledging);
            acknowledged=1;
        }
        action=type->MouseAction;
        DebugLevel3(__FUNCTION__": Mouse action %d\n",action);

        //
        //      Enter transporters?
        //
        dest=UnitOnMapTile(x,y);
        if( dest && dest->Type->Transporter
                && dest->Player==ThisPlayer
                && unit->Type->UnitType==UnitTypeLand ) {
            dest->Blink=3;
            DebugLevel3(__FUNCTION__": Board transporter\n");
            SendCommandBoard(unit,dest);
            continue;
        }

        //
        //      Peon/Peasant
        //
        if( action==MouseActionHarvest ) {
            DebugLevel3("Action %x\n",TheMap.ActionMap[x+y*TheMap.Width]);
            if( type->Type==UnitPeonWithWood
                    || type->Type==UnitPeasantWithWood
                    || type->Type==UnitPeonWithGold
                    || type->Type==UnitPeasantWithGold ) {
                dest=UnitOnMapTile(x,y);
                if( dest ) {
                    dest->Blink=3;
                    if( dest->Type->StoresGold
                            && (type->Type==UnitPeonWithGold
                                || type->Type==UnitPeasantWithGold) ) {
                        DebugLevel3("GOLD-DEPOSIT\n");
                        // FIXME: return to this depot??
                        SendCommandReturnGoods(unit);
                        continue;
                    }
                    if( (dest->Type->StoresWood || dest->Type->StoresGold)
                            && (type->Type==UnitPeonWithWood
                                || type->Type==UnitPeasantWithWood) ) {
                        DebugLevel3("WOOD-DEPOSIT\n");
                        // FIXME: return to this depot??
                        SendCommandReturnGoods(unit);
                        continue;
                    }
                }
            } else {
                if( ForestOnMap(x,y) ) {
                    SendCommandHarvest(unit,x,y);
                    continue;
                }
                if( (dest=GoldMineOnMap(x,y)) ) {
                    dest->Blink=3;
                    DebugLevel3("GOLD-MINE\n");
                    SendCommandMineGold(unit,dest);
                    continue;
                }
            }
            // FIXME: repair/attack/follow/board

            dest=TargetOnMapTile(unit,x,y);
            if( dest ) {
                dest->Blink=3;
                if( dest->Player==ThisPlayer ) {
                    // FIXME: SendCommandFollow(unit,x,y,dest);
                    // FIXME: continue;
                } else {
                    // FIXME: can I attack this unit?
                    SendCommandAttack(unit,x,y,dest);
                    continue;
                }
            }

            // cade: this is default repair action
            dest=UnitOnMapTile(x,y);
            if( dest && dest->Type
                    && dest->Player==ThisPlayer
                    && dest->HP<dest->Stats[dest->Player->Player].HitPoints
                    && dest->Type->Building ) {
                SendCommandRepair(unit,x,y);
            } else {
                SendCommandMoveUnit(unit,x,y);
            }
            continue;
        }

        //
        //      Tanker
        //
        if( action==MouseActionHaulOil ) {
            if( type->Type==UnitTankerOrcFull
                    || type->Type==UnitTankerHumanFull ) {
                DebugLevel2("Should return to oil deposit\n");
            } else {
                if( (dest=PlatformOnMap(x,y)) ) {
                    dest->Blink=3;
                    DebugLevel2("PLATFORM\n");
                    SendCommandHaulOil(unit,dest);
                    continue;
                }
            }

            SendCommandMoveUnit(unit,x,y);
            continue;
        }

        //
        //      Fighters
        //
        if( action==MouseActionAttack ) {
            // FIXME: more units on same tile
            dest=TargetOnMapTile(unit,x,y);
            if( dest ) {
                dest->Blink=3;
                if( dest->Player==ThisPlayer ) {
                    // FIXME: SendCommandFollow(unit,x,y,dest);
                    // FIXME: continue;
                } else {
                    // FIXME: can I attack this unit?
                    SendCommandAttack(unit,x,y,dest);
                    continue;
                }
            }
            if( WallOnMap(x,y) ) {
                DebugLevel3("WALL ON TILE\n");
                if( ThisPlayer->Race==PlayerRaceHuman
                        && OrcWallOnMap(x,y) ) {
                    DebugLevel3("HUMAN ATTACKS ORC\n");
                    SendCommandAttack(unit,x,y,NoUnitP);
                }
                if( ThisPlayer->Race==PlayerRaceOrc
                        && HumanWallOnMap(x,y) ) {
                    DebugLevel3("ORC ATTACKS HUMAN\n");
                    SendCommandAttack(unit,x,y,NoUnitP);
                }
            }
            SendCommandMoveUnit(unit,x,y);
            continue;
        }

        // FIXME: demolish!!!!!!!

        // FIXME: attack/follow/board ...
        if( action==MouseActionMove ) {
        }

//	    if( !unit->Type->Building ) {
        SendCommandMoveUnit(unit,x,y);
//	    }
    }
}
/*
**	Chop the wood.
**	Return TRUE if ready, otherwise FALSE.
*/
local int ChopWood(Unit* unit)
{
    Unit* destu;
    int flags;
    extern Animation PeonAttack[];

    flags=UnitShowAnimation(unit,PeonAttack);

    if( (flags&AnimationSound) ) {
	PlayUnitSound(unit,VoiceTreeChopping);
    }

    if( unit->Reset ) {

	DebugCheck( unit->Wait!=1 );

	//
	//	This a work around the bug: "lumber bug"
	//		We give a worker a new command and in the next cycle
	//		the worker is ready chopping.
	//
#if 0
	// FIXME: johns+cade: this didn't work with the current code
	if( unit->NextCommand[0].Action==UnitActionHarvest 
		 || unit->NextCommand[0].Action==UnitActionMineGold ) {
	    unit->SubAction=0;
	    return 0;
	} 
#endif

	//
	//	Wood gone while chopping?
	//
	if( !ForestOnMap(unit->Command.Data.Move.DX
		,unit->Command.Data.Move.DY) ) {
	    if( FindWoodInSight(unit
		    ,&unit->Command.Data.Move.DX
		    ,&unit->Command.Data.Move.DY) ) {
		unit->Command.Data.Move.Fast=1;
		unit->Command.Data.Move.Goal=NoUnitP;
		unit->Command.Data.Move.Range=0;
		// FIXME: shouldn't it be range=1 ??
		DebugCheck( unit->Command.Action!=UnitActionHarvest );
		unit->SubAction=0;
	    } else {
		unit->Command.Action=UnitActionStill;
		unit->SubAction=0;
		DebugLevel3("NO-WOOD in sight range\n");
	    }
	    return 0;
	}

	//
	//	Ready chopping wood?
	//
	if( !(unit->WoodToHarvest = --unit->Value) ) {

	    // Have wood
	    if( unit->Type->Type==UnitPeon ) {
		unit->Type=&UnitTypes[UnitPeonWithWood];
	    } else if( unit->Type->Type==UnitPeasant ) {
		unit->Type=&UnitTypes[UnitPeasantWithWood];
	    } else {
		DebugLevel0("Wrong unit for chopping wood %d\n"
			,unit->Type->Type);
	    }

	    //
	    //	Update the display.
	    //
	    if( UnitVisible(unit) ) {
		MustRedraw|=RedrawMap;
	    }
	    if( IsSelected(unit) ) {
		UpdateBottomPanel();
		MustRedraw|=RedrawBottomPanel;
	    }

	    //
	    //	Update the map.
	    //
	    MapRemoveWood(unit->Command.Data.Move.DX
		,unit->Command.Data.Move.DY);

	    //
	    //	Find place to return wood.
	    //
	    unit->Command.Data.Move.SX=unit->X;
	    unit->Command.Data.Move.SY=unit->Y;
	    if( !(destu=FindWoodDeposit(unit->Player,unit->X,unit->Y)) ) {
		unit->Command.Action=UnitActionStill;
		unit->SubAction=0;
	    } else {
		unit->Command.Data.Move.Fast=1;
		unit->Command.Data.Move.Range=1;
		unit->Command.Data.Move.Goal=destu;
#if 1
		// Fast movement need this??
		NearestOfUnit(destu,unit->X,unit->Y
			,&unit->Command.Data.Move.DX
			,&unit->Command.Data.Move.DY);
#else
		unit->Command.Data.Move.DX=destu->X;
		unit->Command.Data.Move.DY=destu->Y;
#endif
		DebugLevel3("Return to %Zd=%d,%d\n"
			    ,destu-UnitsPool
			    ,unit->Command.Data.Move.DX
			    ,unit->Command.Data.Move.DY);
		DebugCheck( unit->Command.Action!=UnitActionHarvest );
		return 1;
	    }

	}
    }
    return 0;
}
Beispiel #9
0
/* virtual */ void COrder_Train::Execute(CUnit &unit)
{
	AnimateActionTrain(unit);
	if (unit.Wait) {
		unit.Wait--;
		return ;
	}
	CPlayer &player = *unit.Player;
	const CUnitType &nType = *this->Type;
	const int cost = nType.Stats[player.Index].Costs[TimeCost];
	this->Ticks += std::max(1, player.SpeedTrain / SPEEDUP_FACTOR);

	if (this->Ticks < cost) {
		unit.Wait = CYCLES_PER_SECOND / 6;
		return ;
	}
	this->Ticks = std::min(this->Ticks, cost);

	// Check if enough supply available.
	const int food = player.CheckLimits(nType);
	if (food < 0) {
		if (food == -3 && unit.Player->AiEnabled) {
			AiNeedMoreSupply(*unit.Player);
		}
		unit.Wait = CYCLES_PER_SECOND / 6;
		return ;
	}

	CUnit *newUnit = MakeUnit(nType, &player);

	if (newUnit == NULL) { // No more memory :/
		player.Notify(NotifyYellow, unit.tilePos, _("Unable to train %s"), nType.Name.c_str());
		unit.Wait = CYCLES_PER_SECOND / 6;
		return ;
	}

	// New unit might supply food
	UpdateForNewUnit(*newUnit, 0);

	// Set life span
	if (unit.Type->DecayRate) {
		newUnit->TTL = GameCycle + unit.Type->DecayRate * 6 * CYCLES_PER_SECOND;
	}

	/* Auto Group Add */
	if (!unit.Player->AiEnabled && unit.GroupId) {
		int num = 0;
		while (!(unit.GroupId & (1 << num))) {
			++num;
		}
		AddToGroup(&newUnit, 1, num);
	}

	DropOutOnSide(*newUnit, LookingW, &unit);
	player.Notify(NotifyGreen, newUnit->tilePos, _("New %s ready"), nType.Name.c_str());
	if (&player == ThisPlayer) {
		PlayUnitSound(*newUnit, VoiceReady);
	}
	if (unit.Player->AiEnabled) {
		AiTrainingComplete(unit, *newUnit);
	}

	if (unit.NewOrder && unit.NewOrder->HasGoal()
		&& unit.NewOrder->GetGoal()->Destroyed) {
		delete unit.NewOrder;
		unit.NewOrder = NULL;
	}

	if (CanHandleOrder(*newUnit, unit.NewOrder) == true) {
		delete newUnit->Orders[0];
		newUnit->Orders[0] = unit.NewOrder->Clone();
	} else {
#if 0
		// Tell the unit to rigth-click ?
#endif
	}
	this->Finished = true;
	if (IsOnlySelected(unit)) {
		UI.ButtonPanel.Update();
	}

}
Beispiel #10
0
static void Finish(COrder_Built &order, CUnit &unit)
{
	const CUnitType &type = *unit.Type;
	CPlayer &player = *unit.Player;

	DebugPrint("%d: Building %s(%s) ready.\n" _C_ player.Index _C_ type.Ident.c_str() _C_ type.Name.c_str());

	// HACK: the building is ready now
	player.UnitTypesCount[type.Slot]++;
	if (unit.Active) {
		player.UnitTypesAiActiveCount[type.Slot]++;
	}
	unit.Constructed = 0;
	if (unit.Frame < 0) {
		unit.Frame = -1;
	} else {
		unit.Frame = 0;
	}
	CUnit *worker = order.GetWorkerPtr();

	if (worker != NULL) {
		if (type.BoolFlag[BUILDERLOST_INDEX].value) {
			// Bye bye worker.
			LetUnitDie(*worker);
			worker = NULL;
		} else { // Drop out the worker.
			worker->ClearAction();

			DropOutOnSide(*worker, LookingW, &unit);

			// If we can harvest from the new building, do it.
			if (worker->Type->ResInfo[type.GivesResource]) {
				CommandResource(*worker, unit, 0);
			}
			// If we can reurn goods to a new depot, do it.
			if (worker->CurrentResource && worker->ResourcesHeld > 0 && type.CanStore[worker->CurrentResource]) {
				CommandReturnGoods(*worker, &unit, 0);
			}
		}
	}

	if (type.GivesResource && type.StartingResources != 0) {
		// Has StartingResources, Use those
		unit.ResourcesHeld = type.StartingResources;
	}

	player.Notify(NotifyGreen, unit.tilePos, _("New %s done"), type.Name.c_str());
	if (&player == ThisPlayer) {
		if (type.MapSound.Ready.Sound) {
			PlayUnitSound(unit, VoiceReady);
		} else if (worker) {
			PlayUnitSound(*worker, VoiceWorkCompleted);
		} else {
			PlayUnitSound(unit, VoiceBuilding);
		}
	}

	if (player.AiEnabled) {
		/* Worker can be NULL */
		AiWorkComplete(worker, unit);
	}

	// FIXME: Vladi: this is just a hack to test wall fixing,
	// FIXME:  also not sure if the right place...
	// FIXME: Johns: hardcoded unit-type wall / more races!
	if (&type == UnitTypeOrcWall || &type == UnitTypeHumanWall) {
		Map.SetWall(unit.tilePos, &type == UnitTypeHumanWall);
		unit.Remove(NULL);
		UnitLost(unit);
		UnitClearOrders(unit);
		unit.Release();
		return ;
	}

	UpdateForNewUnit(unit, 0);

	// Set the direction of the building if it supports them
	if (type.NumDirections > 1 && type.BoolFlag[NORANDOMPLACING_INDEX].value == false) {
		if (type.BoolFlag[WALL_INDEX].value) { // Special logic for walls
			CorrectWallDirections(unit);
			CorrectWallNeighBours(unit);
		} else {
			unit.Direction = (MyRand() >> 8) & 0xFF; // random heading
		}
		UnitUpdateHeading(unit);
	}

	if (IsOnlySelected(unit) || &player == ThisPlayer) {
		SelectedUnitChanged();
	}
	MapUnmarkUnitSight(unit);
	unit.CurrentSightRange = unit.Stats->Variables[SIGHTRANGE_INDEX].Max;
	MapMarkUnitSight(unit);
	order.Finished = true;
}
Beispiel #11
0
/**
**  Fire missile.
**
**  @param unit  Unit that fires the missile.
*/
void FireMissile(CUnit &unit, CUnit *goal, const Vec2i &goalPos)
{
	Vec2i newgoalPos = goalPos;
	// Goal dead?
	if (goal) {
		Assert(!unit.Type->Missile.Missile->AlwaysFire || unit.Type->Missile.Missile->Range);
		if (goal->Destroyed) {
			DebugPrint("destroyed unit\n");
			return;
		}
		if (goal->Removed) {
			return;
		}
		if (goal->CurrentAction() == UnitActionDie) {
			if (unit.Type->Missile.Missile->AlwaysFire) {
				newgoalPos = goal->tilePos;
				goal = NULL;
			} else {
				return;
			}
		}
	}

	// No missile hits immediately!
	//Wyrmgus start
//	if (unit.Type->Missile.Missile->Class == MissileClassNone) {
	if (unit.Type->Missile.Missile->Class == MissileClassNone || (unit.Type->Animations && unit.Type->Animations->Attack && unit.Type->Animations->RangedAttack && ((goal && unit.MapDistanceTo(*goal) <= 1) || (!goal && unit.MapDistanceTo(goalPos) <= 1)) && !unit.Container)) { // treat melee attacks from units that have both attack and ranged attack animations as having missile class none
	//Wyrmgus end
		//Wyrmgus start
		int damage = 0;
		//Wyrmgus end
		// No goal, take target coordinates
		if (!goal) {
			if (Map.WallOnMap(goalPos)) {
				//Wyrmgus start
//				if (Map.HumanWallOnMap(goalPos)) {
				if (Map.HumanWallOnMap(goalPos) && CalculateHit(unit, *UnitTypeHumanWall->Stats, NULL) == true) {
				//Wyrmgus end
					//Wyrmgus start
					PlayUnitSound(unit, VoiceHit);
					damage = CalculateDamageStats(unit, *UnitTypeHumanWall->Stats, NULL);
					//Wyrmgus end
					Map.HitWall(goalPos,
								//Wyrmgus start
//								CalculateDamageStats(*unit.Stats,
//													 *UnitTypeHumanWall->Stats, unit.Variable[BLOODLUST_INDEX].Value));
								damage);
								//Wyrmgus end
				//Wyrmgus start
//				} else {
				} else if (Map.OrcWallOnMap(goalPos) && CalculateHit(unit, *UnitTypeOrcWall->Stats, NULL) == true) {
				//Wyrmgus end
					//Wyrmgus start
					PlayUnitSound(unit, VoiceHit);
					damage = CalculateDamageStats(unit, *UnitTypeOrcWall->Stats, NULL);
					//Wyrmgus end
					Map.HitWall(goalPos,
								//Wyrmgus start
//								CalculateDamageStats(*unit.Stats,
//													 *UnitTypeOrcWall->Stats, unit.Variable[BLOODLUST_INDEX].Value));
								damage);
								//Wyrmgus end
				}
				return;
			}
			DebugPrint("Missile-none hits no unit, shouldn't happen!\n");
			return;
		}
		//Wyrmgus start
//		HitUnit(&unit, *goal, CalculateDamage(unit, *goal, Damage));
		if (CalculateHit(unit, *goal->Stats, goal) == true) {
			damage = CalculateDamage(unit, *goal, Damage);
			HitUnit(&unit, *goal, damage);
			PlayUnitSound(unit, VoiceHit);
			
			//apply Thorns damage if attacker is at melee range
			if (goal && goal->Variable[THORNSDAMAGE_INDEX].Value && unit.MapDistanceTo(*goal) <= 1) {
				int thorns_damage = std::max<int>(goal->Variable[THORNSDAMAGE_INDEX].Value - unit.Variable[ARMOR_INDEX].Value, 1);
				if (GameSettings.NoRandomness) {
					thorns_damage -= ((thorns_damage + 2) / 2) / 2; //if no randomness setting is used, then the damage will always return what would have been the average damage with randomness
				} else {
					thorns_damage -= SyncRand() % ((thorns_damage + 2) / 2);
				}
				HitUnit(goal, unit, thorns_damage);
			}
		} else {
			PlayUnitSound(unit, VoiceMiss);
		}
		//Wyrmgus end
		return;
	}

	// If Firing from inside a Bunker
	CUnit *from = GetFirstContainer(unit);
	const int dir = ((unit.Direction + NextDirection / 2) & 0xFF) / NextDirection;
	const PixelPos startPixelPos = Map.TilePosToMapPixelPos_Center(from->tilePos)
								   + unit.Type->MissileOffsets[dir][0];

	Vec2i dpos;
	if (goal) {
		Assert(goal->Type);  // Target invalid?
		// Moved out of attack range?

		if (unit.MapDistanceTo(*goal) < unit.Type->MinAttackRange) {
			DebugPrint("Missile target too near %d,%d\n" _C_
					   unit.MapDistanceTo(*goal) _C_ unit.Type->MinAttackRange);
			// FIXME: do something other?
			return;
		}
		// Fire to nearest point of the unit!
		// If Firing from inside a Bunker
		if (unit.Container) {
			NearestOfUnit(*goal, GetFirstContainer(unit)->tilePos, &dpos);
		} else {
			dpos = goal->tilePos + goal->Type->GetHalfTileSize();
		}
	} else {
		dpos = newgoalPos;
		// FIXME: Can this be too near??
	}

	PixelPos destPixelPos = Map.TilePosToMapPixelPos_Center(dpos);
	Missile *missile = MakeMissile(*unit.Type->Missile.Missile, startPixelPos, destPixelPos);
	//
	// Damage of missile
	//
	if (goal) {
		missile->TargetUnit = goal;
	}
	missile->SourceUnit = &unit;
}