Split spell by SpellActionType.

This commit is contained in:
joris 2012-08-10 13:26:54 +02:00
parent d6da2d2c21
commit abfe3f2086
14 changed files with 997 additions and 593 deletions

View file

@ -226,12 +226,6 @@ set(pathfinder_SRCS
)
source_group(pathfinder FILES ${pathfinder_SRCS})
set(spell_SRCS
src/spell/script_spell.cpp
src/spell/spells.cpp
)
source_group(spell FILES ${spell_SRCS})
set(sound_SRCS
src/sound/mikmod.cpp
src/sound/music.cpp
@ -245,6 +239,22 @@ set(sound_SRCS
)
source_group(sound FILES ${sound_SRCS})
set(spell_SRCS
src/spell/script_spell.cpp
src/spell/spell_adjustvariable.cpp
src/spell/spell_adjustvital.cpp
src/spell/spell_areaadjustvital.cpp
src/spell/spell_areabombardment.cpp
src/spell/spell_capture.cpp
src/spell/spell_demolish.cpp
src/spell/spell_polymorph.cpp
src/spell/spell_spawnmissile.cpp
src/spell/spell_spawnportal.cpp
src/spell/spell_summon.cpp
src/spell/spells.cpp
)
source_group(spell FILES ${spell_SRCS})
set(stratagusmain_SRCS
src/stratagus/construct.cpp
src/stratagus/groups.cpp
@ -349,8 +359,8 @@ set(stratagus_SRCS
${network_SRCS}
${particle_SRCS}
${pathfinder_SRCS}
${spell_SRCS}
${sound_SRCS}
${spell_SRCS}
${stratagusmain_SRCS}
${ui_SRCS}
${unit_SRCS}

View file

@ -138,10 +138,10 @@ public:
// Specific spells.
//
class AreaAdjustVitals : public SpellActionType
class AreaAdjustVital : public SpellActionType
{
public:
AreaAdjustVitals() : HP(0), Mana(0) {};
AreaAdjustVital() : HP(0), Mana(0) {};
virtual int Cast(CUnit &caster, const SpellType &spell,
CUnit *target, const Vec2i &goalPos);
@ -214,10 +214,10 @@ public:
SpellActionTypeAdjustVariable *Var;
};
class AdjustVitals : public SpellActionType
class AdjustVital : public SpellActionType
{
public:
AdjustVitals() : SpellActionType(1), HP(0), Mana(0), MaxMultiCast(0) {};
AdjustVital() : SpellActionType(1), HP(0), Mana(0), MaxMultiCast(0) {};
virtual int Cast(CUnit &caster, const SpellType &spell,
CUnit *target, const Vec2i &goalPos);

View file

@ -179,7 +179,7 @@ static SpellActionType *CclSpellAction(lua_State *l)
}
return spellaction;
} else if (!strcmp(value, "area-adjust-vitals")) {
AreaAdjustVitals *spellaction = new AreaAdjustVitals;
AreaAdjustVital *spellaction = new AreaAdjustVital;
for (; j < args; ++j) {
lua_rawgeti(l, -1, j + 1);
value = LuaToString(l, -1);
@ -439,7 +439,7 @@ static SpellActionType *CclSpellAction(lua_State *l)
}
return spellaction;
} else if (!strcmp(value, "adjust-vitals")) {
AdjustVitals *spellaction = new AdjustVitals;
AdjustVital *spellaction = new AdjustVital;
for (; j < args; ++j) {
lua_rawgeti(l, -1, j + 1);
value = LuaToString(l, -1);

View file

@ -0,0 +1,89 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name spell_adjustvariable.cpp - The spell AdjustVariable. */
//
// (c) Copyright 1998-2012 by Vladi Belperchinov-Shabanski, Lutz Sammer,
// Jimmy Salmon, and Joris DAUPHIN
//
// 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.
//
//@{
#include "stratagus.h"
#include "spells.h"
#include "unit.h"
/**
** Adjust User Variables.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target
** @param goalPos coord of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int AdjustVariable::Cast(CUnit &caster, const SpellType &, CUnit *target, const Vec2i &/*goalPos*/)
{
for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); ++i) {
CUnit *unit = (this->Var[i].TargetIsCaster) ? &caster : target;
if (!unit) {
continue;
}
// Enable flag.
if (this->Var[i].ModifEnable) {
unit->Variable[i].Enable = this->Var[i].Enable;
}
unit->Variable[i].Enable ^= this->Var[i].InvertEnable;
// Max field
if (this->Var[i].ModifMax) {
unit->Variable[i].Max = this->Var[i].Max;
}
unit->Variable[i].Max += this->Var[i].AddMax;
// Increase field
if (this->Var[i].ModifIncrease) {
unit->Variable[i].Increase = this->Var[i].Increase;
}
unit->Variable[i].Increase += this->Var[i].AddIncrease;
// Value field
if (this->Var[i].ModifValue) {
unit->Variable[i].Value = this->Var[i].Value;
}
unit->Variable[i].Value += this->Var[i].AddValue;
unit->Variable[i].Value += this->Var[i].IncreaseTime * unit->Variable[i].Increase;
clamp(&unit->Variable[i].Value, 0, unit->Variable[i].Max);
}
return 1;
}
//@}

View file

@ -0,0 +1,122 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name spell_adjustvital.cpp - The spell AdjustVital. */
//
// (c) Copyright 1998-2012 by Vladi Belperchinov-Shabanski, Lutz Sammer,
// Jimmy Salmon, and Joris DAUPHIN
//
// 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.
//
//@{
#include "stratagus.h"
#include "spells.h"
#include "unit.h"
/**
** Cast healing. (or exorcism)
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos coord of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int AdjustVital::Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &/*goalPos*/)
{
if (!target) {
return 0;
}
const int hp = this->HP;
const int mana = this->Mana;
const int manacost = spell.ManaCost;
int diffHP;
int diffMana;
// Healing and harming
if (hp > 0) {
diffHP = target->Variable[HP_INDEX].Max - target->Variable[HP_INDEX].Value;
} else {
diffHP = target->Variable[HP_INDEX].Value;
}
if (mana > 0) {
diffMana = target->Stats->Variables[MANA_INDEX].Max - target->Variable[MANA_INDEX].Value;
} else {
diffMana = target->Variable[MANA_INDEX].Value;
}
// When harming cast again to send the hp to negative values.
// Carefull, a perfect 0 target hp kills too.
// Avoid div by 0 errors too!
int castcount = 1;
if (hp) {
castcount = std::max<int>(castcount,
diffHP / abs(hp) + (((hp < 0) && (diffHP % (-hp) > 0)) ? 1 : 0));
}
if (mana) {
castcount = std::max<int>(castcount,
diffMana / abs(mana) + (((mana < 0) && (diffMana % (-mana) > 0)) ? 1 : 0));
}
if (manacost) {
castcount = std::min<int>(castcount, caster.Variable[MANA_INDEX].Value / manacost);
}
if (this->MaxMultiCast) {
castcount = std::min<int>(castcount, this->MaxMultiCast);
}
caster.Variable[MANA_INDEX].Value -= castcount * manacost;
if (hp < 0) {
if (&caster != target) {
HitUnit(&caster, *target, -(castcount * hp));
} else {
target->Variable[HP_INDEX].Value += castcount * hp;
if (target->Variable[HP_INDEX].Value < 0) {
target->Variable[HP_INDEX].Value = 0;
}
}
} else {
target->Variable[HP_INDEX].Value += castcount * hp;
if (target->Variable[HP_INDEX].Value > target->Variable[HP_INDEX].Max) {
target->Variable[HP_INDEX].Value = target->Variable[HP_INDEX].Max;
}
}
target->Variable[MANA_INDEX].Value += castcount * mana;
if (target->Variable[MANA_INDEX].Value < 0) {
target->Variable[MANA_INDEX].Value = 0;
}
if (target->Variable[MANA_INDEX].Value > target->Variable[MANA_INDEX].Max) {
target->Variable[MANA_INDEX].Value = target->Variable[MANA_INDEX].Max;
}
if (spell.RepeatCast) {
return 1;
}
return 0;
}
//@}

