[+] Added new order COrder_Defend, which allows to send some unit to defend another. You may use right click with ALT, or use the "move" button with ALT button

[*] The coward units in AI force now use Defend order instead of attacking. The same rule applies to summoned units.
[*] The summoned units get GroupId = -1 to avoid AI defence force activate
[*] Changed CclKillUnitAt to use CclGetPos
This commit is contained in:
cybermind 2012-12-27 14:58:44 +06:00
parent 53721e1ce9
commit 09e63586c7
17 changed files with 444 additions and 59 deletions

View file

@ -43,6 +43,7 @@ set(action_SRCS
src/action/action_board.cpp
src/action/action_build.cpp
src/action/action_built.cpp
src/action/action_defend.cpp
src/action/action_die.cpp
src/action/action_follow.cpp
src/action/action_move.cpp
@ -422,6 +423,7 @@ set(stratagus_action_HDRS
src/include/action/action_board.h
src/include/action/action_build.h
src/include/action/action_built.h
src/include/action/action_defend.h
src/include/action/action_die.h
src/include/action/action_follow.h
src/include/action/action_move.h

View file

@ -0,0 +1,209 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name action_defend.cpp - The defend action. */
//
// (c) Copyright 2012 by cybermind
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; only version 2 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
//@{
/*----------------------------------------------------------------------------
-- Includes
----------------------------------------------------------------------------*/
#include "stratagus.h"
#include "action/action_defend.h"
#include "iolib.h"
#include "map.h"
#include "pathfinder.h"
#include "script.h"
#include "ui.h"
#include "unit.h"
#include "unittype.h"
#include "video.h"
enum {
State_Init = 0,
State_MovingToTarget,
State_Defending
};
/*----------------------------------------------------------------------------
-- Functions
----------------------------------------------------------------------------*/
/* static */ COrder *COrder::NewActionDefend(CUnit &dest)
{
COrder_Defend *order = new COrder_Defend();
if (dest.Destroyed) {
order->goalPos = dest.tilePos + dest.Type->GetHalfTileSize();
} else {
order->SetGoal(&dest);
order->Range = 1;
}
return order;
}
/* virtual */ void COrder_Defend::Save(CFile &file, const CUnit &unit) const
{
file.printf("{\"action-defend\",");
if (this->Finished) {
file.printf(" \"finished\", ");
}
file.printf(" \"range\", %d,", this->Range);
if (this->HasGoal()) {
file.printf(" \"goal\", \"%s\",", UnitReference(this->GetGoal()).c_str());
}
file.printf(" \"tile\", {%d, %d},", this->goalPos.x, this->goalPos.y);
file.printf(" \"state\", %d", this->State);
file.printf("}");
}
/* virtual */ bool COrder_Defend::ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit)
{
if (!strcmp(value, "state")) {
++j;
lua_rawgeti(l, -1, j + 1);
this->State = LuaToNumber(l, -1);
lua_pop(l, 1);
} else if (!strcmp(value, "range")) {
++j;
lua_rawgeti(l, -1, j + 1);
this->Range = LuaToNumber(l, -1);
lua_pop(l, 1);
} else if (!strcmp(value, "tile")) {
++j;
lua_rawgeti(l, -1, j + 1);
CclGetPos(l, &this->goalPos.x , &this->goalPos.y);
lua_pop(l, 1);
} else {
return false;
}
return true;
}
/* virtual */ bool COrder_Defend::IsValid() const
{
return true;
}
/* virtual */ PixelPos COrder_Defend::Show(const CViewport &vp, const PixelPos &lastScreenPos) const
{
PixelPos targetPos;
if (this->HasGoal()) {
targetPos = vp.MapToScreenPixelPos(this->GetGoal()->GetMapPixelPosCenter());
} else {
targetPos = vp.TilePosToScreen_Center(this->goalPos);
}
Video.FillCircleClip(ColorGreen, lastScreenPos, 2);
Video.DrawLineClip(ColorGreen, lastScreenPos, targetPos);
Video.FillCircleClip(ColorOrange, targetPos, 3);
return targetPos;
}
/* virtual */ void COrder_Defend::UpdatePathFinderData(PathFinderInput &input)
{
input.SetMinRange(0);
input.SetMaxRange(this->Range);
Vec2i tileSize;
if (this->HasGoal()) {
CUnit *goal = this->GetGoal();
tileSize.x = goal->Type->TileWidth;
tileSize.y = goal->Type->TileHeight;
input.SetGoal(goal->tilePos, tileSize);
} else {
tileSize.x = 0;
tileSize.y = 0;
input.SetGoal(this->goalPos, tileSize);
}
}
/* virtual */ void COrder_Defend::Execute(CUnit &unit)
{
if (unit.Wait) {
unit.Wait--;
return ;
}
CUnit *goal = this->GetGoal();
if (this->State == State_Init) {
if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) {
this->Finished = true;
return;
}
this->State = State_MovingToTarget;
} else if (this->State == State_Defending) {
if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) {
this->Finished = true;
return;
}
}
if (!unit.Anim.Unbreakable) {
if (AutoCast(unit) || AutoAttack(unit) || AutoRepair(unit)) {
return;
}
}
switch (DoActionMove(unit)) {
case PF_UNREACHABLE:
// Some tries to reach the goal
this->Range++;
break;
case PF_REACHED: {
if (!goal || !goal->IsVisibleAsGoal(*unit.Player)) { // goal has died
this->Finished = true;
return;
}
// Now defend the goal
this->goalPos = goal->tilePos;
this->State = State_Defending;
}
default:
break;
}
// Target destroyed?
if (goal && !goal->IsVisibleAsGoal(*unit.Player)) {
DebugPrint("Goal gone\n");
this->goalPos = goal->tilePos + goal->Type->GetHalfTileSize();
this->ClearGoal();
goal = NULL;
if (this->State == State_Defending) {
this->Finished = true;
return;
}
}
}

