Apply patch #3083 by Brilliand

This commit is contained in:
Alexander van Gessel 2012-03-09 18:13:04 +01:00
parent 34647d1a9a
commit 71ef9ab0a2
7 changed files with 460 additions and 142 deletions

View file

@ -686,6 +686,11 @@ Version 1.9.9:
* Added support for 24 hour ToD.
* Added WML validation system based on schema validation.
* Enabled validation for GUI WML.
* The [drain] weapon special now supports value=, multiply=, divide=, add= and sub=
* Added [heal_on_hit] for healing/harming the user by a fixed amount
* Drained HP amounts can now be negative. Trigger this by setting
a negative value in the [drain] or [heal_on_hit] weapon special.
* Negative drain amounts will not take a unit below 1 health.
* Miscellaneous and bug fixes:
* Teach wmllint to fix deprecated implicit side=1 in [store_gold], [gold]
[remove_shroud], [place_shroud], [modify_side], [modify_ai] actions

View file

@ -195,7 +195,165 @@ Xu , Xu , Qxu , Qxu , Ql , Ql
[/effect]
[effect]
apply_to=attack
name=chill wave
increase_attacks=12
[set_specials]
mode=append
[heal_on_hit]
id=sacrifice
name="Sacrifice"
description="Sacrifice:
This unit sacrifices its own life in order to make more attacks."
value=-5
[/heal_on_hit]
[/set_specials]
[/effect]
[effect]
apply_to=attack
name=shadow wave
increase_attacks=18
[set_specials]
mode=append
[heal_on_hit]
id=sacrifice
name="Greater Sacrifice"
description="Greater Sacrifice:
This unit sacrifices its own life in order to make more attacks."
value=-6
[/heal_on_hit]
[/set_specials]
[/effect]
[/object]
[/modifications]
[/unit]
[unit]
# Drain tester
x,y=20,7
type="Elvish Champion"
generate_name=yes
[modifications]
[object]
silent=yes
[effect]
apply_to=remove_attacks
[/effect]
[effect]
apply_to=new_attack
name=sword
description=_"sword"
icon=attacks/greatsword-elven.png
type=blade
[specials]
[drains]
id=life_vortex
name= _ "Life Vortex"
description= _ "Life Vortex:
Each successful strike in a battle involving this attack causes the attacker to gain the health that his attack removed from the target. This has no effect against nonliving targets."
value=0
add=100
apply_to=both
[/drains]
[heal_on_hit]
id=entropy
name= _ "Entropy"
description= _ "Entropy:
Each successful strike in a battle involving this attack causes the attacker to lose 4 HP. This has no effect against nonliving targets."
[filter_opponent]
[not]
[filter_wml]
[status]
not_living="yes"
[/status]
[/filter_wml]
[/not]
[/filter_opponent]
sub=4
apply_to=both
[/heal_on_hit]
[/specials]
range=melee
damage=9
number=5
[/effect]
[effect]
apply_to=new_attack
name=bow
description=_"bow"
icon=attacks/bow-elven.png
type=pierce
[specials]
[drains]
id=empathic_weapon
name= _ "Empathic Weapon"
description= _ "Empathic Weapon:
All damage dealt by this attack to a living target is dealt to the user as well"
multiply=-2
[/drains]
[heal_on_hit]
id=rejuvenating_weapon
name= _ "Rejuvenating Weapon (10)"
description= _ "Rejuvenating Weapon (10):
Each successful strike with this weapon heals the user for 10 HP"
value=10
[/heal_on_hit]
[/specials]
range=ranged
damage=9
number=3
[/effect]
[effect]
apply_to=new_attack
name=longbow
description=_"longbow"
icon=attacks/bow-elven-magic.png
type=pierce
[specials]
{WEAPON_SPECIAL_MARKSMAN}
[drains]
id=drains
name= _ "drains 20%"
description= _ "Drain 20%:
This unit drains health from living units, healing itself for 20% of the amount of damage it deals (rounded down)."
value=20
[/drains]
[heal_on_hit]
id=drains
name= _ "sacrifice 5"
description= _ "Sacrifice 5:
Each successful attack costs this unit 5 HP."
sub=5
[/heal_on_hit]
[/specials]
range=ranged
damage=10
number=5
[/effect]
[effect]
apply_to=new_attack
name=life bomb
description=_"life bomb"
type=impact
[specials]
[heal_on_hit]
id=drains
name= _ "life copy 20"
description= _ "Life copy 20:
When this attack hits a living unit, the user is healed for 20 HP."
[filter_opponent]
[not]
[filter_wml]
[status]
not_living="yes"
[/status]
[/filter_wml]
[/not]
[/filter_opponent]
value=20
[/heal_on_hit]
[/specials]
damage=3
number=2
range=ranged
[/effect]
[/object]
[/modifications]
@ -370,6 +528,61 @@ Xu , Xu , Qxu , Qxu , Ql , Ql
[/object]
[/modifications]
[/unit]
[unit]
# Drain tester (for undead and default drain)
x,y=21,7
type="Lich"
generate_name=yes
[/unit]
[unit]
# Drain tester
x,y=21,8
type="Elvish Sharpshooter"
generate_name=yes
[modifications]
[object]
silent=yes
[effect]
apply_to=attack
name=sword
[set_specials]
mode=append
[heal_on_hit]
id=fiery
name= _ "fiery 4"
name_inactive= _ "fiery 4"
description= _ "Fiery 4:
A successful hit against this unit costs the opponent 4 HP."
description_inactive= _ "Fiery 4:
A successful hit against this unit costs the opponent 4 HP."
sub=4
apply_to=opponent
[/heal_on_hit]
[/set_specials]
[/effect]
[effect]
apply_to=new_attack
name=lifebow
description=_"lifebow"
icon=attacks/bow-elven-magic.png
type=pierce
[specials]
{WEAPON_SPECIAL_MARKSMAN}
[drains]
id=drains
name= _ "drains 100%"
description= _ "Drain 100%:
This unit drains health from living units, healing itself for 100% of the amount of damage it deals (rounded down)."
value=100
[/drains]
[/specials]
range=ranged
damage=4
number=5
[/effect]
[/object]
[/modifications]
[/unit]
[/side]
[side]
side=3

