overlord_event_system/behaviors/combat/
cast_projectile.rs1use configs::game_config::GameConfig;
24use essences::entity::Entity;
25use essences::fighting::ActiveFight;
26use event_system::script::random::GameRng;
27use uuid::Uuid;
28
29use crate::behaviors::{BehaviorKind, BehaviorMeta, BehaviorRegistry};
30use crate::event::OverlordEvent;
31use crate::mechanics::balance;
32use crate::mechanics::content::{AbilityInfo, ability_info};
33use crate::mechanics::content_lookups::ContentLookups;
34use crate::mechanics::effect_cb::OverlordEffectCb;
35use crate::mechanics::fight::{AttackParams, NativeSink, attack, heal_entity};
36
37pub struct CastProjectileCtx<'a> {
39 pub caster_entity: &'a Entity,
41 pub target_entity: &'a Entity,
43 pub fight: &'a ActiveFight,
45 pub rng: &'a GameRng,
47 pub projectile_level: i64,
49 pub config: &'a GameConfig,
50 pub lookups: &'a ContentLookups,
51}
52
53pub type CastProjectileFn = fn(&CastProjectileCtx) -> anyhow::Result<Vec<OverlordEvent>>;
55
56fn info(ctx: &CastProjectileCtx, ability_id: &str) -> anyhow::Result<AbilityInfo> {
58 let id = Uuid::parse_str(ability_id).expect("valid ability uuid literal");
59 ability_info(ctx.config, ctx.lookups, id, ctx.projectile_level)
60}
61
62fn run_attack(
64 ctx: &CastProjectileCtx,
65 sink: &mut NativeSink,
66 params: &AttackParams,
67) -> anyhow::Result<Option<i64>> {
68 let mut effects = OverlordEffectCb;
69 attack(
70 sink,
71 ctx.rng,
72 ctx.lookups,
73 &mut effects,
74 ctx.fight.player_id,
75 ctx.caster_entity,
76 ctx.target_entity,
77 params,
78 )
79 .map_err(|e| anyhow::anyhow!("attack: {e}"))
80}
81
82fn attack_power(ctx: &CastProjectileCtx, ability_id: &str) -> anyhow::Result<Vec<OverlordEvent>> {
84 let mut sink = NativeSink::default();
85 let ability_info = info(ctx, ability_id)?;
86 let params = AttackParams {
87 power: ability_info.damage,
88 ..Default::default()
89 };
90 run_attack(ctx, &mut sink, ¶ms)?;
91 Ok(sink.events)
92}
93
94pub fn attack_power_dot(ctx: &CastProjectileCtx) -> anyhow::Result<Vec<OverlordEvent>> {
97 let mut sink = NativeSink::default();
98 let ability_info = info(ctx, "01958172-9e65-7061-9d15-56b2c33cc13e")?;
99 let params = AttackParams {
100 power: ability_info.damage,
101 dot_power: ability_info.dot,
102 ..Default::default()
103 };
104 run_attack(ctx, &mut sink, ¶ms)?;
105 Ok(sink.events)
106}
107
108pub fn arcane_missiles_attack(ctx: &CastProjectileCtx) -> anyhow::Result<Vec<OverlordEvent>> {
110 attack_power(ctx, "019589e6-f9dd-7b22-8d39-5350e95aaf69")
111}
112
113pub fn mushroom_attack(ctx: &CastProjectileCtx) -> anyhow::Result<Vec<OverlordEvent>> {
115 attack_power(ctx, "019a0245-ff0c-7964-b345-ded525c71e74")
116}
117
118pub fn chipmunk_attack(ctx: &CastProjectileCtx) -> anyhow::Result<Vec<OverlordEvent>> {
120 attack_power(ctx, "019a0246-5aaf-7c01-9a1a-3969e04129ec")
121}
122
123pub fn bat_attack(ctx: &CastProjectileCtx) -> anyhow::Result<Vec<OverlordEvent>> {
125 attack_power(ctx, "019a0246-cf87-73b0-b701-f8788cf9cc08")
126}
127
128pub fn shot_attack(ctx: &CastProjectileCtx) -> anyhow::Result<Vec<OverlordEvent>> {
130 attack_power(ctx, "019c00a4-38c9-7859-a642-fd82c25ef285")
131}
132
133pub fn attack_vampiric(ctx: &CastProjectileCtx) -> anyhow::Result<Vec<OverlordEvent>> {
140 let mut sink = NativeSink::default();
141 let ability_info = info(ctx, "019589f7-f4a3-701c-bc0f-f60d980ae250")?;
142 let params = AttackParams {
143 power: ability_info.damage,
144 ..Default::default()
145 };
146 let damage = run_attack(ctx, &mut sink, ¶ms)?;
147 let Some(damage) = damage else {
148 anyhow::bail!("attack returned no damage; the `damage > 0` contract failed");
149 };
150 if damage > 0 {
151 let vampiric = ability_info.vampiric.unwrap_or(0.0);
152 heal_entity(&mut sink, ctx.caster_entity, damage as f64 * vampiric)
153 .map_err(|e| anyhow::anyhow!("heal_entity: {e}"))?;
154 }
155 Ok(sink.events)
156}
157
158pub fn counterattack(ctx: &CastProjectileCtx) -> anyhow::Result<Vec<OverlordEvent>> {
161 let mut sink = NativeSink::default();
162 let params = AttackParams {
163 power: Some(balance::COUNTERATTACK_POWER),
164 no_counterattack: true,
165 ..Default::default()
166 };
167 run_attack(ctx, &mut sink, ¶ms)?;
168 Ok(sink.events)
169}
170
171pub fn damage_target_5(ctx: &CastProjectileCtx) -> anyhow::Result<Vec<OverlordEvent>> {
175 Ok(vec![OverlordEvent::Damage {
176 entity_id: ctx.target_entity.id,
177 damage: 5,
178 damage_data: crate::event::CustomEventData::default(),
179 }])
180}
181
182pub fn register(registry: &mut BehaviorRegistry) {
184 let mut reg = |name: &str, title: &str, desc: &str, f: CastProjectileFn| {
185 registry.register_cast_projectile(
186 BehaviorMeta {
187 name: name.to_string(),
188 category: BehaviorKind::CastProjectile,
189 title: title.to_string(),
190 description: desc.to_string(),
191 },
192 f,
193 );
194 };
195
196 reg(
197 "projectile_damage_target_5",
198 "Снаряд: урон 5 по цели (тест)",
199 "Damage(TargetEntity, 5) — порт test projectile script.",
200 damage_target_5,
201 );
202 reg(
203 "projectile_attack_power_dot",
204 "Снаряд: attack{power,dot} (01966cbc)",
205 "attack(#{power: ability_info.damage, dot_power: ability_info.dot}) \
206 info из 01958172 (порт projectile script 01966cbc).",
207 attack_power_dot,
208 );
209 reg(
210 "projectile_arcane_missiles_attack",
211 "Снаряд: attack{power} (0196a6b3)",
212 "attack(#{power: ability_info.damage}) info из 019589e6 (порт projectile script 0196a6b3).",
213 arcane_missiles_attack,
214 );
215 reg(
216 "projectile_mushroom_attack",
217 "Снаряд: attack{power} (019a0244-4675)",
218 "attack(#{power: ability_info.damage}) info из 019a0245 (порт projectile script 019a0244-4675).",
219 mushroom_attack,
220 );
221 reg(
222 "projectile_chipmunk_attack",
223 "Снаряд: attack{power} (019a0244-7a0a)",
224 "attack(#{power: ability_info.damage}) info из 019a0246-5aaf (порт projectile script 019a0244-7a0a).",
225 chipmunk_attack,
226 );
227 reg(
228 "projectile_bat_attack",
229 "Снаряд: attack{power} (019a0244-aad4)",
230 "attack(#{power: ability_info.damage}) info из 019a0246-cf87 (порт projectile script 019a0244-aad4).",
231 bat_attack,
232 );
233 reg(
234 "projectile_shot_attack",
235 "Снаряд: attack{power} (019c009c)",
236 "attack(#{power: ability_info.damage}) info из 019c00a4 (порт projectile script 019c009c).",
237 shot_attack,
238 );
239 reg(
240 "projectile_attack_vampiric",
241 "Снаряд: вампиризм (0196a6b4)",
242 "attack(#{power}); при уроне>0 heal_entity(Caster, damage*vampiric) \
243 info из 019589f7 (порт projectile script 0196a6b4).",
244 attack_vampiric,
245 );
246 reg(
247 "projectile_counterattack",
248 "Снаряд: контратака (019aeeed)",
249 "attack(#{power: COUNTERATTACK_POWER, no_counterattack: true}) \
250 (порт projectile script 019aeeed).",
251 counterattack,
252 );
253}