View file

@ -0,0 +1,87 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name spell_areaadjustvital.cpp - The spell AreaAdjustVital. */
//
// (c) Copyright 1998-2012 by Vladi Belperchinov-Shabanski, Lutz Sammer,
// Jimmy Salmon, and Joris DAUPHIN
//
// 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.
//
//@{
#include "stratagus.h"
#include "spells.h"
#include "map.h"
#include "unit.h"
/**
** Cast Area Adjust Vital on all valid units in range.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos TilePos of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int AreaAdjustVital::Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &goalPos)
{
const Vec2i range(spell.Range, spell.Range);
const Vec2i typeSize(caster.Type->Width, caster.Type->Height);
std::vector<CUnit *> units;
// Get all the units around the unit
Map.Select(goalPos - range, goalPos + typeSize + range, units);
int hp = this->HP;
int mana = this->Mana;
caster.Variable[MANA_INDEX].Value -= spell.ManaCost;
for (size_t j = 0; j != units.size(); ++j) {
target = units[j];
// if (!PassCondition(caster, spell, target, goalPos) {
if (!CanCastSpell(caster, spell, target, goalPos)) {
continue;
}
if (hp < 0) {
HitUnit(&caster, *target, -hp);
} else {
target->Variable[HP_INDEX].Value += hp;
if (target->Variable[HP_INDEX].Value > target->Variable[HP_INDEX].Max) {
target->Variable[HP_INDEX].Value = target->Variable[HP_INDEX].Max;
}
}
target->Variable[MANA_INDEX].Value += mana;
if (target->Variable[MANA_INDEX].Value < 0) {
target->Variable[MANA_INDEX].Value = 0;
}
if (target->Variable[MANA_INDEX].Value > target->Variable[MANA_INDEX].Max) {
target->Variable[MANA_INDEX].Value = target->Variable[MANA_INDEX].Max;
}
}
return 0;
}
//@}

View file

@ -0,0 +1,91 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name spell_areabombardment.cpp - The spell AreaBombardment. */
//
// (c) Copyright 1998-2012 by Vladi Belperchinov-Shabanski, Lutz Sammer,
// Jimmy Salmon, and Joris DAUPHIN
//
// 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.
//
//@{
#include "stratagus.h"
#include "spells.h"
#include "map.h"
#include "missile.h"
#include "unit.h"
/**
** Cast area bombardment.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos TilePos of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
** @internal: vladi: blizzard differs than original in this way:
** original: launches 50 shards at 5 random spots x 10 for 25 mana.
*/
int AreaBombardment::Cast(CUnit &caster, const SpellType &, CUnit *, const Vec2i &goalPos)
{
int fields = this->Fields;
const int shards = this->Shards;
const int damage = this->Damage;
const PixelDiff offset(this->StartOffsetX, this->StartOffsetY);
const MissileType *missile = this->Missile;
while (fields--) {
Vec2i dpos;
// FIXME: radius configurable...
do {
// find new destination in the map
dpos.x = goalPos.x + SyncRand() % 5 - 2;
dpos.y = goalPos.y + SyncRand() % 5 - 2;
} while (!Map.Info.IsPointOnMap(dpos));
const PixelPos dest = Map.TilePosToMapPixelPos_Center(dpos);
const PixelPos start = dest + offset;
for (int i = 0; i < shards; ++i) {
::Missile *mis = MakeMissile(*missile, start, dest);
// FIXME: This is just patched up, it works, but I have no idea why.
// FIXME: What is the reasoning behind all this?
if (mis->Type->Speed) {
mis->Delay = i * mis->Type->Sleep * 2 * PixelTileSize.x / mis->Type->Speed;
} else {
mis->Delay = i * mis->Type->Sleep * mis->Type->G->NumFrames;
}
mis->Damage = damage;
// FIXME: not correct -- blizzard should continue even if mage is
// destroyed (though it will be quite short time...)
mis->SourceUnit = &caster;
}
}
return 1;
}
//@}