View file

@ -45,6 +45,7 @@
#include "action/action_board.h"
#include "action/action_build.h"
#include "action/action_built.h"
#include "action/action_defend.h"
#include "action/action_die.h"
#include "action/action_follow.h"
#include "action/action_move.h"
@ -185,6 +186,8 @@ void CclParseOrder(lua_State *l, CUnit &unit, COrderPtr *orderPtr)
*orderPtr = new COrder_Build;
} else if (!strcmp(actiontype, "action-built")) {
*orderPtr = new COrder_Built;
} else if (!strcmp(actiontype, "action-defend")) {
*orderPtr = new COrder_Defend;
} else if (!strcmp(actiontype, "action-die")) {
*orderPtr = new COrder_Die;
} else if (!strcmp(actiontype, "action-follow")) {

View file

@ -182,6 +182,33 @@ void CommandStandGround(CUnit &unit, int flush)
ClearSavedAction(unit);
}
/**
** Follow unit and defend it
**
** @param unit pointer to unit.
** @param dest unit to follow
** @param flush if true, flush command queue.
*/
void CommandDefend(CUnit &unit, CUnit &dest, int flush)
{
if (IsUnitValidForNetwork(unit) == false) {
return ;
}
COrderPtr *order;
if (!unit.CanMove()) {
ClearNewAction(unit);
order = &unit.NewOrder;
} else {
order = GetNextOrder(unit, flush);
if (order == NULL) {
return;
}
}
*order = COrder::NewActionDefend(dest);
ClearSavedAction(unit);
}
/**
** Follow unit to new position
**

View file

@ -399,6 +399,15 @@ void AiForce::Attack(const Vec2i &pos)
}
// Send all units in the force to enemy.
CUnit *leader = NULL;
for (size_t i = 0; i != this->Units.size(); ++i) {
CUnit *const unit = this->Units[i];
if (unit->IsAgressive()) {
leader = unit;
break;
}
}
for (size_t i = 0; i != this->Units.size(); ++i) {
CUnit *const unit = this->Units[i];
@ -406,10 +415,14 @@ void AiForce::Attack(const Vec2i &pos)
const int delay = i / 5; // To avoid lot of CPU consuption, send them with a small time difference.
unit->Wait = delay;
if (unit->Type->CanAttack) {
if (unit->IsAgressive()) {
CommandAttack(*unit, this->GoalPos, NULL, FlushCommands);
} else {
CommandMove(*unit, this->GoalPos, FlushCommands);
if (leader) {
CommandDefend(*unit, *leader, FlushCommands);
} else {
CommandMove(*unit, this->GoalPos, FlushCommands);
}
}
}
}
@ -800,9 +813,13 @@ void AiForce::Update()
}
std::vector<CUnit *> idleUnits;
CUnit *leader = NULL;
for (unsigned int i = 0; i != Size(); ++i) {
CUnit &aiunit = *Units[i];
if (aiunit.IsAgressive()) {
leader = &aiunit;
}
if (aiunit.IsIdle() && aiunit.IsAliveOnMap()) {
idleUnits.push_back(&aiunit);
}
@ -854,7 +871,7 @@ void AiForce::Update()
const int delay = i / 5; // To avoid lot of CPU consuption, send them with a small time difference.
aiunit.Wait = delay;
if (aiunit.Type->CanAttack) {
if (aiunit.IsAgressive()) {
CommandAttack(aiunit, this->GoalPos, NULL, FlushCommands);
} else if (aiunit.Type->CanTransport()) {
if (aiunit.BoardCount != 0) {
@ -865,7 +882,11 @@ void AiForce::Update()
this->Remove(aiunit);
}
} else {
CommandMove(aiunit, this->GoalPos, FlushCommands);
if (leader) {
CommandDefend(aiunit, *leader, FlushCommands);
} else {
CommandMove(aiunit, this->GoalPos, FlushCommands);
}
}
}

View file

@ -819,6 +819,8 @@ static void DoNextReplay()
SendCommandStopUnit(*unit);
} else if (!strcmp(action, "stand-ground")) {
SendCommandStandGround(*unit, flags);
} else if (!strcmp(action, "defend")) {
SendCommandDefend(*unit, *dunit, flags);
} else if (!strcmp(action, "follow")) {
SendCommandFollow(*unit, *dunit, flags);
} else if (!strcmp(action, "move")) {

View file

@ -0,0 +1,64 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name action_defend.h - The actions headerfile. */
//
// (c) Copyright 2012 by cybermind
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; only version 2 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
#ifndef __ACTION_DEFEND_H__
#define __ACTION_DEFEND_H__
#include "actions.h"
//@{
class COrder_Defend : public COrder
{
friend COrder *COrder::NewActionDefend(CUnit &dest);
public:
COrder_Defend() : COrder(UnitActionDefend), State(0), Range(0) {
goalPos.x = -1;
goalPos.y = -1;
}
virtual COrder_Defend *Clone() const { return new COrder_Defend(*this); }
virtual bool IsValid() const;
virtual void Save(CFile &file, const CUnit &unit) const;
virtual bool ParseSpecificData(lua_State *l, int &j, const char *value, const CUnit &unit);
virtual void Execute(CUnit &unit);
virtual PixelPos Show(const CViewport &vp, const PixelPos &lastScreenPos) const;
virtual void UpdatePathFinderData(PathFinderInput &input);
private:
unsigned int State;
int Range;
Vec2i goalPos;
};
//@}
#endif // !__ACTION_DEFEND_H__