View file

@ -1041,6 +1041,8 @@ battle_context_unit_stats::battle_context_unit_stats(const unit &u, const map_lo
chance_to_hit(0),
damage(0),
slow_damage(0),
drain_percent(0),
drain_constant(0),
num_blows(0),
swarm_min(0),
swarm_max(0),
@ -1125,13 +1127,28 @@ battle_context_unit_stats::battle_context_unit_stats(const unit &u, const map_lo
// Resistance modifier.
damage_multiplier *= opp.damage_from(*weapon, !attacking, opp_loc);
// Compute both the normal and slowed damage. For the record,
// drain = normal damage / 2 and slow_drain = slow_damage / 2.
// Compute both the normal and slowed damage.
damage = round_damage(base_damage, damage_multiplier, 10000);
slow_damage = round_damage(base_damage, damage_multiplier, 20000);
if (is_slowed)
damage = slow_damage;
// Compute drain amounts only if draining is possible.
if(drains) {
unit_ability_list drain_specials = weapon->get_specials("drains");
// Compute the drain percent (with 50% as the base for backward compatibility)
unit_abilities::effect drain_percent_effects(drain_specials, 50, backstab_pos);
drain_percent = drain_percent_effects.get_composite_value();
}
// Add heal_on_hit (the drain constant)
unit_ability_list heal_on_hit_specials = weapon->get_specials("heal_on_hit");
unit_abilities::effect heal_on_hit_effects(heal_on_hit_specials, 0, backstab_pos);
drain_constant += heal_on_hit_effects.get_composite_value();
drains = drain_constant || drain_percent;
// Compute the number of blows and handle swarm.
unit_ability_list swarm_specials = weapon->get_specials("swarm");
@ -1180,6 +1197,8 @@ void battle_context_unit_stats::dump() const
printf("chance_to_hit: %d\n", chance_to_hit);
printf("damage: %d\n", damage);
printf("slow_damage: %d\n", slow_damage);
printf("drain_percent: %d\n", drain_percent);
printf("drain_constant: %d\n", drain_constant);
printf("num_blows: %d\n", num_blows);
printf("swarm_min: %d\n", swarm_min);
printf("swarm_max: %d\n", swarm_max);
@ -1230,6 +1249,10 @@ class attack
std::string dump();
};
void unit_killed(unit_info &, unit_info &,
const battle_context_unit_stats *&, const battle_context_unit_stats *&,
bool);
battle_context *bc_;
const battle_context_unit_stats *a_stats_;
const battle_context_unit_stats *d_stats_;
@ -1481,6 +1504,17 @@ bool attack::perform_hit(bool attacker_turn, statistics::attack_context &stats)
}
}
int damage_done = std::min<int>(defender.get_unit().hitpoints(), attacker.damage_);
int drains_damage = 0;
if (hits && attacker_stats->drains) {
drains_damage = damage_done * attacker_stats->drain_percent / 100 + attacker_stats->drain_constant;
// don't drain so much that the attacker gets more than his maximum hitpoints
drains_damage = std::min<int>(drains_damage, attacker.get_unit().max_hitpoints() - attacker.get_unit().hitpoints());
// if drain is negative, don't allow drain to kill the attacker
drains_damage = std::max<int>(drains_damage, 1 - attacker.get_unit().hitpoints());
}
if (update_display_)
{
std::ostringstream float_text;
@ -1505,18 +1539,9 @@ bool attack::perform_hit(bool attacker_turn, statistics::attack_context &stats)
unit_display::unit_attack(attacker.loc_, defender.loc_, damage,
*attacker_stats->weapon, defender_stats->weapon,
abs_n, float_text.str(), attacker_stats->drains, "");
abs_n, float_text.str(), drains_damage, "");
}
int drains_damage = 0;
if (attacker_stats->drains) {
// don't drain so much that the attacker gets more than his maximum hitpoints
drains_damage = std::min<int>(damage / 2, attacker.get_unit().max_hitpoints() - attacker.get_unit().hitpoints());
// don't drain more than the defenders remaining hitpoints
drains_damage = std::min<int>(drains_damage, defender.get_unit().hitpoints() / 2);
}
int damage_done = std::min<int>(defender.get_unit().hitpoints(), attacker.damage_);
bool dies = defender.get_unit().take_hit(damage);
LOG_NG << "defender took " << damage << (dies ? " and died\n" : "\n");
if (attacker_turn) {
@ -1581,98 +1606,23 @@ bool attack::perform_hit(bool attacker_turn, statistics::attack_context &stats)
}
refresh_bc();
bool attacker_dies = false;
if (drains_damage > 0) {
attacker.get_unit().heal(drains_damage);
} else if(drains_damage < 0) {
attacker_dies = attacker.get_unit().take_hit(-drains_damage);
}
if (dies)
{
attacker.xp_ = game_config::kill_xp(defender.get_unit().level());
defender.xp_ = 0;
resources::screen->invalidate(attacker.loc_);
game_events::entity_location death_loc(defender.loc_, defender.id_);
game_events::entity_location attacker_loc(attacker.loc_, attacker.id_);
std::string undead_variation = defender.get_unit().undead_variation();
fire_event("attack_end");
refresh_bc();
// get weapon info for last_breath and die events
config dat;
config a_weapon_cfg = attacker_stats->weapon && attacker.valid() ?
attacker_stats->weapon->get_cfg() : config();
config d_weapon_cfg = defender_stats->weapon && defender.valid() ?
defender_stats->weapon->get_cfg() : config();
if (a_weapon_cfg["name"].empty())
a_weapon_cfg["name"] = "none";
if (d_weapon_cfg["name"].empty())
d_weapon_cfg["name"] = "none";
dat.add_child("first", d_weapon_cfg);
dat.add_child("second", a_weapon_cfg);
game_events::fire("last breath", death_loc, attacker_loc, dat);
refresh_bc();
if (!defender.valid() || defender.get_unit().hitpoints() > 0) {
// WML has invalidated the dying unit, abort
return false;
}
if (!attacker.valid()) {
unit_display::unit_die(defender.loc_, defender.get_unit(),
NULL, defender_stats->weapon);
} else {
unit_display::unit_die(defender.loc_, defender.get_unit(),
attacker_stats->weapon, defender_stats->weapon,
attacker.loc_, &attacker.get_unit());
}
game_events::fire("die", death_loc, attacker_loc, dat);
refresh_bc();
if (!defender.valid() || defender.get_unit().hitpoints() > 0) {
// WML has invalidated the dying unit, abort
return false;
}
units_.erase(defender.loc_);
if (attacker.valid() && attacker_stats->plagues)
{
// plague units make new units on the target hex
LOG_NG << "trying to reanimate " << attacker_stats->plague_type << '\n';
const unit_type *reanimator =
unit_types.find(attacker_stats->plague_type);
if (reanimator)
{
LOG_NG << "found unit type:" << reanimator->id() << '\n';
unit newunit(reanimator, attacker.get_unit().side(),
true, unit_race::MALE);
newunit.set_attacks(0);
newunit.set_movement(0);
// Apply variation
if (undead_variation != "null")
{
config mod;
config &variation = mod.add_child("effect");
variation["apply_to"] = "variation";
variation["name"] = undead_variation;
newunit.add_modification("variation",mod);
newunit.heal_all();
}
units_.add(death_loc, newunit);
preferences::encountered_units().insert(newunit.type_id());
if (update_display_) {
resources::screen->invalidate(death_loc);
}
}
}
else
{
LOG_NG << "unit not reanimated\n";
}
if (dies) {
unit_killed(attacker, defender, attacker_stats, defender_stats, false);
update_fog = true;
}
if (attacker_dies) {
unit_killed(defender, attacker, defender_stats, attacker_stats, true);
*(attacker_turn ? &update_att_fog_ : &update_def_fog_) = true;
}
if(dies) {
update_minimap_ = true;
return false;
}
@ -1703,10 +1653,106 @@ bool attack::perform_hit(bool attacker_turn, statistics::attack_context &stats)
}
}
// Delay until here so that poison and slow go through
if(attacker_dies) {
update_minimap_ = true;
return false;
}
--attacker.n_attacks_;
return true;
}
void attack::unit_killed(unit_info& attacker, unit_info& defender,
const battle_context_unit_stats *&attacker_stats, const battle_context_unit_stats *&defender_stats,
bool drain_killed)
{
attacker.xp_ = game_config::kill_xp(defender.get_unit().level());
defender.xp_ = 0;
resources::screen->invalidate(attacker.loc_);
game_events::entity_location death_loc(defender.loc_, defender.id_);
game_events::entity_location attacker_loc(attacker.loc_, attacker.id_);
std::string undead_variation = defender.get_unit().undead_variation();
fire_event("attack_end");
refresh_bc();
// get weapon info for last_breath and die events
config dat;
config a_weapon_cfg = attacker_stats->weapon && attacker.valid() ?
attacker_stats->weapon->get_cfg() : config();
config d_weapon_cfg = defender_stats->weapon && defender.valid() ?
defender_stats->weapon->get_cfg() : config();
if (a_weapon_cfg["name"].empty())
a_weapon_cfg["name"] = "none";
if (d_weapon_cfg["name"].empty())
d_weapon_cfg["name"] = "none";
dat.add_child("first", d_weapon_cfg);
dat.add_child("second", a_weapon_cfg);
game_events::fire("last breath", death_loc, attacker_loc, dat);
refresh_bc();
if (!defender.valid() || defender.get_unit().hitpoints() > 0) {
// WML has invalidated the dying unit, abort
return;
}
if (!attacker.valid()) {
unit_display::unit_die(defender.loc_, defender.get_unit(),
NULL, defender_stats->weapon);
} else {
unit_display::unit_die(defender.loc_, defender.get_unit(),
attacker_stats->weapon, defender_stats->weapon,
attacker.loc_, &attacker.get_unit());
}
game_events::fire("die", death_loc, attacker_loc, dat);
refresh_bc();
if (!defender.valid() || defender.get_unit().hitpoints() > 0) {
// WML has invalidated the dying unit, abort
return;
}
units_.erase(defender.loc_);
if (attacker.valid() && attacker_stats->plagues && !drain_killed)
{
// plague units make new units on the target hex
LOG_NG << "trying to reanimate " << attacker_stats->plague_type << '\n';
const unit_type *reanimator =
unit_types.find(attacker_stats->plague_type);
if (reanimator)
{
LOG_NG << "found unit type:" << reanimator->id() << '\n';
unit newunit(reanimator, attacker.get_unit().side(),
true, unit_race::MALE);
newunit.set_attacks(0);
newunit.set_movement(0);
// Apply variation
if (undead_variation != "null")
{
config mod;
config &variation = mod.add_child("effect");
variation["apply_to"] = "variation";
variation["name"] = undead_variation;
newunit.add_modification("variation",mod);
newunit.heal_all();
}
units_.add(death_loc, newunit);
preferences::encountered_units().insert(newunit.type_id());
if (update_display_) {
resources::screen->invalidate(death_loc);
}
}
}
else
{
LOG_NG << "unit not reanimated\n";
}
}
void attack::perform()
{
// Stop the user from issuing any commands while the units are fighting

View file

@ -154,6 +154,8 @@ struct battle_context_unit_stats
unsigned int chance_to_hit; /**< Effective chance to hit as a percentage (all factors accounted for). */
int damage; /**< Effective damage of the weapon (all factors accounted for). */
int slow_damage; /**< Effective damage if unit becomes slowed (== damage, if already slowed) */
int drain_percent; /**< Percentage of damage recovered as health */
int drain_constant; /**< Base HP drained regardless of damage dealt */
unsigned int num_blows; /**< Effective number of blows, takes swarm into account. */
unsigned int swarm_min; /**< Minimum number of blows with swarm (equal to num_blows if swarm isn't used). */
unsigned int swarm_max; /**< Maximum number of blows with swarm (equal to num_blows if swarm isn't used). */

View file

@ -62,11 +62,11 @@ struct prob_matrix
// A hits B.
void receive_blow_b(unsigned damage, unsigned slow_damage, double hit_chance,
bool a_slows, bool a_drains);
bool a_slows, int a_drain_constant, int a_drain_percent);
// B hits A. Why can't they just get along?
void receive_blow_a(unsigned damage, unsigned slow_damage, double hit_chance,
bool b_slows, bool b_drains);
bool b_slows, int b_drain_constant, int b_drain_percent);
void forced_levelup_a();
void conditional_levelup_a();
@ -105,17 +105,17 @@ private:
// Move this much from src to dst. Returns true if anything transferred.
void xfer(unsigned dst_plane, unsigned src_plane,
unsigned row_dst, unsigned col_dst,
unsigned row_src, unsigned col_src,
int row_dst, int col_dst,
int row_src, int col_src,
double prob);
// Shift columns on this plane (b taking damage). Returns min col.
void shift_cols(unsigned dst, unsigned src,
unsigned damage, double prob, bool drain);
unsigned damage, double prob, int drain_constant, int drain_percent);
// Shift rows on this plane (a taking damage). Returns new min row.
void shift_rows(unsigned dst, unsigned src,
unsigned damage, double prob, bool drain);
unsigned damage, double prob, int drain_constant, int drain_percent);
/** @todo FIXME: rename using _ at end. */
unsigned int rows_, cols_;
@ -256,8 +256,8 @@ void prob_matrix::dump() const
// xfer, shift_cols and shift_rows use up most of our time. Careful!
void prob_matrix::xfer(unsigned dst_plane, unsigned src_plane,
unsigned row_dst, unsigned col_dst,
unsigned row_src, unsigned col_src,
int row_dst, int col_dst,
int row_src, int col_src,
double prob)
{
double &src = val(src_plane, row_src, col_src);
@ -266,10 +266,14 @@ void prob_matrix::xfer(unsigned dst_plane, unsigned src_plane,
src -= diff;
// This is here for drain.
if (col_dst >= cols_)
col_dst = cols_ - 1;
if (row_dst >= rows_)
row_dst = rows_ - 1;
if (col_dst >= (signed) cols_)
col_dst = (signed) cols_ - 1;
else if (col_dst < 0)
col_dst = 0;
if (row_dst >= (signed) rows_)
row_dst = (signed) rows_ - 1;
else if (row_dst < 0)
row_dst = 0;
val(dst_plane, row_dst, col_dst) += diff;
@ -288,47 +292,79 @@ void prob_matrix::xfer(unsigned dst_plane, unsigned src_plane,
}
void prob_matrix::shift_cols(unsigned dst, unsigned src,
unsigned damage, double prob, bool drain)
unsigned damage, double prob, int drain_constant, int drain_percent)
{
unsigned int row, col;
unsigned int shift = drain ? 1 : 31; // Avoids a branch.
int row, col;
int drainmax = (drain_percent*((signed) damage)/100+drain_constant);
if(drain_constant || drain_percent) {
debug(("Drains %i (%i%% of %i plus %i)\n", drainmax, drain_percent, damage, drain_constant));
}
if (damage >= cols_)
damage = cols_ - 1;
// Loop backwards so we write drain behind us, for when src == dst.
for (row = rows_ - 1; row > min_row_[src]; row--) {
// Killing blows can have strange drain amounts, so handle them first
for (row = min_row_[src] + 1; row < (signed) rows_; row++) {
// These are all going to die (move to col 0).
for (col = 1; col <= damage; ++col)
xfer(dst, src, row+(col>>shift), 0, row, col, prob);
for (col = damage+1; col < cols_; ++col)
xfer(dst, src, row+(damage>>shift), col - damage, row, col, prob);
for (col = 1; (unsigned) col <= damage; ++col)
xfer(dst, src, std::max(row+(drain_percent*col/100+drain_constant), 1), 0, row, col, prob);
}
// Loop downwards if we drain positive, but upwards if we drain negative,
// so we write behind us (for when src == dst).
if(drainmax > 0) {
for (row = rows_ - 1; row > (signed) min_row_[src]; row--) {
for (col = damage+1; (unsigned) col < cols_; ++col)
xfer(dst, src, std::max(row+drainmax, 1), col - damage, row, col, prob);
}
} else {
for (row = min_row_[src] + 1; (unsigned) row < rows_; row++) {
for (col = damage+1; (unsigned) col < cols_; ++col)
xfer(dst, src, std::max(row+drainmax, 1), col - damage, row, col, prob);
}
}
}
void prob_matrix::shift_rows(unsigned dst, unsigned src,
unsigned damage, double prob, bool drain)
unsigned damage, double prob, int drain_constant, int drain_percent)
{
unsigned int row, col;
unsigned int shift = drain ? 1 : 31; // Avoids a branch.
int row, col;
int drainmax = (drain_percent*((signed) damage)/100+drain_constant);
if(drain_constant || drain_percent) {
debug(("Drains %i (%i%% of %i plus %i)\n", drainmax, drain_percent, damage, drain_constant));
}
if (damage >= rows_)
damage = rows_ - 1;
// Loop downwards so if we drain, we write behind us.
for (col = cols_ - 1; col > min_col_[src]; col--) {
// Killing blows can have strange drain amounts, so handle them first
for (col = min_col_[src] + 1; (unsigned) col < cols_; col++) {
// These are all going to die (move to row 0).
for (row = 1; row <= damage; ++row)
xfer(dst, src, 0, col+(row>>shift), row, col, prob);
for (row = damage+1; row < rows_; ++row)
xfer(dst, src, row - damage, col+(damage>>shift), row, col, prob);
for (row = 1; row <= (signed) damage; ++row)
xfer(dst, src, 0, std::max(col+(drain_percent*row/100+drain_constant), 1), row, col, prob);
}
// Loop downwards if we drain positive, but upwards if we drain negative,
// so we write behind us (for when src == dst).
if(drainmax > 0) {
for (col = cols_ - 1; col > (signed) min_col_[src]; col--) {
for (row = damage+1; (unsigned) row < rows_; ++row)
xfer(dst, src, row - damage, std::max(col+drainmax, 1), row, col, prob);
}
} else {
for (col = min_col_[src] + 1; (unsigned) col < cols_; col++) {
for (row = damage+1; (unsigned) row < rows_; ++row)
xfer(dst, src, row - damage, std::max(col+drainmax, 1), row, col, prob);
}
}
}
// Shift prob_matrix to reflect probability 'hit_chance'
// that damage (up to) 'damage' is done to 'b'.
void prob_matrix::receive_blow_b(unsigned damage, unsigned slow_damage, double hit_chance,
bool a_slows, bool a_drains)
bool a_slows, int a_drain_constant, int a_drain_percent)
{
int src, dst;
@ -351,13 +387,21 @@ void prob_matrix::receive_blow_b(unsigned damage, unsigned slow_damage, double h
else
actual_damage = damage;
shift_cols(dst, src, actual_damage, hit_chance, a_drains);
shift_cols(dst, src, actual_damage, hit_chance, a_drain_constant, a_drain_percent);
if (min_col_[src] < damage)
min_col_[dst] = 0;
else if (min_col_[src] - damage < min_col_[dst])
min_col_[dst] = min_col_[src] - damage;
if (min_row_[src] < min_row_[dst])
min_row_[dst] = min_row_[src];
int drain_worst = std::min<int>(a_drain_percent*((signed) actual_damage)/100+a_drain_constant, a_drain_constant);
if(drain_worst < 0) {
if((unsigned) -drain_worst >= min_row_[dst])
min_row_[dst] = 0;
else
min_row_[dst] += drain_worst;
}
}
}
@ -442,10 +486,10 @@ double prob_matrix::dead_prob() const
for (p = 0; p < 4; ++p) {
if (!plane_[p])
continue;
// We might count 0,0 twice, but that is always 0 anyway.
// Don't count 0,0 twice
for (row = min_row_[p]; row < rows_; ++row)
prob += val(p, row, 0);
for (col = min_col_[p]; col < cols_; ++col)
for (col = min_col_[p]?min_col_[p]:1; col < cols_; ++col)
prob += val(p, 0, col);
}
return prob;
@ -454,7 +498,7 @@ double prob_matrix::dead_prob() const
// Shift matrix to reflect probability 'hit_chance'
// that damage (up to) 'damage' is done to 'a'.
void prob_matrix::receive_blow_a(unsigned damage, unsigned slow_damage, double hit_chance,
bool b_slows, bool b_drains)
bool b_slows, int b_drain_constant, int b_drain_percent)
{
int src, dst;
@ -477,13 +521,21 @@ void prob_matrix::receive_blow_a(unsigned damage, unsigned slow_damage, double h
else
actual_damage = damage;
shift_rows(dst, src, actual_damage, hit_chance, b_drains);
shift_rows(dst, src, actual_damage, hit_chance, b_drain_constant, b_drain_percent);
if (min_row_[src] < damage)
min_row_[dst] = 0;
else if (min_row_[src] - damage < min_row_[dst])
min_row_[dst] = min_row_[src] - damage;
if (min_col_[src] < min_col_[dst])
min_col_[dst] = min_col_[src];
int drain_worst = std::min<int>(b_drain_percent*((signed) actual_damage)/100+b_drain_constant, b_drain_constant);
if(drain_worst < 0) {
if((unsigned) -drain_worst >= min_col_[dst])
min_col_[dst] = 0;
else
min_col_[dst] += drain_worst;
}
}
}
@ -811,13 +863,13 @@ void combatant::complex_fight(combatant &opp, unsigned rounds, bool levelup_cons
if (i < hit_chances_.size()) {
debug(("A strikes\n"));
m.receive_blow_b(a_damage, a_slow_damage, hit_chances_[i],
u_.slows && !opp.u_.is_slowed, u_.drains);
u_.slows && !opp.u_.is_slowed, u_.drain_constant, u_.drain_percent);
m.dump();
}
if (i < opp.hit_chances_.size()) {
debug(("B strikes\n"));
m.receive_blow_a(b_damage, b_slow_damage, opp.hit_chances_[i],
opp.u_.slows && !u_.is_slowed, opp.u_.drains);
opp.u_.slows && !u_.is_slowed, opp.u_.drain_constant, opp.u_.drain_percent);
m.dump();
}
}