View file

@ -0,0 +1,99 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name spell_capture.cpp - The spell Capture. */
//
// (c) Copyright 1998-2012 by Vladi Belperchinov-Shabanski, Lutz Sammer,
// Jimmy Salmon, and Joris DAUPHIN
//
// 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.
//
//@{
#include "stratagus.h"
#include "spells.h"
#include "unit.h"
/**
** Cast capture.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos coord of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int Capture::Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &/*goalPos*/)
{
if (!target || caster.Player == target->Player) {
return 0;
}
if (this->DamagePercent) {
if ((100 * target->Variable[HP_INDEX].Value) /
target->Variable[HP_INDEX].Max > this->DamagePercent &&
target->Variable[HP_INDEX].Value > this->Damage) {
HitUnit(&caster, *target, this->Damage);
if (this->SacrificeEnable) {
// No corpse.
caster.Remove(NULL);
UnitLost(caster);
UnitClearOrders(caster);
}
return 1;
}
}
caster.Player->Score += target->Variable[POINTS_INDEX].Value;
if (caster.IsEnemy(*target)) {
if (target->Type->Building) {
caster.Player->TotalRazings++;
} else {
caster.Player->TotalKills++;
}
if (UseHPForXp) {
caster.Variable[XP_INDEX].Max += target->Variable[HP_INDEX].Value;
} else {
caster.Variable[XP_INDEX].Max += target->Variable[POINTS_INDEX].Value;
}
caster.Variable[XP_INDEX].Value = caster.Variable[XP_INDEX].Max;
caster.Variable[KILL_INDEX].Value++;
caster.Variable[KILL_INDEX].Max++;
caster.Variable[KILL_INDEX].Enable = 1;
}
target->ChangeOwner(*caster.Player);
if (this->SacrificeEnable) {
// No corpse.
caster.Remove(NULL);
UnitLost(caster);
UnitClearOrders(caster);
} else {
caster.Variable[MANA_INDEX].Value -= spell.ManaCost;
}
UnitClearOrders(*target);
return 0;
}
//@}

View file

