Hard Light Productions Forums
Modding, Mission Design, and Coding => The Modding Workshop => Topic started by: Goober5000 on November 28, 2009, 01:42:04 am
-
This is a short program I cooked up because I needed a good way to compare beam damages for balancing Scroll of Atankharzim. It's not the cleanest or best written code I've ever released, but it does the job and it produces some useful output.
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import javax.swing.JFileChooser;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.filechooser.FileFilter;
public class BeamStats
{
private static final Pattern NAME_PATTERN = Pattern.compile("\\s*\\$Name\\:.*");
private static final Pattern BEAM_FLAG_PATTERN = Pattern.compile("\\s*\\$Flags\\:\\s*\\(.*\\\"beam\\\".*\\)\\s*");
private static final double BEAM_TIMES_PER_SECOND = 5.8824;
public static void main(String[] args)
{
setLAF();
JFileChooser chooser = new JFileChooser();
chooser.setFileFilter(new FileFilter()
{
@Override
public boolean accept(File f)
{
return (f.getName().equals("weapons.tbl")) || f.isDirectory();
}
@Override
public String getDescription()
{
return "Only weapons.tbl";
}
});
chooser.setDialogTitle("Choose a weapons table");
chooser.setCurrentDirectory(new File("C:\\Games\\FreeSpace2\\"));
int returnVal = chooser.showOpenDialog(null);
if (returnVal != JFileChooser.APPROVE_OPTION)
return;
File file = chooser.getSelectedFile();
List<String> fileLines = readTextFileLines(file);
for (int i = 0; i < fileLines.size(); i++)
{
// find flag
if (!BEAM_FLAG_PATTERN.matcher(fileLines.get(i)).matches())
continue;
// backtrack to name
for (int j = i; j >= 0; j--)
{
// find name
if (NAME_PATTERN.matcher(fileLines.get(j)).matches())
{
processBeam(fileLines, j);
break;
}
}
}
}
private static void processBeam(List<String> fileLines, int lineNumber)
{
// print the name
String name = fileLines.get(lineNumber).trim();
for (int i = 6; i < name.length(); i++)
{
if (!Character.isWhitespace(name.charAt(i)))
{
System.out.println(name.substring(i));
break;
}
}
double fireWait = Double.parseDouble(findValue(fileLines, lineNumber, "$Fire Wait:"));
double damage = Double.parseDouble(findValue(fileLines, lineNumber, "$Damage:"));
double life = Double.parseDouble(findValue(fileLines, lineNumber, "+Life:"));
// for AAA beams
int shots = Integer.parseInt(findValue(fileLines, lineNumber, "+Shots:"));
if (shots <= 0)
shots = 1;
// calculate total damage per shot
double damage_per_shot = damage * life * BEAM_TIMES_PER_SECOND;
// calculate times per minute
double times_per_minute = 60.0 / fireWait;
System.out.println("Damage per shot: " + (int) (damage_per_shot));
if (shots > 1)
{
System.out.println("Shots: " + shots);
System.out.println("Cumulative damage: " + (int) (damage_per_shot) * shots);
}
System.out.println("Fire wait: " + (int) fireWait);
System.out.println("Damage per minute: " + (int) (damage_per_shot * times_per_minute * shots));
System.out.println();
}
private static String findValue(List<String> fileLines, int startLine, String startsWith)
{
for (int i = startLine; i < fileLines.size(); i++)
{
String line = fileLines.get(i).trim();
if (line.startsWith(startsWith))
{
int pos = line.indexOf(';');
if (pos >= 0)
return line.substring(startsWith.length(), pos).trim();
else
return line.substring(startsWith.length()).trim();
}
}
System.err.println("Can't find " + startsWith);
return null;
}
public static List<String> readTextFileLines(File file)
{
List<String> lines = new ArrayList<String>();
try
{
BufferedReader reader = new BufferedReader(new FileReader(file));
String str;
while ((str = reader.readLine()) != null)
lines.add(str);
reader.close();
}
catch (IOException ioe)
{
ioe.printStackTrace();
lines.clear();
}
return lines;
}
private static void setLAF()
{
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (ClassNotFoundException cnfe)
{
cnfe.printStackTrace();
}
catch (InstantiationException ie)
{
ie.printStackTrace();
}
catch (IllegalAccessException iae)
{
iae.printStackTrace();
}
catch (UnsupportedLookAndFeelException ulafe)
{
ulafe.printStackTrace();
}
}
}
It's a standard Java program with no dependencies. It compiles and runs under Java 1.6; I haven't tried it on anything earlier but you could probably get it to work on an earlier version with little trouble.
Here's the output on the standard FS2 weapons table:
@Targeting Laser
Damage per shot: 0
Fire wait: 0
Damage per minute: 0
SRed
Damage per shot: 4470
Fire wait: 25
Damage per minute: 10729
SAAA
Damage per shot: 185
Shots: 3
Cumulative damage: 555
Fire wait: 5
Damage per minute: 6670
LRed
Damage per shot: 24706
Fire wait: 10
Damage per minute: 148236
BFRed
Damage per shot: 86471
Fire wait: 10
Damage per minute: 518827
AAAf
Damage per shot: 205
Shots: 3
Cumulative damage: 615
Fire wait: 5
Damage per minute: 6738
AAAh
Damage per shot: 205
Shots: 3
Cumulative damage: 615
Fire wait: 5
Damage per minute: 6738
S-AAA-Weak
Damage per shot: 88
Shots: 3
Cumulative damage: 264
Fire wait: 5
Damage per minute: 3176
ULTRA Anti-Fighter Beam
Damage per shot: 441
Shots: 3
Cumulative damage: 1323
Fire wait: 2
Damage per minute: 36096
TerSlash
Damage per shot: 4117
Fire wait: 10
Damage per minute: 24706
LTerSlash
Damage per shot: 1941
Fire wait: 10
Damage per minute: 11647
BFGreen
Damage per shot: 44706
Fire wait: 35
Damage per minute: 76639
LRBGreen
Damage per shot: 44706
Fire wait: 35
Damage per minute: 76639
BGreen
Damage per shot: 28235
Fire wait: 30
Damage per minute: 56471
SGreen
Damage per shot: 3088
Fire wait: 45
Damage per minute: 4117
SVas
Damage per shot: 5147
Fire wait: 20
Damage per minute: 15441
BVas
Damage per shot: 23941
Fire wait: 24
Damage per minute: 59853
VSlash
Damage per shot: 8823
Fire wait: 10
Damage per minute: 52941
Green Beam
Damage per shot: 2352
Fire wait: 10
Damage per minute: 14117
MjolnirBeam
Damage per shot: 17647
Fire wait: 7
Damage per minute: 151261
MjolnirBeam#home
Damage per shot: 9411
Fire wait: 7
Damage per minute: 80672
One of the things we've changed in Scroll is the massive disparity between SGreen and SVas. :)
-
what! You're changing the stats of canonical weapons? That sucks. Reminds me of what CP did with Procyon Insurgency; he edited the Akheton SDG to make it much better and didn't tell anyone about it.
On the other hand :v: did the same things with all kinds of stats, going from FS1 to FS2. So I guess you can if you want to.
-
Not only is that off-topic, it's inapplicable because the Akheton is a player-chosen weapon and the SGreen is not... :nervous:
-
i usually just use spreadsheets for this kinda thing.
-
I thought that the lifetime of a AAA beam is divided among its shots, so that the number of shots has no effect on damage.
And where did 5.8824 come from? Just wondering.
-
I thought that the lifetime of a AAA beam is divided among its shots, so that the number of shots has no effect on damage.
Mmmm, you might be right. Easily fixed in the code, though.
And where did 5.8824 come from? Just wondering.
Beam damage is applied every 170 milliseconds, thus 5.8824 times a second.
-
^good to know, because my spreadsheets assumed damage was applied 6 times a second, which kinda threw my spreadsheets off.
-
I was actually thinking about making an app that did something similar for all sorts of weapons, based off of whatever tables you feed it. Haven't had time though.
-
Beam damage is applied every 170 milliseconds, thus 5.8824 times a second.
Is this the sort of thing that maybe should go into ai-profiles.tbl (or whatever that one that has big changes in it is) ?
That is, being able to change the interval. I imagine some mods (particularly ones with fighterbeams) might find that useful.
-
Ummm... no. That's a fundamental game mechanic. I doubt there'd be any valid reason to change it.
-
Not completely related, but...Scaling the interval with time compression would probably help reduce issues caused by, well, time compression, though...
Because as it stands right now, beams do different amounts of damage in the same number of full pulses under different levels of time compression.
-
That makes sense. If time compression is faster than 5.8824x, then of course some beam damage is going to be skipped.
And incidentally, time compression was originally capped at 4x. Perhaps this was one of the reasons why. In any case, there really isn't any reason why players should be fast-forwarding at more than 4x during a battle.
-
Unless you want to get killed, thare's absolutely no reason to fast forward during battle at all, at least well written one.
-
I think it was actually used as a hack, since it became known that you could change the damage done by using different time compression levels.
-
Sometimes you just get bored after hearing the same dialog over and over, or watching the same predefined sequence over and over, etc., and 64x time compression starts to look good.
Although... you could possibly change it so that > 4x is only enabled with cheats or sexps, as it is for < 1x
-
Really if missions with a long intro had a Press X to Fast Forward option, that'd be awesome.