View file

@ -52,6 +52,7 @@ enum UnitAction {
UnitActionStill, /// unit stand still, does nothing
UnitActionStandGround, /// unit stands ground
UnitActionFollow, /// unit follows units
UnitActionDefend, /// unit defends unit
UnitActionMove, /// unit moves to position/unit
UnitActionAttack, /// unit attacks position/unit
UnitActionAttackGround, /// unit attacks ground
@ -128,6 +129,7 @@ public:
static COrder *NewActionBoard(CUnit &unit);
static COrder *NewActionBuild(const CUnit &builder, const Vec2i &pos, CUnitType &building);
static COrder *NewActionBuilt(CUnit &builder, CUnit &unit);
static COrder *NewActionDefend(CUnit &dest);
static COrder *NewActionDie();
static COrder *NewActionFollow(CUnit &dest);
static COrder *NewActionMove(const Vec2i &pos);

View file

@ -62,6 +62,8 @@ extern void CommandQuit(int player);
extern void CommandStopUnit(CUnit &unit);
/// Prepare command stand ground
extern void CommandStandGround(CUnit &unit, int flush);
/// Prepare command defend
extern void CommandDefend(CUnit &unit, CUnit &dest, int flush);
/// Prepare command follow
extern void CommandFollow(CUnit &unit, CUnit &dest, int flush);
/// Prepare command move
@ -128,6 +130,8 @@ typedef unsigned short UnitRef;
extern void SendCommandStopUnit(CUnit &unit);
/// Send stand ground command
extern void SendCommandStandGround(CUnit &unit, int flush);
/// Send defend command
extern void SendCommandDefend(CUnit &unit, CUnit &dest, int flush);
/// Send follow command
extern void SendCommandFollow(CUnit &unit, CUnit &dest, int flush);
/// Send move command

View file

@ -75,6 +75,7 @@ enum _message_type_ {
MessageCommandStop, /// Unit command stop
MessageCommandStand, /// Unit command stand ground
MessageCommandDefend, /// Unit command defend
MessageCommandFollow, /// Unit command follow
MessageCommandMove, /// Unit command move
MessageCommandRepair, /// Unit command repair

View file

@ -78,6 +78,23 @@ void SendCommandStandGround(CUnit &unit, int flush)
}
}
/**
** Send command: Defend some unit.
**
** @param unit pointer to unit.
** @param dest defend this unit.
** @param flush Flag flush all pending commands.
*/
void SendCommandDefend(CUnit &unit, CUnit &dest, int flush)
{
if (!IsNetworkGame()) {
CommandLog("defend", &unit, flush, -1, -1, &dest, NULL, -1);
CommandDefend(unit, dest, flush);
} else {
NetworkSendCommand(MessageCommandDefend, unit, 0, 0, &dest, 0, flush);
}
}
/**
** Send command: Follow unit to position.
**
@ -571,6 +588,15 @@ void ParseCommand(unsigned char msgnr, UnitRef unum,
CommandLog("stand-ground", &unit, status, -1, -1, NoUnitP, NULL, -1);
CommandStandGround(unit, status);
break;
case MessageCommandDefend: {
if (dstnr != (unsigned short)0xFFFF) {
CUnit &dest = UnitManager.GetSlotUnit(dstnr);
Assert(dest.Type);
CommandLog("defend", &unit, status, -1, -1, &dest, NULL, -1);
CommandDefend(unit, dest, status);
}
break;
}
case MessageCommandFollow: {
if (dstnr != (unsigned short)0xFFFF) {
CUnit &dest = UnitManager.GetSlotUnit(dstnr);

View file

@ -32,9 +32,12 @@
#include "stratagus.h"
#include "action/action_defend.h"
#include "spell/spell_summon.h"
#include "actions.h"
#include "commands.h"
#include "script.h"
#include "unit.h"
#include "unit_find.h"
@ -133,8 +136,15 @@ public:
if (ttl) {
target->TTL = GameCycle + ttl;
}
if (caster.GroupId && caster.Player->AiEnabled) {
target->GroupId = caster.GroupId;
// To avoid defending summoned unit for AI
if (caster.Player->AiEnabled) {
if (caster.GroupId) {
target->GroupId = caster.GroupId;
CommandDefend(*target, caster, FlushCommands);
} else {
target->GroupId = -1;
}
}
caster.Variable[MANA_INDEX].Value -= spell.ManaCost;

View file

@ -815,7 +815,7 @@ void CPlayer::UpdateFreeWorkers()
for (int i = 0; i < nunits; ++i) {
CUnit &unit = this->GetUnit(i);
if (unit.Type->Harvester && unit.Type->ResInfo && !unit.Removed) {
if (unit.IsAlive() && unit.Type->Harvester && unit.Type->ResInfo && !unit.Removed) {
if (unit.CurrentAction() == UnitActionStill) {
FreeWorkers.push_back(&unit);
}

View file

@ -184,6 +184,7 @@ void LoadPO(const char *file)
**/
void SetTranslationsFiles(const char *stratagusfile, const char *gamefile)
{
Entries.clear();
LoadPO(stratagusfile);
LoadPO(gamefile);
}