View file

@ -301,7 +301,7 @@ void unit_die(const map_location& loc, unit& loser,
void unit_attack(
const map_location& a, const map_location& b, int damage,
const attack_type& attack, const attack_type* secondary_attack,
int swing,std::string hit_text,bool drain,std::string att_text)
int swing,std::string hit_text,int drain_amount,std::string att_text)
{
game_display* disp = game_display::get_singleton();
if(!disp ||disp->video().update_locked() || disp->video().faked() ||
@ -342,7 +342,7 @@ void unit_attack(
}
std::string text_2 ;
if(drain && damage) text_2 = lexical_cast<std::string>(std::min<int>(damage,defender.hitpoints())/2);
if(drain_amount) text_2 = lexical_cast<std::string>(drain_amount > 0 ? drain_amount : -drain_amount);
if(!att_text.empty()) {
text_2.insert(text_2.begin(),att_text.size()/2,' ');
text_2 = text_2 + "\n" + att_text;
@ -358,8 +358,8 @@ void unit_attack(
}
animator.add_animation(&attacker, "attack", att->get_location(),
def->get_location(), damage, true, text_2,
display::rgb(0, 255, 0), hit_type, &attack, secondary_attack,
swing);
(drain_amount >= 0) ? display::rgb(0, 255, 0) : display::rgb(255, 0, 0),
hit_type, &attack, secondary_attack, swing);
// note that we take an anim from the real unit, we'll use it later
const unit_animation *defender_anim = def->choose_animation(*disp,

View file

@ -90,7 +90,7 @@ void unit_sheath_weapon( const map_location& loc, unit* u=NULL, const attack_typ
*/
void unit_attack(const map_location& a, const map_location& b, int damage,
const attack_type& attack, const attack_type* secondary_attack,
int swing, std::string hit_text, bool drain, std::string att_text);
int swing, std::string hit_text, int drain_amount, std::string att_text);
void unit_recruited(const map_location& loc,