001package net.tnemc.item.component.impl;
002/*
003 * The New Item Library
004 * Copyright (C) 2022 - 2025 Daniel "creatorfromhell" Vidmar
005 *
006 * This program is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019 */
020
021import net.tnemc.item.AbstractItemStack;
022import net.tnemc.item.JSONHelper;
023import net.tnemc.item.component.SerialComponent;
024import net.tnemc.item.component.helper.DamageReduction;
025import net.tnemc.item.component.helper.ItemDamage;
026import net.tnemc.item.platform.ItemPlatform;
027import org.json.simple.JSONArray;
028import org.json.simple.JSONObject;
029
030import java.util.ArrayList;
031import java.util.Arrays;
032import java.util.List;
033import java.util.Objects;
034
035/**
036 * BlocksAttacksComponent - When present, this item can be used like a Shield to block attacks to the
037 * holding player. Added in MC 1.21.5
038 *
039 * @author creatorfromhell
040 * @see <a href="https://minecraft.wiki/w/Data_component_format#blocks_attacks">Reference</a>
041 * <p>
042 * @since 0.2.0.0
043 */
044public abstract class BlocksAttacksComponent<I extends AbstractItemStack<T>, T> implements SerialComponent<I, T> {
045
046  protected final List<DamageReduction> reductions = new ArrayList<>();
047
048  protected final List<String> bypassedBy = new ArrayList<>();
049
050  protected ItemDamage itemDamage = null;
051
052  //The number of seconds that right-click must be held before successfully blocking attacks
053  protected float blockDelay = 0;
054
055  //Multiplier applied to the number of seconds that the item will be on cooldown for when attacked
056  //by a disabling attack (disable_blocking_for_seconds on the weapon component)
057  //If 0, this item can never be disabled by attacks
058  protected float disableCooldownScale = 1;
059
060  protected String blockSound = "";
061
062  protected String disableSound = "";
063
064  public BlocksAttacksComponent() {
065
066  }
067
068  /**
069   * @return the type of component this is.
070   * @since 0.2.0.0
071   */
072  @Override
073  public String identifier() {
074
075    return "blocks_attacks";
076  }
077
078  /**
079   * Converts the {@link SerialComponent} to a JSON object.
080   *
081   * @return The JSONObject representing this {@link SerialComponent}.
082   * @since 0.2.0.0
083   */
084  @Override
085  public JSONObject toJSON() {
086
087    final JSONObject json = new JSONObject();
088
089    //Serialize DamageReductions
090    final JSONArray reductionsArray = new JSONArray();
091    for(final DamageReduction reduction : reductions) {
092
093      final JSONObject reductionJson = new JSONObject();
094      reductionJson.put("type", reduction.type());
095      reductionJson.put("base", reduction.base());
096      reductionJson.put("factor", reduction.factor());
097      reductionJson.put("horizontalBlockingAngle", reduction.horizontalBlockingAngle());
098      reductionsArray.add(reductionJson);
099    }
100    json.put("reductions", reductionsArray);
101
102
103    //Serialize bypassedBy
104    final JSONArray bypassedByArray = new JSONArray();
105    for(final String bypassedBy : bypassedBy) {
106
107      bypassedByArray.add(bypassedBy);
108    }
109    json.put("bypassedBy", bypassedByArray);
110
111    // Serialize ItemDamage
112    if(itemDamage != null) {
113
114      final JSONObject itemDamageJson = new JSONObject();
115
116      itemDamageJson.put("threshold", itemDamage.threshold());
117      itemDamageJson.put("base", itemDamage.base());
118      itemDamageJson.put("factor", itemDamage.factor());
119      json.put("item_damage", itemDamageJson);
120    }
121
122    // Serialize other properties
123    json.put("block_delay", blockDelay);
124    json.put("disable_cooldown_scale", disableCooldownScale);
125    json.put("block_sound", blockSound);
126    json.put("disable_sound", disableSound);
127
128    return json;
129  }
130
131  /**
132   * Reads JSON data and converts it back to a {@link SerialComponent} object.
133   *
134   * @param json The JSONHelper instance of the json data.
135   * @since 0.2.0.0
136   */
137  @Override
138  public void readJSON(final JSONHelper json, final ItemPlatform<I, T, ?> platform) {
139
140    //Deserialize DamageReductions
141    final JSONArray reductionsArray = (JSONArray)json.getObject().get("reductions");
142    reductions.clear();
143
144    if(reductionsArray != null) {
145
146      for(final Object obj : reductionsArray) {
147
148        final JSONObject reductionJson = (JSONObject)obj;
149        final String type = reductionJson.get("type").toString();
150        final float base = Float.parseFloat(reductionJson.get("base").toString());
151        final float factor = Float.parseFloat(reductionJson.get("factor").toString());
152        final float horizontalBlockingAngle = Float.parseFloat(reductionJson.get("horizontalBlockingAngle").toString());
153        reductions.add(new DamageReduction(type, base, factor, horizontalBlockingAngle));
154      }
155    }
156
157    //Deserialize bypassBy
158    final JSONArray bypassByArray = (JSONArray)json.getObject().get("bypassedBy");
159    bypassedBy.clear();
160
161    if(bypassByArray != null) {
162
163      for(final Object obj : bypassByArray) {
164
165        bypassedBy.add(obj.toString());
166      }
167    }
168
169    //Deserialize ItemDamage
170    final JSONObject itemDamageJson = (JSONObject) json.getObject().get("item_damage");
171    if(itemDamageJson != null) {
172
173      final float threshold = Float.parseFloat(itemDamageJson.get("threshold").toString());
174      final float base = Float.parseFloat(itemDamageJson.get("base").toString());
175      final float factor = Float.parseFloat(itemDamageJson.get("factor").toString());
176      itemDamage = new ItemDamage(threshold, base, factor);
177    }
178
179    //Deserialize other properties
180    blockDelay = json.getFloat("block_delay");
181    disableCooldownScale = json.getFloat("disable_cooldown_scale");
182    blockSound = json.getString("block_sound");
183    disableSound = json.getString("disable_sound");
184  }
185
186  /**
187   * Used to determine if some data is equal to this data. This means that it has to be an exact
188   * copy of this data. For instance, book copies will return false when compared to the original.
189   *
190   * @param component The component to compare.
191   *
192   * @return True if similar, otherwise false.
193   * @since 0.2.0.0
194   */
195  @Override
196  public boolean similar(final SerialComponent<?, ?> component) {
197
198    if(!(component instanceof final BlocksAttacksComponent<?, ?> other)) return false;
199
200    return Objects.equals(this.reductions, other.reductions) &&
201           Objects.equals(this.bypassedBy, other.bypassedBy) &&
202           Objects.equals(this.itemDamage, other.itemDamage) &&
203           Float.compare(this.blockDelay, other.blockDelay) == 0 &&
204           Float.compare(this.disableCooldownScale, other.disableCooldownScale) == 0 &&
205           Objects.equals(this.blockSound, other.blockSound) &&
206           Objects.equals(this.disableSound, other.disableSound);
207  }
208
209  @Override
210  public int hashCode() {
211
212    return Objects.hash(reductions, bypassedBy, itemDamage, blockDelay, disableCooldownScale, blockSound, disableSound);
213  }
214
215  public List<DamageReduction> reductions() {
216
217    return reductions;
218  }
219
220  public void reductions(final List<DamageReduction> reductions) {
221
222    this.reductions.clear();
223    this.reductions.addAll(reductions);
224  }
225
226  public void reductions(final DamageReduction... reductions) {
227
228    this.reductions.clear();
229    this.reductions.addAll(Arrays.asList(reductions));
230  }
231
232  public List<String> bypassedBy() {
233
234    return bypassedBy;
235  }
236
237  public void bypassedBy(final List<String> bypassedBy) {
238
239    this.bypassedBy.clear();
240    this.bypassedBy.addAll(bypassedBy);
241  }
242
243  public void bypassedBy(final String... bypassedBy) {
244
245    this.bypassedBy.clear();
246    this.bypassedBy.addAll(Arrays.asList(bypassedBy));
247  }
248
249  public ItemDamage itemDamage() {
250
251    return itemDamage;
252  }
253
254  public void itemDamage(final ItemDamage itemDamage) {
255
256    this.itemDamage = itemDamage;
257  }
258
259  public float blockDelay() {
260
261    return blockDelay;
262  }
263
264  public void blockDelay(final float blockDelay) {
265
266    this.blockDelay = blockDelay;
267  }
268
269  public float disableCooldownScale() {
270
271    return disableCooldownScale;
272  }
273
274  public void disableCooldownScale(final float disableCooldownScale) {
275
276    this.disableCooldownScale = disableCooldownScale;
277  }
278
279  public String blockSound() {
280
281    return blockSound;
282  }
283
284  public void blockSound(final String blockSound) {
285
286    this.blockSound = blockSound;
287  }
288
289  public String disableSound() {
290
291    return disableSound;
292  }
293
294  public void disableSound(final String disableSound) {
295
296    this.disableSound = disableSound;
297  }
298}