1use crate::abilities::AbilityId;
2use crate::bundles::BundleId;
3use crate::dungeons::DungeonTemplateId;
4use crate::entity::{Coordinates, Entity, EntityId};
5use crate::game::EntityTemplateId;
6use crate::pets::PetId;
7
8use crate::prelude::*;
9
10use strum_macros::{Display, EnumString};
11
12#[derive(
13 Clone,
14 Debug,
15 Default,
16 Serialize,
17 Deserialize,
18 PartialEq,
19 Eq,
20 Tsify,
21 EnumString,
22 Display,
23 JsonSchema,
24)]
25#[tsify(from_wasm_abi, into_wasm_abi)]
26pub enum EntityTeam {
27 #[default]
28 Ally,
29 Enemy,
30}
31
32#[derive(
33 Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, Tsify, Display, JsonSchema,
34)]
35#[tsify(from_wasm_abi, into_wasm_abi)]
36pub enum EntityType {
37 #[schemars(title = "ПВЕ юнит")]
38 PVEEntity {
39 #[schemars(title = "ID юнита", schema_with = "entity_link_id_schema")]
40 entity_template_id: EntityTemplateId,
41 },
42 #[default]
43 #[schemars(title = "ПВП юнит")]
44 PVPEntity,
45}
46
47#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, Tsify, JsonSchema)]
48pub struct FightEntity {
49 #[schemars(title = "Тип юнита")]
50 pub entity_type: EntityType,
51 #[schemars(title = "Координаты юнита")]
52 pub position: Coordinates,
53 #[schemars(title = "Нужна ли отрисовка большого хп бара")]
54 pub has_big_hp_bar: bool,
55 #[schemars(title = "Команда юнита")]
56 pub team: EntityTeam,
57}
58
59#[derive(
60 Clone,
61 Debug,
62 Default,
63 Serialize,
64 Deserialize,
65 PartialEq,
66 Eq,
67 Tsify,
68 EnumString,
69 Display,
70 JsonSchema,
71)]
72#[tsify(from_wasm_abi, into_wasm_abi)]
73pub enum FightType {
74 #[default]
75 CampaignFight,
76 CampaignBossFight,
77 ArenaPVP,
78 VassalPVP,
79 SingleFight,
80}
81
82#[declare]
83pub type FightTemplateId = Uuid;
84
85#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Tsify, JsonSchema)]
90pub struct PrepareFightWaves {
91 #[schemars(title = "Относительные силы шаблонов сущностей")]
92 pub entities: Vec<PrepareFightEntityPower>,
93 #[schemars(title = "Волны спавна")]
94 pub waves: Vec<Vec<PrepareFightSpawn>>,
95 #[schemars(title = "Бюджет времени боя (сек), для нормализации HP мобов")]
96 pub time: f64,
97 #[serde(default)]
98 #[schemars(title = "Опорная сила волны (base_power для spawn_wave)")]
99 pub power: f64,
100}
101
102#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Tsify, JsonSchema)]
103pub struct PrepareFightEntityPower {
104 #[serde(default)]
107 #[schemars(schema_with = "option_entity_link_id_schema")]
108 pub entity_id: Option<String>,
109 #[serde(default)]
110 pub power: Option<f64>,
111}
112
113#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Tsify, JsonSchema)]
114pub struct PrepareFightSpawn {
115 #[schemars(schema_with = "entity_link_id_schema")]
116 pub entity_id: String,
117 #[serde(default)]
118 pub delay: Option<f64>,
119 #[serde(default)]
120 pub position: Option<Coordinates>,
121}
122
123#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Tsify, JsonSchema)]
127pub struct FightTemplate {
128 #[schemars(schema_with = "id_schema")]
129 pub id: FightTemplateId,
130
131 #[schemars(title = "Название боя")]
132 pub title: i18n::I18nString,
133
134 #[schemars(title = "Мощь боя")]
135 pub power: Option<u64>,
136
137 #[schemars(title = "Все существа в бою")]
140 pub fight_entities: Vec<FightEntity>,
141
142 #[schemars(title = "Максимальная длительность боя в тиках")]
143 pub max_duration_ticks: u64,
144
145 #[schemars(title = "Целевая ширина экрана (в клетках)")]
146 pub target_width_cells: u64,
147
148 #[schemars(title = "Fx на старте боя")]
149 pub starting_fx: String,
151
152 #[schemars(title = "Тип боя")]
153 pub fight_type: FightType,
154
155 #[schemars(title = "Количество волн в бою")]
156 pub waves_amount: i64,
157
158 #[schemars(
159 title = "Волны боя (типизированные)",
160 description = "Типизированные данные волн боя."
161 )]
162 #[serde(default)]
163 pub prepare_fight_waves: Option<PrepareFightWaves>,
164
165 #[schemars(
166 title = "Нативная функция начала боя",
167 description = "Имя нативной функции категории fight_start, выполняющей начало боя.",
168 schema_with = "fight_start_ref_schema"
169 )]
170 #[serde(default)]
171 pub start_behavior: Option<String>,
172
173 #[schemars(title = "Задник", schema_with = "asset_background_schema")]
174 pub background: String,
175
176 #[schemars(
177 title = "Время ожидания перед стартом следующего боя в тиках",
178 description = "Это момент анимации перебежки персонажа между битвами"
179 )]
180 pub start_fight_delay_ticks: Option<u64>,
181
182 #[schemars(
183 title = "Время ожидания перед стартом уже начавшегося боя в случае победы",
184 description = "Это момент, когда враги выбегают на экран"
185 )]
186 pub prepare_fight_win_duration_ticks: Option<u64>,
187
188 #[schemars(
189 title = "Время ожидания перед стартом уже начавшегося боя в случае поражения",
190 description = "Это момент, когда враги выбегают на экран"
191 )]
192 pub prepare_fight_lose_duration_ticks: Option<u64>,
193
194 #[schemars(title = "Время ожидания перед окончанием боя")]
195 pub end_fight_delay_ticks: Option<u64>,
196
197 #[schemars(
198 title = "Бандл награды за победу в бою",
199 schema_with = "option_bundle_id_schema"
200 )]
201 pub bundle_reward_id: Option<BundleId>,
202
203 #[schemars(title = "Останавливать ли бой после победы")]
204 pub stop_on_win: bool,
205
206 #[schemars(title = "Останавливать ли бой после поражения")]
207 pub stop_on_lose: bool,
208
209 #[schemars(title = "Показывать ли VS экран в начале боя")]
210 pub show_vs_screen: bool,
211
212 #[schemars(title = "Показывать ли стейджи")]
213 pub show_stages: Option<bool>,
214
215 #[schemars(
216 title = "Является ли подземельем",
217 description = "Бой подземелья (fight_template_is_dungeon): включает dungeon-таланты в init_fight."
218 )]
219 #[serde(default)]
220 pub is_dungeon: bool,
221
222 #[schemars(
223 title = "Является ли боссфайтом",
224 description = "Босс-бой (fight_template_is_bossfight): включает boss-таланты в init_fight."
225 )]
226 #[serde(default)]
227 pub is_bossfight: bool,
228}
229
230#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, Tsify, JsonSchema)]
231pub struct ActiveDungeon {
232 pub id: DungeonTemplateId,
233 pub difficulty: i64,
234}
235
236#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, Tsify, JsonSchema)]
237pub struct ActivePetAbility {
238 pub pet_template_id: PetId,
239 pub ability_id: AbilityId,
240 pub charge: i64,
241 pub max_charge: i64,
242}
243
244#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, Tsify, JsonSchema)]
245pub struct ActiveFight {
246 pub id: uuid::Uuid,
247 pub fight_id: FightTemplateId,
248 pub current_wave: i64,
249 pub player_id: EntityId,
250 pub party_player_id: Option<EntityId>,
251 pub entities: Vec<Entity>,
252 pub fight_stopped: bool,
253 pub fight_ended: bool,
254 pub max_duration_ticks: u64,
255 pub dungeon: Option<ActiveDungeon>,
256 pub paused: bool,
257 pub pet_combat_state: Option<ActivePetAbility>,
258 pub leader_pet_template_id: Option<PetId>,
259}
260
261impl ActiveFight {
262 pub fn get_player(&self) -> Option<&Entity> {
263 self.entities
264 .iter()
265 .find(|entity| entity.id == self.player_id)
266 }
267
268 pub fn get_player_mut(&mut self) -> Option<&mut Entity> {
269 self.entities
270 .iter_mut()
271 .find(|entity| entity.id == self.player_id)
272 }
273
274 pub fn get_party_player(&self) -> Option<&Entity> {
275 let party_id = self.party_player_id?;
276 self.entities.iter().find(|entity| entity.id == party_id)
277 }
278
279 pub fn get_party_player_mut(&mut self) -> Option<&mut Entity> {
280 let party_id = self.party_player_id?;
281 self.entities
282 .iter_mut()
283 .find(|entity| entity.id == party_id)
284 }
285
286 pub fn get_enemies_amount(&self) -> usize {
287 self.entities
288 .iter()
289 .filter(|entity| entity.team == EntityTeam::Enemy)
290 .count()
291 }
292}