@ -0,0 +1,98 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name spell_demolish.cpp - The spell demolish. */
//
// (c) Copyright 1998-2012 by Vladi Belperchinov-Shabanski, Lutz Sammer,
// Jimmy Salmon, and Joris DAUPHIN
//
// 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.
//
//@{
#include "stratagus.h"
#include "spells.h"
#include "map.h"
/**
** Cast demolish
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos tilePos of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int Demolish::Cast(CUnit &caster, const SpellType &, CUnit *, const Vec2i &goalPos)
{
// Allow error margins. (Lame, I know)
const Vec2i offset(this->Range + 2, this->Range + 2);
Vec2i minpos = goalPos - offset;
Vec2i maxpos = goalPos + offset;
Map.FixSelectionArea(minpos, maxpos);
//
// Terrain effect of the explosion
//
Vec2i ipos;
for (ipos.x = minpos.x; ipos.x <= maxpos.x; ++ipos.x) {
for (ipos.y = minpos.y; ipos.y <= maxpos.y; ++ipos.y) {
const int flag = Map.Field(ipos)->Flags;
if (SquareDistance(ipos, goalPos) > square(this->Range)) {
// Not in circle range
continue;
} else if (flag & MapFieldWall) {
Map.RemoveWall(ipos);
} else if (flag & MapFieldRocks) {
Map.ClearTile(MapFieldRocks, ipos);
} else if (flag & MapFieldForest) {
Map.ClearTile(MapFieldForest, ipos);
}
}
}
//
// Effect of the explosion on units. Don't bother if damage is 0
//
if (this->Damage) {
std::vector<CUnit *> table;
Map.SelectFixed(minpos, maxpos, table);
for (size_t i = 0; i != table.size(); ++i) {
CUnit &unit = *table[i];
if (unit.Type->UnitType != UnitTypeFly && unit.IsAlive()
&& unit.MapDistanceTo(goalPos) <= this->Range) {
// Don't hit flying units!
HitUnit(&caster, unit, this->Damage);
}
}
}
return 1;
}
//@}

View file

@ -0,0 +1,101 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name spell_polymorph.cpp - The spell Polymorph. */
//
// (c) Copyright 1998-2012 by Vladi Belperchinov-Shabanski, Lutz Sammer,
// Jimmy Salmon, and Joris DAUPHIN
//
// 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.
//
//@{
#include "stratagus.h"
#include "spells.h"
#include "map.h"
#include "unit.h"
/**
** Cast polymorph.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos coord of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int Polymorph::Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &goalPos)
{
if (!target) {
return 0;
}
CUnitType &type = *this->NewForm;
const Vec2i pos(goalPos - type.GetHalfTileSize());
caster.Player->Score += target->Variable[POINTS_INDEX].Value;
if (caster.IsEnemy(*target)) {
if (target->Type->Building) {
caster.Player->TotalRazings++;
} else {
caster.Player->TotalKills++;
}
if (UseHPForXp) {
caster.Variable[XP_INDEX].Max += target->Variable[HP_INDEX].Value;
} else {
caster.Variable[XP_INDEX].Max += target->Variable[POINTS_INDEX].Value;
}
caster.Variable[XP_INDEX].Value = caster.Variable[XP_INDEX].Max;
caster.Variable[KILL_INDEX].Value++;
caster.Variable[KILL_INDEX].Max++;
caster.Variable[KILL_INDEX].Enable = 1;
}
// as said somewhere else -- no corpses :)
target->Remove(NULL);
Vec2i offset;
for (offset.x = 0; offset.x < type.TileWidth; ++offset.x) {
for (offset.y = 0; offset.y < type.TileHeight; ++offset.y) {
if (!UnitTypeCanBeAt(type, pos + offset)) {
target->Place(target->tilePos);
return 0;
}
}
}
caster.Variable[MANA_INDEX].Value -= spell.ManaCost;
if (this->PlayerNeutral == 1) {
MakeUnitAndPlace(pos, type, Players + PlayerNumNeutral);
} else if (this->PlayerNeutral == 2) {
MakeUnitAndPlace(pos, type, caster.Player);
} else {
MakeUnitAndPlace(pos, type, target->Player);
}
UnitLost(*target);
UnitClearOrders(*target);
target->Release();
return 1;
}
//@}

View file

@ -0,0 +1,106 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name spell_spawnmissile.cpp - The spell SpawnMissile. */
//
// (c) Copyright 1998-2012 by Vladi Belperchinov-Shabanski, Lutz Sammer,
// Jimmy Salmon, and Joris DAUPHIN
//
// 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.
//
//@{
#include "stratagus.h"
#include "spells.h"
#include "map.h"
#include "missile.h"
#include "unit.h"
/**
** Evaluate missile location description.
**
** @param location Parameters for location.
** @param caster Unit that casts the spell
** @param target Target unit that spell is addressed to
** @param goalPos TilePos of target spot when/if target does not exist
** @param res pointer to PixelPos of the result
*/
static void EvaluateMissileLocation(const SpellActionMissileLocation &location,
CUnit &caster, CUnit *target, const Vec2i &goalPos, PixelPos *res)
{
if (location.Base == LocBaseCaster) {
*res = caster.GetMapPixelPosCenter();
} else {
if (target) {
*res = target->GetMapPixelPosCenter();
} else {
*res = Map.TilePosToMapPixelPos_Center(goalPos);
}
}
res->x += location.AddX;
if (location.AddRandX) {
res->x += SyncRand() % location.AddRandX;
}
res->y += location.AddY;
if (location.AddRandY) {
res->y += SyncRand() % location.AddRandY;
}
}
/**
** Cast spawn missile.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos TilePos of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int SpawnMissile::Cast(CUnit &caster, const SpellType &, CUnit *target, const Vec2i &goalPos)
{
PixelPos startPos;
PixelPos endPos;
EvaluateMissileLocation(this->StartPoint, caster, target, goalPos, &startPos);
EvaluateMissileLocation(this->EndPoint, caster, target, goalPos, &endPos);
::Missile *missile = MakeMissile(*this->Missile, startPos, endPos);
missile->TTL = this->TTL;
missile->Delay = this->Delay;
missile->Damage = this->Damage;
if (this->UseUnitVar) {
missile->Damage = 0;
missile->SourceUnit = &caster;
} else if (missile->Damage != 0) {
missile->SourceUnit = &caster;
}
missile->TargetUnit = target;
return 1;
}
//@}

View file

@ -0,0 +1,68 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name spell_spawnportal.cpp - The spell SpawnPortal. */
//
// (c) Copyright 1998-2012 by Vladi Belperchinov-Shabanski, Lutz Sammer,
// Jimmy Salmon, and Joris DAUPHIN
//
// 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.
//
//@{
#include "stratagus.h"
#include "spells.h"
#include "unit.h"
/**
** Cast circle of power.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos tilePos of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int SpawnPortal::Cast(CUnit &caster, const SpellType &, CUnit *, const Vec2i &goalPos)
{
// FIXME: vladi: cop should be placed only on explored land
CUnit *portal = caster.Goal;
DebugPrint("Spawning a portal exit.\n");
if (portal) {
portal->MoveToXY(goalPos);
} else {
portal = MakeUnitAndPlace(goalPos, *this->PortalType, &Players[PlayerNumNeutral]);
}
// Goal is used to link to destination circle of power
caster.Goal = portal;
//FIXME: setting destination circle of power should use mana
return 0;
}
//@}

112
src/spell/spell_summon.cpp Normal file
View file

@ -0,0 +1,112 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name spell_summon.cpp - The spell Summon. */
//
// (c) Copyright 1998-2012 by Vladi Belperchinov-Shabanski, Lutz Sammer,
// Jimmy Salmon, and Joris DAUPHIN
//
// 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.
//
//@{
#include "stratagus.h"
#include "spells.h"
#include "actions.h"
#include "map.h"
#include "unit.h"
class IsDyingAndNotABuilding
{
public:
bool operator()(const CUnit *unit) const {
return unit->CurrentAction() == UnitActionDie && !unit->Type->Building;
}
};
/**
** Cast summon spell.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos coord of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int Summon::Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &goalPos)
{
Vec2i pos = goalPos;
bool cansummon;
CUnitType &unittype = *this->UnitType;
int ttl = this->TTL;
if (this->RequireCorpse) {
const Vec2i offset(1, 1);
const Vec2i minPos = pos - offset;
const Vec2i maxPos = pos + offset;
CUnit *unit = Map.Find_If(minPos, maxPos, IsDyingAndNotABuilding());
cansummon = false;
if (unit != NULL) { // Found a corpse. eliminate it and proceed to summoning.
pos = unit->tilePos;
unit->Remove(NULL);
unit->Release();
cansummon = true;
}
} else {
cansummon = true;
}
if (cansummon) {
DebugPrint("Summoning a %s\n" _C_ unittype.Name.c_str());
//
// Create units.
// FIXME: do summoned units count on food?
//
target = MakeUnit(unittype, caster.Player);
if (target != NULL) {
target->tilePos = pos;
DropOutOnSide(*target, LookingW, NULL);
//
// set life span. ttl=0 results in a permanent unit.
//
if (ttl) {
target->TTL = GameCycle + ttl;
}
caster.Variable[MANA_INDEX].Value -= spell.ManaCost;
} else {
DebugPrint("Unable to allocate Unit");
}
return 1;
}
return 0;
}
//@}

View file

@ -10,7 +10,7 @@
//
/**@name spells.cpp - The spell cast action. */
//
// (c) Copyright 1998-2006 by Vladi Belperchinov-Shabanski, Lutz Sammer,
// (c) Copyright 1998-2012 by Vladi Belperchinov-Shabanski, Lutz Sammer,
// Jimmy Salmon, and Joris DAUPHIN
//
// This program is free software; you can redistribute it and/or modify
@ -38,10 +38,6 @@
//@{
/*----------------------------------------------------------------------------
-- Notes
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
-- Includes
----------------------------------------------------------------------------*/
@ -50,14 +46,9 @@
#include "spells.h"
#include "actions.h"
#include "commands.h"
#include "map.h"
#include "missile.h"
#include "sound.h"
#include "tileset.h"
#include "ui.h"
#include "unittype.h"
#include "upgrade.h"
/*----------------------------------------------------------------------------
@ -74,576 +65,6 @@ std::vector<SpellType *> SpellTypeTable;
-- Functions
----------------------------------------------------------------------------*/
// ****************************************************************************
// Cast the Spell
// ****************************************************************************
/**
** Cast demolish
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos tilePos of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int Demolish::Cast(CUnit &caster, const SpellType &, CUnit *, const Vec2i &goalPos)
{
// Allow error margins. (Lame, I know)
const Vec2i offset(this->Range + 2, this->Range + 2);
Vec2i minpos = goalPos - offset;
Vec2i maxpos = goalPos + offset;
Map.FixSelectionArea(minpos, maxpos);
//
// Terrain effect of the explosion
//
Vec2i ipos;
for (ipos.x = minpos.x; ipos.x <= maxpos.x; ++ipos.x) {
for (ipos.y = minpos.y; ipos.y <= maxpos.y; ++ipos.y) {
const int flag = Map.Field(ipos)->Flags;
if (SquareDistance(ipos, goalPos) > square(this->Range)) {
// Not in circle range
continue;
} else if (flag & MapFieldWall) {
Map.RemoveWall(ipos);
} else if (flag & MapFieldRocks) {
Map.ClearTile(MapFieldRocks, ipos);
} else if (flag & MapFieldForest) {
Map.ClearTile(MapFieldForest, ipos);
}
}
}
//
// Effect of the explosion on units. Don't bother if damage is 0
//
if (this->Damage) {
std::vector<CUnit *> table;
Map.SelectFixed(minpos, maxpos, table);
for (size_t i = 0; i != table.size(); ++i) {
CUnit &unit = *table[i];
if (unit.Type->UnitType != UnitTypeFly && unit.IsAlive()
&& unit.MapDistanceTo(goalPos) <= this->Range) {
// Don't hit flying units!
HitUnit(&caster, unit, this->Damage);
}
}
}
return 1;
}
/**
** Cast circle of power.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos tilePos of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int SpawnPortal::Cast(CUnit &caster, const SpellType &, CUnit *, const Vec2i &goalPos)
{
// FIXME: vladi: cop should be placed only on explored land
CUnit *portal = caster.Goal;
DebugPrint("Spawning a portal exit.\n");
if (portal) {
portal->MoveToXY(goalPos);
} else {
portal = MakeUnitAndPlace(goalPos, *this->PortalType, &Players[PlayerNumNeutral]);
}
// Goal is used to link to destination circle of power
caster.Goal = portal;
//FIXME: setting destination circle of power should use mana
return 0;
}
/**
** Cast Area Adjust Vitals on all valid units in range.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos TilePos of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int AreaAdjustVitals::Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &goalPos)
{
const Vec2i range(spell.Range, spell.Range);
const Vec2i typeSize(caster.Type->Width, caster.Type->Height);
std::vector<CUnit *> units;
// Get all the units around the unit
Map.Select(goalPos - range, goalPos + typeSize + range, units);
int hp = this->HP;
int mana = this->Mana;
caster.Variable[MANA_INDEX].Value -= spell.ManaCost;
for (size_t j = 0; j != units.size(); ++j) {
target = units[j];
// if (!PassCondition(caster, spell, target, goalPos) {
if (!CanCastSpell(caster, spell, target, goalPos)) {
continue;
}
if (hp < 0) {
HitUnit(&caster, *target, -hp);
} else {
target->Variable[HP_INDEX].Value += hp;
if (target->Variable[HP_INDEX].Value > target->Variable[HP_INDEX].Max) {
target->Variable[HP_INDEX].Value = target->Variable[HP_INDEX].Max;
}
}
target->Variable[MANA_INDEX].Value += mana;
if (target->Variable[MANA_INDEX].Value < 0) {
target->Variable[MANA_INDEX].Value = 0;
}
if (target->Variable[MANA_INDEX].Value > target->Variable[MANA_INDEX].Max) {
target->Variable[MANA_INDEX].Value = target->Variable[MANA_INDEX].Max;
}
}
return 0;
}
/**
** Cast area bombardment.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos TilePos of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
** @internal: vladi: blizzard differs than original in this way:
** original: launches 50 shards at 5 random spots x 10 for 25 mana.
*/
int AreaBombardment::Cast(CUnit &caster, const SpellType &, CUnit *, const Vec2i &goalPos)
{
int fields = this->Fields;
const int shards = this->Shards;
const int damage = this->Damage;
const PixelDiff offset(this->StartOffsetX, this->StartOffsetY);
const MissileType *missile = this->Missile;
while (fields--) {
Vec2i dpos;
// FIXME: radius configurable...
do {
// find new destination in the map
dpos.x = goalPos.x + SyncRand() % 5 - 2;
dpos.y = goalPos.y + SyncRand() % 5 - 2;
} while (!Map.Info.IsPointOnMap(dpos));
const PixelPos dest = Map.TilePosToMapPixelPos_Center(dpos);
const PixelPos start = dest + offset;
for (int i = 0; i < shards; ++i) {
::Missile *mis = MakeMissile(*missile, start, dest);
// FIXME: This is just patched up, it works, but I have no idea why.
// FIXME: What is the reasoning behind all this?
if (mis->Type->Speed) {
mis->Delay = i * mis->Type->Sleep * 2 * PixelTileSize.x / mis->Type->Speed;
} else {
mis->Delay = i * mis->Type->Sleep * mis->Type->G->NumFrames;
}
mis->Damage = damage;
// FIXME: not correct -- blizzard should continue even if mage is
// destroyed (though it will be quite short time...)
mis->SourceUnit = &caster;
}
}
return 1;
}
/**
** Evaluate missile location description.
**
** @param location Parameters for location.
** @param caster Unit that casts the spell
** @param target Target unit that spell is addressed to
** @param goalPos TilePos of target spot when/if target does not exist
** @param res pointer to PixelPos of the result
*/
static void EvaluateMissileLocation(const SpellActionMissileLocation &location,
CUnit &caster, CUnit *target, const Vec2i &goalPos, PixelPos *res)
{
if (location.Base == LocBaseCaster) {
*res = caster.GetMapPixelPosCenter();
} else {
if (target) {
*res = target->GetMapPixelPosCenter();
} else {
*res = Map.TilePosToMapPixelPos_Center(goalPos);
}
}
res->x += location.AddX;
if (location.AddRandX) {
res->x += SyncRand() % location.AddRandX;
}
res->y += location.AddY;
if (location.AddRandY) {
res->y += SyncRand() % location.AddRandY;
}
}
/**
** Cast spawn missile.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos TilePos of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int SpawnMissile::Cast(CUnit &caster, const SpellType &, CUnit *target, const Vec2i &goalPos)
{
PixelPos startPos;
PixelPos endPos;
EvaluateMissileLocation(this->StartPoint, caster, target, goalPos, &startPos);
EvaluateMissileLocation(this->EndPoint, caster, target, goalPos, &endPos);
::Missile *missile = MakeMissile(*this->Missile, startPos, endPos);
missile->TTL = this->TTL;
missile->Delay = this->Delay;
missile->Damage = this->Damage;
if (this->UseUnitVar) {
missile->Damage = 0;
missile->SourceUnit = &caster;
} else if (missile->Damage != 0) {
missile->SourceUnit = &caster;
}
missile->TargetUnit = target;
return 1;
}
/**
** Adjust User Variables.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target
** @param goalPos coord of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int AdjustVariable::Cast(CUnit &caster, const SpellType &, CUnit *target, const Vec2i &/*goalPos*/)
{
for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); ++i) {
CUnit *unit = (this->Var[i].TargetIsCaster) ? &caster : target;
if (!unit) {
continue;
}
// Enable flag.
if (this->Var[i].ModifEnable) {
unit->Variable[i].Enable = this->Var[i].Enable;
}
unit->Variable[i].Enable ^= this->Var[i].InvertEnable;
// Max field
if (this->Var[i].ModifMax) {
unit->Variable[i].Max = this->Var[i].Max;
}
unit->Variable[i].Max += this->Var[i].AddMax;
// Increase field
if (this->Var[i].ModifIncrease) {
unit->Variable[i].Increase = this->Var[i].Increase;
}
unit->Variable[i].Increase += this->Var[i].AddIncrease;
// Value field
if (this->Var[i].ModifValue) {
unit->Variable[i].Value = this->Var[i].Value;
}
unit->Variable[i].Value += this->Var[i].AddValue;
unit->Variable[i].Value += this->Var[i].IncreaseTime * unit->Variable[i].Increase;
clamp(&unit->Variable[i].Value, 0, unit->Variable[i].Max);
}
return 1;
}
/**
** Cast healing. (or exorcism)
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos coord of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int AdjustVitals::Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &/*goalPos*/)
{
if (!target) {
return 0;
}
const int hp = this->HP;
const int mana = this->Mana;
const int manacost = spell.ManaCost;
int diffHP;
int diffMana;
// Healing and harming
if (hp > 0) {
diffHP = target->Variable[HP_INDEX].Max - target->Variable[HP_INDEX].Value;
} else {
diffHP = target->Variable[HP_INDEX].Value;
}
if (mana > 0) {
diffMana = target->Stats->Variables[MANA_INDEX].Max - target->Variable[MANA_INDEX].Value;
} else {
diffMana = target->Variable[MANA_INDEX].Value;
}
// When harming cast again to send the hp to negative values.
// Carefull, a perfect 0 target hp kills too.
// Avoid div by 0 errors too!
int castcount = 1;
if (hp) {
castcount = std::max<int>(castcount,
diffHP / abs(hp) + (((hp < 0) && (diffHP % (-hp) > 0)) ? 1 : 0));
}
if (mana) {
castcount = std::max<int>(castcount,
diffMana / abs(mana) + (((mana < 0) && (diffMana % (-mana) > 0)) ? 1 : 0));
}
if (manacost) {
castcount = std::min<int>(castcount, caster.Variable[MANA_INDEX].Value / manacost);
}
if (this->MaxMultiCast) {
castcount = std::min<int>(castcount, this->MaxMultiCast);
}
caster.Variable[MANA_INDEX].Value -= castcount * manacost;
if (hp < 0) {
if (&caster != target) {
HitUnit(&caster, *target, -(castcount * hp));
} else {
target->Variable[HP_INDEX].Value += castcount * hp;
if (target->Variable[HP_INDEX].Value < 0) {
target->Variable[HP_INDEX].Value = 0;
}
}
} else {
target->Variable[HP_INDEX].Value += castcount * hp;
if (target->Variable[HP_INDEX].Value > target->Variable[HP_INDEX].Max) {
target->Variable[HP_INDEX].Value = target->Variable[HP_INDEX].Max;
}
}
target->Variable[MANA_INDEX].Value += castcount * mana;
if (target->Variable[MANA_INDEX].Value < 0) {
target->Variable[MANA_INDEX].Value = 0;
}
if (target->Variable[MANA_INDEX].Value > target->Variable[MANA_INDEX].Max) {
target->Variable[MANA_INDEX].Value = target->Variable[MANA_INDEX].Max;
}
if (spell.RepeatCast) {
return 1;
}
return 0;
}
/**
** Cast polymorph.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos coord of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int Polymorph::Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &goalPos)
{
if (!target) {
return 0;
}
CUnitType &type = *this->NewForm;
const Vec2i pos(goalPos - type.GetHalfTileSize());
caster.Player->Score += target->Variable[POINTS_INDEX].Value;
if (caster.IsEnemy(*target)) {
if (target->Type->Building) {
caster.Player->TotalRazings++;
} else {
caster.Player->TotalKills++;
}
if (UseHPForXp) {
caster.Variable[XP_INDEX].Max += target->Variable[HP_INDEX].Value;
} else {
caster.Variable[XP_INDEX].Max += target->Variable[POINTS_INDEX].Value;
}
caster.Variable[XP_INDEX].Value = caster.Variable[XP_INDEX].Max;
caster.Variable[KILL_INDEX].Value++;
caster.Variable[KILL_INDEX].Max++;
caster.Variable[KILL_INDEX].Enable = 1;
}
// as said somewhere else -- no corpses :)
target->Remove(NULL);
Vec2i offset;
for (offset.x = 0; offset.x < type.TileWidth; ++offset.x) {
for (offset.y = 0; offset.y < type.TileHeight; ++offset.y) {
if (!UnitTypeCanBeAt(type, pos + offset)) {
target->Place(target->tilePos);
return 0;
}
}
}
caster.Variable[MANA_INDEX].Value -= spell.ManaCost;
if (this->PlayerNeutral == 1) {
MakeUnitAndPlace(pos, type, Players + PlayerNumNeutral);
} else if (this->PlayerNeutral == 2) {
MakeUnitAndPlace(pos, type, caster.Player);
} else {
MakeUnitAndPlace(pos, type, target->Player);
}
UnitLost(*target);
UnitClearOrders(*target);
target->Release();
return 1;
}
/**
** Cast capture.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos coord of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int Capture::Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &/*goalPos*/)
{
if (!target || caster.Player == target->Player) {
return 0;
}
if (this->DamagePercent) {
if ((100 * target->Variable[HP_INDEX].Value) /
target->Variable[HP_INDEX].Max > this->DamagePercent &&
target->Variable[HP_INDEX].Value > this->Damage) {
HitUnit(&caster, *target, this->Damage);
if (this->SacrificeEnable) {
// No corpse.
caster.Remove(NULL);
UnitLost(caster);
UnitClearOrders(caster);
}
return 1;
}
}
caster.Player->Score += target->Variable[POINTS_INDEX].Value;
if (caster.IsEnemy(*target)) {
if (target->Type->Building) {
caster.Player->TotalRazings++;
} else {
caster.Player->TotalKills++;
}
if (UseHPForXp) {
caster.Variable[XP_INDEX].Max += target->Variable[HP_INDEX].Value;
} else {
caster.Variable[XP_INDEX].Max += target->Variable[POINTS_INDEX].Value;
}
caster.Variable[XP_INDEX].Value = caster.Variable[XP_INDEX].Max;
caster.Variable[KILL_INDEX].Value++;
caster.Variable[KILL_INDEX].Max++;
caster.Variable[KILL_INDEX].Enable = 1;
}
target->ChangeOwner(*caster.Player);
if (this->SacrificeEnable) {
// No corpse.
caster.Remove(NULL);
UnitLost(caster);
UnitClearOrders(caster);
} else {
caster.Variable[MANA_INDEX].Value -= spell.ManaCost;
}
UnitClearOrders(*target);
return 0;
}
class IsDyingAndNotABuilding
{
public:
bool operator()(const CUnit *unit) const {
return unit->CurrentAction() == UnitActionDie && !unit->Type->Building;
}
};
/**
** Cast summon spell.
**
** @param caster Unit that casts the spell
** @param spell Spell-type pointer
** @param target Target unit that spell is addressed to
** @param goalPos coord of target spot when/if target does not exist
**
** @return =!0 if spell should be repeated, 0 if not
*/
int Summon::Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &goalPos)
{
Vec2i pos = goalPos;
bool cansummon;
CUnitType &unittype = *this->UnitType;
int ttl = this->TTL;
if (this->RequireCorpse) {
const Vec2i offset(1, 1);
const Vec2i minPos = pos - offset;
const Vec2i maxPos = pos + offset;
CUnit *unit = Map.Find_If(minPos, maxPos, IsDyingAndNotABuilding());
cansummon = false;
if (unit != NULL) { // Found a corpse. eliminate it and proceed to summoning.
pos = unit->tilePos;
unit->Remove(NULL);
unit->Release();
cansummon = true;
}
} else {
cansummon = true;
}
if (cansummon) {
DebugPrint("Summoning a %s\n" _C_ unittype.Name.c_str());
//
// Create units.
// FIXME: do summoned units count on food?
//
target = MakeUnit(unittype, caster.Player);
if (target != NULL) {
target->tilePos = pos;
DropOutOnSide(*target, LookingW, NULL);
//
// set life span. ttl=0 results in a permanent unit.
//
if (ttl) {
target->TTL = GameCycle + ttl;
}
caster.Variable[MANA_INDEX].Value -= spell.ManaCost;
} else {
DebugPrint("Unable to allocate Unit");
}
return 1;
}
return 0;
}
// ****************************************************************************
// Target constructor
// ****************************************************************************