View file

@ -274,7 +274,8 @@ static int GetButtonStatus(const ButtonAction &button, int UnderCursor)
int saction = Selected[i]->CurrentAction();
if (saction != UnitActionMove &&
saction != UnitActionBuild &&
saction != UnitActionFollow) {
saction != UnitActionFollow &&
saction != UnitActionDefend) {
break;
}
}

View file

@ -458,6 +458,17 @@ static void DoRightButton_ForSelectedUnit(CUnit &unit, CUnit *dest, const Vec2i
return;
}
// Alt + right click on unit is defend anything.
if ((KeyModifiers & ModifierAlt) && dest) {
dest->Blink = 4;
if (!acknowledged) {
PlayUnitSound(unit, VoiceAcknowledging);
acknowledged = 1;
}
SendCommandDefend(unit, *dest, flush);
return;
}
if (DoRightButton_Transporter(unit, dest, flush, acknowledged)) {
return;
}
@ -1014,39 +1025,49 @@ static int SendRepair(const Vec2i &tilePos)
*/
static int SendMove(const Vec2i &tilePos)
{
CUnit *transporter = UnitUnderCursor;
CUnit *goal = UnitUnderCursor;
int ret = 0;
// Move to a transporter.
if (transporter && transporter->Type->CanTransport()) {
int i;
for (i = 0; i < NumSelected; ++i) {
if (CanTransport(*transporter, *Selected[i])) {
SendCommandStopUnit(*transporter);
ret = 1;
break;
}
}
if (i == NumSelected) {
transporter = NULL;
}
} else {
transporter = NULL;
}
const int flush = !(KeyModifiers & ModifierShift);
for (int i = 0; i < NumSelected; ++i) {
CUnit *unit = Selected[i];
// Alt makes unit to defend goal
if (goal && (KeyModifiers & ModifierAlt)) {
for (int i = 0; i < NumSelected; ++i) {
CUnit *unit = Selected[i];
if (transporter && CanTransport(*transporter, *unit)) {
transporter->Blink = 4;
SendCommandFollow(*transporter, *unit, 0);
SendCommandBoard(*unit, *transporter, flush);
goal->Blink = 4;
SendCommandDefend(*unit, *goal, flush);
ret = 1;
}
} else {
// Move to a transporter.
if (goal && goal->Type->CanTransport()) {
int i;
for (i = 0; i < NumSelected; ++i) {
if (CanTransport(*goal, *Selected[i])) {
SendCommandStopUnit(*goal);
ret = 1;
break;
}
}
if (i == NumSelected) {
goal = NULL;
}
} else {
SendCommandMove(*unit, tilePos, flush);
ret = 1;
goal = NULL;
}
for (int i = 0; i < NumSelected; ++i) {
CUnit *unit = Selected[i];
if (goal && CanTransport(*goal, *unit)) {
goal->Blink = 4;
SendCommandFollow(*goal, *unit, 0);
SendCommandBoard(*unit, *goal, flush);
ret = 1;
} else {
SendCommandMove(*unit, tilePos, flush);
ret = 1;
}
}
}
return ret;

View file

@ -911,47 +911,38 @@ static int CclKillUnit(lua_State *l)
*/
static int CclKillUnitAt(lua_State *l)
{
LuaCheckArgs(l, 2);
LuaCheckArgs(l, 5);
lua_pushvalue(l, 1);
const CUnitType *unittype = TriggerGetUnitType(l);
lua_pop(l, 1);
lua_pushvalue(l, 2);
int plynr = TriggerGetPlayer(l);
lua_pop(l, 1);
int q = LuaToNumber(l, 3);
lua_pushvalue(l, 1);
const CUnitType *unittype = TriggerGetUnitType(l);
lua_pop(l, 1);
if (!lua_istable(l, 4)) {
if (!lua_istable(l, 4) || !lua_istable(l, 5)) {
LuaError(l, "incorrect argument");
}
Vec2i pos1;
Vec2i pos2;
lua_rawgeti(l, 4, 1);
pos1.x = LuaToNumber(l, -1);
lua_pop(l, 1);
lua_rawgeti(l, 4, 2);
pos1.y = LuaToNumber(l, -1);
lua_pop(l, 1);
lua_rawgeti(l, 4, 3);
pos2.x = LuaToNumber(l, -1);
lua_pop(l, 1);
lua_rawgeti(l, 4, 4);
pos2.y = LuaToNumber(l, -1);
lua_pop(l, 1);
CclGetPos(l, &pos1.x, &pos1.y, 4);
CclGetPos(l, &pos2.x, &pos2.y, 5);
std::vector<CUnit *> table;
Select(pos1, pos2, table);
int s = 0;
for (size_t j = 0; j < table.size() && s < q; ++j) {
CUnit *unit = table[j];
for (std::vector<CUnit *>::iterator it = table.begin(); it != table.end() && s < q; ++it) {
CUnit &unit = **it;
if (unittype == ANY_UNIT
|| (unittype == ALL_FOODUNITS && !unit->Type->Building)
|| (unittype == ALL_BUILDINGS && unit->Type->Building)
|| unittype == unit->Type) {
if (plynr == -1 || plynr == unit->Player->Index) {
LetUnitDie(*unit);
|| (unittype == ALL_FOODUNITS && !unit.Type->Building)
|| (unittype == ALL_BUILDINGS && unit.Type->Building)
|| unittype == unit.Type) {
if (plynr == -1 || plynr == unit.Player->Index) {
LetUnitDie(unit);
++s;
}
}