001package com.fs.starfarer.api.impl.campaign.procgen;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.Iterator;
006import java.util.List;
007import java.util.Random;
008
009import org.json.JSONArray;
010import org.json.JSONException;
011import org.json.JSONObject;
012
013import com.fs.starfarer.api.Global;
014import com.fs.starfarer.api.campaign.CargoAPI.CargoItemType;
015import com.fs.starfarer.api.campaign.CargoStackAPI;
016import com.fs.starfarer.api.campaign.SpecialItemData;
017import com.fs.starfarer.api.campaign.SpecialItemPlugin;
018import com.fs.starfarer.api.campaign.SpecialItemSpecAPI;
019import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI;
020import com.fs.starfarer.api.combat.WeaponAPI.WeaponSize;
021import com.fs.starfarer.api.combat.WeaponAPI.WeaponType;
022import com.fs.starfarer.api.impl.campaign.ids.Tags;
023import com.fs.starfarer.api.loading.FighterWingSpecAPI;
024import com.fs.starfarer.api.loading.WeaponSpecAPI;
025import com.fs.starfarer.api.util.Misc;
026import com.fs.starfarer.api.util.WeightedRandomPicker;
027
028public class DropGroupRow implements Cloneable {
029        public static final String NOTHING = "nothing";
030        
031        
032        public static final String WEAPON_PREFIX = "wpn_";
033        //public static final String MOD_PREFIX = "mod_";
034        public static final String FIGHTER_PREFIX = "ftr_";
035        public static final String ITEM_PREFIX = "item_";
036        
037        
038        private String commodity, group;
039        private float freq;
040        private boolean percent = false;
041        
042        private boolean multiValued = false;
043        private int tier = -1;
044        private List<String> tags = new ArrayList<String>(); 
045        //private List<String> tags2 = new ArrayList<String>(); 
046        private WeaponType weaponType = null;
047        private WeaponSize weaponSize = null;
048        
049//      private String itemId = null;
050//      private String itemParams = null;
051        
052        Object writeReplace() {
053                DropGroupRow copy = clone();
054                if (tags != null && tags.isEmpty()) {
055                        copy.tags = null;
056                }
057//              if (tags2 != null && tags2.isEmpty()) {
058//                      copy.tags2 = null;
059//              }
060                return copy;
061                //return this;
062        }
063        
064        @Override
065        public DropGroupRow clone() {
066                try {
067                        DropGroupRow copy = (DropGroupRow) super.clone();
068                        return copy;
069                } catch (CloneNotSupportedException e) {
070                        return null;
071                }
072        }
073
074        public DropGroupRow(JSONObject row) throws JSONException {
075                commodity = row.getString("commodity");
076                group = row.getString("group");
077                
078                String fStr = row.getString("freq");
079                if (fStr.endsWith("%")) {
080                        percent = true;
081                        fStr = fStr.substring(0, fStr.length() - 1);
082                        freq = Float.parseFloat(fStr);
083                } else {
084                        freq = (float) row.getDouble("freq");
085                }
086                parseData();
087        }
088        
089        private void parseData() throws JSONException {
090                if (commodity.contains(":")) {
091                        multiValued = true;
092                        
093                        if (commodity.startsWith(ITEM_PREFIX)) {
094//                              item_factory_core                                                               resolved
095//                              item_modspec:converted_hangar                                           resolved
096//                              item_modspec:{}                                                                         unresolved
097//                              item_:{}                                                                                        unresolved
098//                              item_modspec:{tier:3, tags:[shields]}                           unresolved
099//                              item_:{tags:[modspec], p:{tier:3, tags:[engines]}       unresolved
100                                
101                                String test = commodity.replaceFirst(ITEM_PREFIX, "");
102                                int index = test.indexOf(':');
103                                if (index < 0) {
104                                        multiValued = false;
105                                } else {
106                                        String itemId = test.substring(0, index);
107                                        String secondPart = test.substring(index + 1);
108                                        
109                                        if (itemId.isEmpty() && secondPart.startsWith("{")) {
110                                                JSONObject json = new JSONObject(secondPart);
111                                                
112                                                tier = json.optInt("tier", -1);
113                                                if (json.has("tags")) {
114                                                        JSONArray tags = json.getJSONArray("tags");
115                                                        for (int i = 0; i < tags.length(); i++) {
116                                                                this.tags.add(tags.getString(i));
117                                                        }
118                                                }
119                                        } else if (!secondPart.startsWith("{")) {
120                                                multiValued = false;
121                                        }
122                                }
123                                return;
124                        }
125                        
126                        
127                        JSONObject json = new JSONObject(commodity.substring(commodity.indexOf(":") + 1));
128                        tier = json.optInt("tier", -1);
129                        if (json.has("tags")) {
130                                JSONArray tags = json.getJSONArray("tags");
131                                for (int i = 0; i < tags.length(); i++) {
132                                        this.tags.add(tags.getString(i));
133                                }
134                        }
135                        if (json.has("weaponType")) {
136                                weaponType = Misc.mapToEnum(json, "weaponType", WeaponType.class, null);
137                        }
138                        if (json.has("weaponSize")) {
139                                weaponSize = Misc.mapToEnum(json, "weaponSize", WeaponSize.class, null);
140                        }
141                }
142        }
143        
144        public DropGroupRow(String commodity, String group, float freq) {
145                this.commodity = commodity;
146                this.group = group;
147                this.freq = freq;
148                try {
149                        parseData();
150                } catch (JSONException e) {
151                        throw new RuntimeException(e);
152                }
153        }
154
155        public CommoditySpecAPI getSpec() {
156                if (isNothing() || isWeapon()) return null;
157                
158                CommoditySpecAPI spec = Global.getSector().getEconomy().getCommoditySpec(commodity);
159                return spec;
160        }
161        
162        public WeaponSpecAPI getWeaponSpec() {
163                if (!isWeapon()) return null;
164                
165                WeaponSpecAPI spec = Global.getSettings().getWeaponSpec(getWeaponId());
166                return spec;
167        }
168        
169//      public HullModSpecAPI getHullModSpec() {
170//              if (!isHullMod()) return null;
171//              
172//              HullModSpecAPI spec = Global.getSettings().getHullModSpec(getHullModId());
173//              return spec;
174//      }
175        
176        public float getBaseUnitValue() {
177                if (isMultiValued()) throw new RuntimeException("Call resolveToSpecificItem() before calling getBaseUnitValue()");
178                
179                if (isWeapon()) {
180                        return getWeaponSpec().getBaseValue();
181//              } else if (isHullMod()) {
182//                      return getHullModSpec().getBaseValue();
183                } else if (isFighterWing()) {
184                        return getFighterWingSpec().getBaseValue();
185                } else if (isSpecialItem()) {
186                        CargoStackAPI stack = Global.getFactory().createCargoStack(CargoItemType.SPECIAL, 
187                                        new SpecialItemData(getSpecialItemId(), getSpecialItemData()), null);
188                        return stack.getPlugin().getPrice(null, null);
189                } else {
190                        return getSpec().getBasePrice();
191                }
192        }
193        
194        public FighterWingSpecAPI getFighterWingSpec() {
195                if (!isFighterWing()) return null;
196                
197                FighterWingSpecAPI spec = Global.getSettings().getFighterWingSpec(getFighterWingId());
198                return spec;
199        }
200        
201        public boolean isCommodity() {
202                return !isNothing() && !isWeapon() && !isFighterWing() && !isSpecialItem();
203        }
204        
205        public boolean isWeapon() {
206                return commodity != null && commodity.startsWith(WEAPON_PREFIX);
207        }
208        
209        public String getWeaponId() {
210                return commodity.substring(WEAPON_PREFIX.length());
211        }
212        
213        public String getSpecialItemId() {
214                String afterPrefix = commodity.substring(ITEM_PREFIX.length());
215                int index = afterPrefix.indexOf(":");
216                if (index >= 0) {
217                        afterPrefix = afterPrefix.substring(0, index);
218                }
219                return afterPrefix;
220        }
221        public String getSpecialItemData() {
222                String afterPrefix = commodity.substring(ITEM_PREFIX.length());
223                int index = afterPrefix.indexOf(":");
224                if (index >= 0) {
225                        afterPrefix = afterPrefix.substring(index + 1);
226                        return afterPrefix;
227                }
228                return null;
229        }
230        
231        public SpecialItemSpecAPI getSpecialItemSpec() {
232                if (!isSpecialItem()) return null;
233                
234                SpecialItemSpecAPI spec = Global.getSettings().getSpecialItemSpec(getSpecialItemId());
235                return spec;
236        }
237        
238        public boolean isFighterWing() {
239                return commodity != null && commodity.startsWith(FIGHTER_PREFIX);
240        }
241        public boolean isSpecialItem() {
242                return commodity != null && commodity.startsWith(ITEM_PREFIX);
243        }
244        
245        public String getFighterWingId() {
246                return commodity.substring(FIGHTER_PREFIX.length());
247        }
248        
249//      public boolean isHullMod() {
250//              return commodity != null && commodity.startsWith(MOD_PREFIX);
251//      }
252//      
253//      public String getHullModId() {
254//              return commodity.substring(MOD_PREFIX.length());
255//      }
256        
257        public boolean isMultiValued() {
258                return multiValued;
259        }
260        
261        public boolean isNothing() {
262                return commodity == null || commodity.equals(NOTHING);
263        }
264
265        public String getCommodity() {
266                return commodity;
267        }
268
269        public void setCommodity(String commodity) {
270                this.commodity = commodity;
271        }
272
273        public String getGroup() {
274                return group;
275        }
276
277        public void setGroup(String group) {
278                this.group = group;
279        }
280
281        public float getFreq() {
282                return freq;
283        }
284
285        public void setFreq(float freq) {
286                this.freq = freq;
287        }
288        
289
290        public static WeightedRandomPicker<DropGroupRow> getPicker(String group) {
291                WeightedRandomPicker<DropGroupRow> picker = new WeightedRandomPicker<DropGroupRow>();
292                Collection<DropGroupRow> specs = Global.getSettings().getAllSpecs(DropGroupRow.class);
293                
294                for (DropGroupRow spec : specs) {
295//                      if (!spec.isMultiValued() && spec.isHullMod() && spec.getHullModSpec().hasTag(Tags.HULLMOD_NO_DROP)) {
296//                              continue;
297//                      }
298                        if (!spec.isMultiValued() && spec.isFighterWing() && spec.getFighterWingSpec().hasTag(Tags.WING_NO_DROP)) {
299                                continue;
300                        }
301                        
302                        if (spec.getGroup().equals(group)) {
303                                picker.add(spec, spec.getFreq());
304                        }
305                }
306                
307                for (DropGroupRow curr : picker.getItems()) {
308                        if (curr.isNothing() && curr.percent) {
309                                float totalOther = picker.getTotal() - curr.freq;
310                                
311                                float fNothing = curr.freq / 100f;
312                                if (fNothing < 0) fNothing = 0;
313                                if (fNothing > 1) fNothing = 1;
314                                
315                                float weightNothing = totalOther * fNothing / (1f - fNothing);
316                                
317                                picker.setWeight(picker.getItems().indexOf(curr), weightNothing);
318                                break;
319                        }
320                }
321                
322                if (picker.isEmpty()) {
323                        throw new RuntimeException("No drop data found for drop group [" + group + "], probably an error in drop_groups.csv");
324                }
325                
326                return picker;
327        }
328        
329        
330        public DropGroupRow resolveToSpecificItem(Random random) {
331                if (random == null) random = new Random();
332                
333                if (!isMultiValued()) return this;
334                
335                DropGroupRow copy = clone();
336                copy.multiValued = false;
337                
338                if (isSpecialItem()) {
339//                      item_factory_core                                                               resolved
340//                      item_modspec:converted_hangar                                           resolved
341//                      item_modspec:{}                                                                         unresolved
342//                      item_:{}                                                                                        unresolved
343//                      item_modspec:{tier:3, tags:[shields]}                           unresolved
344//                      item_:{tags:[modspec], p:{tier:3, tags:[engines]}       unresolved
345                        
346                        String test = commodity.replaceFirst(ITEM_PREFIX, "");
347                        int index = test.indexOf(':');
348                        if (index < 0) {
349                        } else {
350                                boolean getParamsFromP = false;
351                                String itemId = test.substring(0, index);
352                                String params = test.substring(index + 1);
353                                
354                                if (!itemId.isEmpty()) {
355                                        // params are already set properly, and we have an item id - do nothing
356                                } else {
357                                        List<SpecialItemSpecAPI> specs = Global.getSettings().getAllSpecialItemSpecs();
358                                        
359                                        Iterator<SpecialItemSpecAPI> iter = specs.iterator();
360//                                      while (iter.hasNext()) {
361//                                              SpecialItemSpecAPI curr = iter.next();
362//                                              if (curr.isHidden() || curr.isHiddenEverywhere()) iter.remove();
363//                                      }
364                                        
365                                        if (!tags.isEmpty()) {
366                                                iter = specs.iterator();
367                                                while (iter.hasNext()) {
368                                                        SpecialItemSpecAPI curr = iter.next();
369                                                        for (String tag : tags) {
370                                                                boolean not = tag.startsWith("!");
371                                                                tag = not ? tag.substring(1) : tag;
372                                                                boolean has = curr.hasTag(tag);
373                                                                if (not == has) {
374                                                                        iter.remove();
375                                                                        break;
376                                                                }
377                                                        }
378                                                }
379                                        }
380                                        
381                                        WeightedRandomPicker<SpecialItemSpecAPI> picker = new WeightedRandomPicker<SpecialItemSpecAPI>(random);
382                                        for (SpecialItemSpecAPI spec : specs) {
383                                                picker.add(spec, 1f * spec.getRarity());
384                                        }
385                                        SpecialItemSpecAPI pick = picker.pick();
386                                        if (pick == null) {
387                                                copy.commodity = NOTHING;
388                                        } else {
389                                                itemId = pick.getId(); 
390                                                getParamsFromP = true;
391                                        }
392                                }
393                                
394                                // we've picked an itemId to use
395                                if (!itemId.isEmpty()) {
396//                                      item_:{tags:[modspec], p:{tier:3, tags:[engines]}       unresolved
397                                        try {
398                                                if (getParamsFromP) {
399                                                        JSONObject json = new JSONObject(params);
400                                                        if (json.has("p")) {
401                                                                params = json.getJSONObject("p").toString();
402                                                        } else {
403                                                                params = "{}";
404                                                        }
405                                                }
406                                                
407                                                SpecialItemSpecAPI spec = Global.getSettings().getSpecialItemSpec(itemId);
408                                                SpecialItemPlugin plugin = spec.getNewPluginInstance(null);
409                                                String itemData = plugin.resolveDropParamsToSpecificItemData(params, random);
410                                                
411                                                if (itemData == null) {
412                                                        copy.commodity = NOTHING;
413                                                } else if (itemData.isEmpty()) {
414                                                        copy.commodity = ITEM_PREFIX + itemId;
415                                                } else {
416                                                        copy.commodity = ITEM_PREFIX + itemId + ":" + itemData;
417                                                }
418//                                              if (copy.commodity.contains("{")) {
419//                                                      System.out.println("wefwefew");
420//                                              }
421                                        } catch (JSONException e) {
422                                                throw new RuntimeException("Params: " + params, e);
423                                        }
424                                } else {
425                                        copy.commodity = NOTHING;
426                                }
427                        }
428//              } else if (isHullMod()) {
429//                      List<HullModSpecAPI> specs = Global.getSettings().getAllHullModSpecs();
430//                      
431//                      Iterator<HullModSpecAPI> iter = specs.iterator();
432//                      while (iter.hasNext()) {
433//                              HullModSpecAPI curr = iter.next();
434//                              if (curr.isHidden() || curr.isHiddenEverywhere()) iter.remove();
435//                      }
436//                      
437//                      if (tier >= 0) {
438//                              iter = specs.iterator();
439//                              while (iter.hasNext()) {
440//                                      HullModSpecAPI curr = iter.next();
441////                                    if (curr.getId().contains("armor")) {
442////                                            System.out.println("wfwefwe");
443////                                    }
444//                                      if (curr.getTier() != tier) iter.remove();
445//                              }
446//                      }
447//                      
448//                      if (!tags.isEmpty()) {
449//                              iter = specs.iterator();
450//                              while (iter.hasNext()) {
451//                                      HullModSpecAPI curr = iter.next();
452//                                      for (String tag : tags) {
453//                                              boolean not = tag.startsWith("!");
454//                                              tag = not ? tag.substring(1) : tag;
455//                                              boolean has = curr.hasTag(tag);
456//                                              if (not == has) {
457//                                                      iter.remove();
458//                                                      break;
459//                                              }
460//                                      }
461//                              }
462//                      }
463//                      
464//                      WeightedRandomPicker<HullModSpecAPI> picker = new WeightedRandomPicker<HullModSpecAPI>(random);
465//                      //picker.addAll(specs);
466//                      for (HullModSpecAPI spec : specs) {
467//                              picker.add(spec, 1f * spec.getRarity());
468//                      }
469//                      HullModSpecAPI pick = picker.pick();
470//                      if (pick == null) {
471//                              copy.commodity = NOTHING;
472//                      } else {
473//                              copy.commodity = MOD_PREFIX + pick.getId(); 
474//                      }
475                } else if (isWeapon()) {
476                        List<WeaponSpecAPI> specs = Global.getSettings().getAllWeaponSpecs();
477                        if (tier >= 0) {
478                                Iterator<WeaponSpecAPI> iter = specs.iterator();
479                                while (iter.hasNext()) {
480                                        WeaponSpecAPI curr = iter.next();
481                                        if (curr.getTier() != tier) iter.remove();
482                                }
483                        }
484                        
485                        if (!tags.isEmpty()) {
486                                Iterator<WeaponSpecAPI> iter = specs.iterator();
487                                while (iter.hasNext()) {
488                                        WeaponSpecAPI curr = iter.next();
489                                        for (String tag : tags) {
490                                                boolean not = tag.startsWith("!");
491                                                tag = not ? tag.substring(1) : tag;
492                                                boolean has = curr.hasTag(tag);
493                                                if (not == has) {
494                                                        iter.remove();
495                                                        break;
496                                                }
497                                        }
498                                }
499                        }
500                        
501                        if (weaponType != null || weaponSize != null) {
502                                Iterator<WeaponSpecAPI> iter = specs.iterator();
503                                while (iter.hasNext()) {
504                                        WeaponSpecAPI curr = iter.next();
505                                        if ((weaponType != null && curr.getType() != weaponType) ||
506                                                        (weaponSize != null && curr.getSize() != weaponSize)) {
507                                                iter.remove();
508                                        }
509                                }
510                        }
511                        
512                        
513                        WeightedRandomPicker<WeaponSpecAPI> picker = new WeightedRandomPicker<WeaponSpecAPI>(random);
514                        //picker.addAll(specs);
515                        for (WeaponSpecAPI spec : specs) {
516                                picker.add(spec, 1f * spec.getRarity());
517                        }
518                        WeaponSpecAPI pick = picker.pick();
519                        if (pick == null) {
520                                copy.commodity = NOTHING;
521                        } else {
522                                copy.commodity = WEAPON_PREFIX + pick.getWeaponId(); 
523                        }
524                } else if (isFighterWing()) {
525                        List<FighterWingSpecAPI> specs = Global.getSettings().getAllFighterWingSpecs();
526                        Iterator<FighterWingSpecAPI> iter = specs.iterator();
527                        while (iter.hasNext()) {
528                                FighterWingSpecAPI curr = iter.next();
529                                if (curr.hasTag(Tags.WING_NO_DROP)) iter.remove();
530                        }
531                        if (tier >= 0) {
532                                iter = specs.iterator();
533                                while (iter.hasNext()) {
534                                        FighterWingSpecAPI curr = iter.next();
535                                        if (curr.getTier() != tier) iter.remove();
536                                }
537                        }
538                        
539                        if (!tags.isEmpty()) {
540                                iter = specs.iterator();
541                                while (iter.hasNext()) {
542                                        FighterWingSpecAPI curr = iter.next();
543                                        for (String tag : tags) {
544                                                boolean not = tag.startsWith("!");
545                                                tag = not ? tag.substring(1) : tag;
546                                                boolean has = curr.hasTag(tag);
547                                                if (not == has) {
548                                                        iter.remove();
549                                                        break;
550                                                }
551                                        }
552                                }
553                        }
554                        WeightedRandomPicker<FighterWingSpecAPI> picker = new WeightedRandomPicker<FighterWingSpecAPI>(random);
555                        //picker.addAll(specs);
556                        for (FighterWingSpecAPI spec : specs) {
557                                picker.add(spec, 1f * spec.getRarity());
558                        }
559                        FighterWingSpecAPI pick = picker.pick();
560                        if (pick == null) {
561                                copy.commodity = NOTHING;
562                        } else {
563                                copy.commodity = FIGHTER_PREFIX + pick.getId(); 
564                        }
565                }
566                
567                
568                return copy;
569        }
570
571        @Override
572        public String toString() {
573                return super.toString() + " " + commodity;
574        }
575
576        
577}
578
579
580
581
582
583
584
585