Apply patch #3083 by Brilliand
This commit is contained in:
parent
34647d1a9a
commit
71ef9ab0a2
7 changed files with 460 additions and 142 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
244
src/actions.cpp
244
src/actions.cpp
|
@ -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
|
||||
|
|
|
@ -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). */
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Reference in a new issue