Fractal Softworks Forum

Please login or register.

Login with username, password and session length
Pages: [1] 2 3

Author Topic: Making new Shipsystems  (Read 10262 times)

silentstormpt

  • Admiral
  • *****
  • Posts: 1060
    • View Profile
Making new Shipsystems
« on: December 07, 2012, 08:54:44 AM »

Right now ive been messing around with the scripts that control what stats a shipsystem changes while active, so as the mod im developing needs new interesting shipsystems, that aren't even close to any existing system on vanilla, i've found some problems where i don't understand what im changing due to the naming leads me to one stats, but its actually another completely different or simply doesn't do anything making me go  :-\

An example:

Heal % amount of hp from total at a flux cost;

Flux cost is done inside the shipsystem.csv
the AI is responsible to make sure NOT to use it when i don't have enough flux (and other things)
and now for the script...

with the help of LazyWizard, the methods responsible in changing hp when the system is clicked are:

stats.getMaxHullRepairFraction().modifyFlat(id, 0.5f);
stats.getHullRepairRatePercentPerSecond().modifyFlat(id, 0.5f);


The first would give me the max amount of hull i want to repair
The second would give me the rate in % i want it repaired so for example
if the first i want 20% then that would be 20% from total of the amount i want repaired
the second would be how much % i want repaired per sec, if i wanted 10% that would be 10% from those 20% repaired every second (thats hull 2% repaired every second)

Now lets see what my Podship from the UQM second ability is:
From: http://wiki.uqm.stack.nl/Podship
Four Mycon are revived in an instant, but the process consumes the Podship's entire combat battery.
Converting 4 crew in hp would exactly 20% of hull hp

After setting the script, pointing the *.system file to the script and adding the cost in flux on the shipsystem.csv i gave it a try. even with my hull at around 1% the system did not heal back so... what exactly am i doing wrong...
« Last Edit: December 07, 2012, 05:34:28 PM by silentstormpt »
Logged

LazyWizard

  • Global Moderator
  • Admiral
  • *****
  • Posts: 1365
    • View Profile
    • GitHub Profile
Re: Making new Shipsystems
« Reply #1 on: December 08, 2012, 05:06:55 PM »

The second would give me the rate in % i want it repaired so for example
if the first i want 20% then that would be 20% from total of the amount i want repaired
the second would be how much % i want repaired per sec, if i wanted 10% that would be 10% from those 20% repaired every second (thats hull 2% repaired every second)

I think the getHullRepairRatePercentPerSecond() MutableStat is based on total hull, not getMaxHullRepairFraction(). So setting it to 10.0f would repair 10% of the total hull per second, regardless of the max repair fraction.

Quote
After setting the script, pointing the *.system file to the script and adding the cost in flux on the shipsystem.csv i gave it a try. even with my hull at around 1% the system did not heal back so... what exactly am i doing wrong...

A quick shipsystem I threw together to test this seems to work fine. What does your ship_systems.csv row look like? Remember, this bonus will be removed when unapply() is called, so you'll either want 'toggle' to be TRUE or 'active' to have a value, otherwise it will have a duration of zero.
Logged

silentstormpt

  • Admiral
  • *****
  • Posts: 1060
    • View Profile
Re: Making new Shipsystems
« Reply #2 on: December 08, 2012, 06:31:23 PM »

Hum, i need it to be instant so

stats.getMaxHullRepairFraction().modifyFlat(id, 100.0f);
stats.getHullRepairRatePercentPerSecond().modifyFlat(id, 20.0f);

should do the work, making it cast in 1 sec, 1 sec cd might do it, since it will use up all the flux the ship has
« Last Edit: December 08, 2012, 06:33:00 PM by silentstormpt »
Logged

LazyWizard

  • Global Moderator
  • Admiral
  • *****
  • Posts: 1365
    • View Profile
    • GitHub Profile
Re: Making new Shipsystems
« Reply #3 on: December 08, 2012, 06:52:07 PM »

Hum, i need it to be instant so

stats.getMaxHullRepairFraction().modifyFlat(id, 100.0f);
stats.getHullRepairRatePercentPerSecond().modifyFlat(id, 20.0f);

should do the work, making it cast in 1 sec, 1 sec cd might do it, since it will use up all the flux the ship has

You could try setting the repair rate to 200 and giving it an active time of .1 seconds. That should repair 20% and be almost instantaneous.
Logged

silentstormpt

  • Admiral
  • *****
  • Posts: 1060
    • View Profile
Re: Making new Shipsystems
« Reply #4 on: December 09, 2012, 07:37:12 AM »

Yeah i just tested, works like a charm, this allows the creation of self repair ship systems.

Now ive been testing out a AI for the cloak, sadly i really dont know what im doing and the only example we got for now is for the fast missile rack AI, the AIFlags also dont have an "ignore target" in there, the closest we got right now is the:

engine.getFogOfWar(integer value)

my plan would be making the AI who targets this ship "think" its still in the FogOfWar so they cant initiate on it
Logged

silentstormpt

  • Admiral
  • *****
  • Posts: 1060
    • View Profile
Re: Making new Shipsystems
« Reply #5 on: December 12, 2012, 11:25:48 AM »

So while its working, it the repair will surpass the amount of hull the ship has,


So.. i tried getting the "com.fs.starfarer.api.combat.ShipAPI" in hoping i could grab the owner of this system (the ship) and grab the % amount it currently has, if the amount was lower then 20% it instead repair the hullamount % i got from the ship.getHitpoints();

BUT, it doesnt work since for that, i would need a ShipAPI on the actual class to work...
any ideas or the overrepair is just a bug?
Code
package data.shipsystems.scripts;

import com.fs.starfarer.api.combat.MutableShipStatsAPI;
import com.fs.starfarer.api.plugins.ShipSystemStatsScript;
import com.fs.starfarer.api.combat.ShipAPI;

public class RegenerateHitpoints  implements ShipSystemStatsScript {
public void apply(ShipAPI ship, MutableShipStatsAPI stats, String id, State state, float effectLevel) {

            float hullamount = ship.getHitpoints();

stats.getMaxHullRepairFraction().modifyFlat(id, 5000.0f); // 100% of the ship is taken in account when reparing
                if(hullamount < 20.0f)
stats.getHullRepairRatePercentPerSecond().modifyFlat(id, 20.0f); // 100% of the 20% is repaired instantly
                else
                    stats.getHullRepairRatePercentPerSecond().modifyFlat(id, hullamount);

}
public void unapply(MutableShipStatsAPI stats, String id)
        {

}

public StatusData getStatusData(int index, State state, float effectLevel) {
if (index == 0) {
return new StatusData("Regenerating Hitpoints...", false);
}
// else if (index == 1) {
// return new StatusData("shield upkeep reduced to 0", false);
// } else if (index == 2) {
// return new StatusData("shield upkeep reduced to 0", false);
// }
return null;
}
}

Heres a funny screen from what happens when a ship with 8000 hp has 16000 hp
Spoiler
[close]
Logged

LazyWizard

  • Global Moderator
  • Admiral
  • *****
  • Posts: 1365
    • View Profile
    • GitHub Profile
Re: Making new Shipsystems
« Reply #6 on: December 12, 2012, 06:10:14 PM »

So while its working, it the repair will surpass the amount of hull the ship has,


So.. i tried getting the "com.fs.starfarer.api.combat.ShipAPI" in hoping i could grab the owner of this system (the ship) and grab the % amount it currently has, if the amount was lower then 20% it instead repair the hullamount % i got from the ship.getHitpoints();

BUT, it doesnt work since for that, i would need a ShipAPI on the actual class to work...
any ideas or the overrepair is just a bug?
Code
package data.shipsystems.scripts;

import com.fs.starfarer.api.combat.MutableShipStatsAPI;
import com.fs.starfarer.api.plugins.ShipSystemStatsScript;
import com.fs.starfarer.api.combat.ShipAPI;

public class RegenerateHitpoints  implements ShipSystemStatsScript {
public void apply(ShipAPI ship, MutableShipStatsAPI stats, String id, State state, float effectLevel) {

            float hullamount = ship.getHitpoints();

stats.getMaxHullRepairFraction().modifyFlat(id, 5000.0f); // 100% of the ship is taken in account when reparing
                if(hullamount < 20.0f)
stats.getHullRepairRatePercentPerSecond().modifyFlat(id, 20.0f); // 100% of the 20% is repaired instantly
                else
                    stats.getHullRepairRatePercentPerSecond().modifyFlat(id, hullamount);

}
public void unapply(MutableShipStatsAPI stats, String id)
        {

}

public StatusData getStatusData(int index, State state, float effectLevel) {
if (index == 0) {
return new StatusData("Regenerating Hitpoints...", false);
}
// else if (index == 1) {
// return new StatusData("shield upkeep reduced to 0", false);
// } else if (index == 2) {
// return new StatusData("shield upkeep reduced to 0", false);
// }
return null;
}
}

Heres a funny screen from what happens when a ship with 8000 hp has 16000 hp
Spoiler
[close]

Wait, that compiled? ???

You can't directly get the owner of a MutableShipStatsAPI right now (but see below). If you have access to the CombatEngineAPI, you can iterate through the ships on the battlefield and find the ship with the same stats reference, though:

Code
public static ShipAPI getOwner(MutableShipStatsAPI stats, CombatEngineAPI activeEngine)
    {
        if (activeEngine == null)
        {
            return null;
        }

        ShipAPI tmp;
        for (Iterator allShips = activeEngine.getShips().iterator(); allShips.hasNext();)
        {
            tmp = (ShipAPI) allShips.next();

            if (tmp.getMutableStats() == stats)
            {
                return tmp;
            }
        }

        return null;
    }
Code
ShipAPI ship = getOwner(stats);

The next patch will let you get the owner of a MutableShipStatsAPI. Once that lands, you can replace the above code with

Code
ShipAPI ship = (ShipAPI) stats.getEntity();

You forgot to unmodify the stats in unapply(), btw. That means this system's effects would be permanent once used. ;)

The math in your code is kind of messed up, though. You want the ship to heal 20% of the hull per use? After the next patch, you could accomplish this with the following (assuming a .1 second system duration):

Code
package data.shipsystems.scripts;

import com.fs.starfarer.api.combat.MutableShipStatsAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.plugins.ShipSystemStatsScript;

public class RegenerateHitpoints implements ShipSystemStatsScript
{
    @Override
    public void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel)
    {
        ShipAPI ship = (ShipAPI) stats.getEntity();
        stats.getMaxHullRepairFraction().modifyFlat(id,
                Math.min(1f, ship.getHullLevel() + .2f));
        stats.getHullRepairRatePercentPerSecond().modifyFlat(id, 200.0f);
    }

    @Override
    public void unapply(MutableShipStatsAPI stats, String id)
    {
        stats.getMaxHullRepairFraction().unmodify(id);
        stats.getHullRepairRatePercentPerSecond().unmodify(id);
    }

    @Override
    public StatusData getStatusData(int index, State state, float effectLevel)
    {
        if (index == 0)
        {
            return new StatusData("Regenerating Hitpoints...", false);
        }

        return null;
    }
}
Logged

silentstormpt

  • Admiral
  • *****
  • Posts: 1060
    • View Profile
Re: Making new Shipsystems
« Reply #7 on: December 13, 2012, 03:49:40 AM »

Hum, the code i posted would be an example, so yh it wouldnt compile if i wanted, the ShipSystemStatsScript doesnt have a ShipAPI ship.
Quote
You want the ship to heal 20% of the hull per use?
Yes: ive tested lower then 1 sec on activated and it doesnt work, it needs to be 1 or +, im guessing it only accepts/reads integer values. This means it need to heal 20% of the hull (if the hull hitpoint % = or > then 20% missing) else heals the current missing % in 1 second. The massive % in there was to make it instant.

Quote
ShipAPI ship = getOwner(stats);
I didnt know the getOwner object was indeed the ShipAPI ship, since i did check all the methods i could see on the MutableShipStatsAPI

With that i should be able to get the current ship HP %.

I didnt know the unapply needed to be set, thought after it did the stats change the fields on the shipsystem.csv "Activate", "Deactivate" and "Toggle" would make the effect stop
« Last Edit: December 13, 2012, 04:01:00 AM by silentstormpt »
Logged

silentstormpt

  • Admiral
  • *****
  • Posts: 1060
    • View Profile
Re: Making new Shipsystems
« Reply #8 on: December 14, 2012, 08:12:15 AM »

Quote
One question this needs 2 fields:

ShipAPI ship = getOwner(stats);

should i send a null activeEngine?

Yeah i get a nullexception because i added (stats,null).

I feel im not understanding what are the values returned by the MutableShipStats, for once i thought it returned 1f if the ship had 100%, after your first reply i thought it actually returned a float of 100f if the ship was 100% hp

heres the current script i got in:
Code
package data.shipsystems.scripts;

import java.util.Iterator;

import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.MutableShipStatsAPI;
import com.fs.starfarer.api.plugins.ShipSystemStatsScript;
import com.fs.starfarer.api.combat.ShipAPI;


public class RegenerateHitpoints  implements ShipSystemStatsScript {

public void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel) {
            ShipAPI ship = getOwner(stats,null);
            float fCurrentHPperc = (ship.getMaxHitpoints() - ship.getHitpoints()) / 100; //finding the % of current hull here

stats.getMaxHullRepairFraction().modifyFlat(id, Math.min(1f, ship.getHullLevel() + .2f)); // 100% of the ship is taken in account when reparing
                if(fCurrentHPperc < 0.8f) //checking if its less then 20% to repair
stats.getHullRepairRatePercentPerSecond().modifyFlat(id, ship.getHullLevel() / 5); // 100% / 5 is 20%
                else
                    stats.getHullRepairRatePercentPerSecond().modifyFlat(id, (1f - fCurrentHPperc) * 100); // else its less

}
public void unapply(MutableShipStatsAPI stats, String id)
        {
            stats.getMaxHullRepairFraction().unmodify(id);
            stats.getHullRepairRatePercentPerSecond().unmodify(id);
}

public StatusData getStatusData(int index, State state, float effectLevel) {
if (index == 0) {
return new StatusData("Regenerating Hitpoints...", false);
}
// else if (index == 1) {
// return new StatusData("shield upkeep reduced to 0", false);
// } else if (index == 2) {
// return new StatusData("shield upkeep reduced to 0", false);
// }
return null;
}

        public static ShipAPI getOwner(MutableShipStatsAPI stats, CombatEngineAPI activeEngine)
        {
            if (activeEngine == null)
                return null;

            ShipAPI tmp;
            for (Iterator allShips = activeEngine.getShips().iterator(); allShips.hasNext();)
            {
                tmp = (ShipAPI) allShips.next();

                if (tmp.getMutableStats() == stats)
                    return tmp;
            }
            return null;
        }
}
« Last Edit: December 14, 2012, 11:56:00 AM by silentstormpt »
Logged

LazyWizard

  • Global Moderator
  • Admiral
  • *****
  • Posts: 1365
    • View Profile
    • GitHub Profile
Re: Making new Shipsystems
« Reply #9 on: December 14, 2012, 04:14:50 PM »

Quote
One question this needs 2 fields:

ShipAPI ship = getOwner(stats);

should i send a null activeEngine?

You'll definitely need the CombatEngineAPI argument, since it uses the engine to find the ship ;). Here's a class I wrote that will make things easier (but remember that the next hotfix makes this completely unnecessary!).

data/scripts/plugins/CombatUtils.java
Code
package data.scripts.plugins;

import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.EveryFrameCombatPlugin;
import com.fs.starfarer.api.combat.MutableShipStatsAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import java.util.Iterator;
import java.util.List;

public class CombatUtils implements EveryFrameCombatPlugin
{
    private static CombatEngineAPI engine;
    private static float combatTime;

    public static CombatEngineAPI getCombatEngine()
    {
        return engine;
    }

    public static float getElapsedCombatTime()
    {
        return combatTime;
    }

    public static ShipAPI getOwner(MutableShipStatsAPI stats)
    {
        if (engine == null)
        {
            return null;
        }

        ShipAPI tmp;
        for (Iterator allShips = engine.getShips().iterator(); allShips.hasNext();)
        {
            tmp = (ShipAPI) allShips.next();

            if (tmp.getMutableStats() == stats)
            {
                return tmp;
            }
        }

        return null;
    }

    @Override
    public void advance(float amount, List events)
    {
        combatTime += amount;
    }

    @Override
    public void init(CombatEngineAPI engine)
    {
        CombatUtils.engine = engine;
        combatTime = 0f;
    }
}

With this, you can remove your getOwner() method and just use ShipAPI ship = CombatUtils.getOwner(stats).


Quote
I feel im not understanding what are the values returned by the MutableShipStats, for once i thought it returned 1f if the ship had 100%, after your first reply i thought it actually returned a float of 100f if the ship was 100% hp

MutableStat and StatBonus are actually fully defined in the API (and not just interfaces), so you can look at the code for them in starfarer.api.zip. As far as I know, you will only be able to query them about how big of a bonus they grant, not get the base/recalculated values.

You'll probably want to use ship.getHullLevel(). That returns the hull percent normalized between 0 and 1, like you are looking for. If you use Math.min(1.0f, ship.getHullLevel() + .2) like I did in my example code, that will clamp the maximum healed to 100% without needing to worry about using an if/else.
Logged

silentstormpt

  • Admiral
  • *****
  • Posts: 1060
    • View Profile
Re: Making new Shipsystems
« Reply #10 on: December 14, 2012, 06:41:39 PM »

Yeah ive tried adding the starfarer.zip.api to the library but netbeans doesnt show that option, ive only been using the starfarer.api.jar, that doesnt show the returned values for most API methods...
Logged

LazyWizard

  • Global Moderator
  • Admiral
  • *****
  • Posts: 1365
    • View Profile
    • GitHub Profile
Re: Making new Shipsystems
« Reply #11 on: December 14, 2012, 06:44:32 PM »

Yeah ive tried adding the starfarer.zip.api to the library but netbeans doesnt show that option, ive only been using the starfarer.api.jar, that doesnt show the returned values for most API methods...

In the Projects window, open the Libraries tab and right click on starfarer.api.jar. Go to Edit - Sources - Browse and select starfarer.api.zip. :)

Edit: for a more permanent solution, right click on Libraries, select Properties, and create your own libary - make sure to include lwjgl_util.jar as well so you can use Vector2f. Once you've done this, you can add the required jars to any project via Libraries - Add Library.

Spoiler

[close]
« Last Edit: December 14, 2012, 06:50:52 PM by LazyWizard »
Logged

silentstormpt

  • Admiral
  • *****
  • Posts: 1060
    • View Profile
Re: Making new Shipsystems
« Reply #12 on: December 15, 2012, 05:49:45 AM »

Yeah i did what you told me, now theres only a few methods that do not possess a small "explanation" of what it does, so ive done what you said it still returning the null, strange...

Quote
91453 [Thread-6] ERROR com.fs.starfarer.combat.String  - java.lang.NullPointerException
java.lang.NullPointerException
   at data.shipsystems.scripts.RegenerateHitpoints.apply(RegenerateHitpoints.java:15)
   at com.fs.starfarer.combat.ai.system.I.<init>(Unknown Source)
   at com.fs.starfarer.loading.specs.nullsuper.o00000(Unknown Source)
   at com.fs.starfarer.combat.ai.BasicShipAI.<init>(Unknown Source)
   at com.fs.starfarer.combat.CombatFleetManager.o00000(Unknown Source)
   at com.fs.starfarer.combat.CombatFleetManager.o00000(Unknown Source)
   at com.fs.starfarer.combat.CombatFleetManager.deploy(Unknown Source)
   at com.fs.starfarer.combat.CombatFleetManager.deployAll(Unknown Source)
   at com.fs.starfarer.combat.OOoO.ÕØÒ000(Unknown Source)
   at com.fs.super.A.new(Unknown Source)
   at com.fs.starfarer.combat.String.o00000(Unknown Source)
   at com.fs.starfarer.StarfarerLauncher$2.run(Unknown Source)
   at java.lang.Thread.run(Thread.java:619)

Quote
package data.shipsystems.scripts;

import com.fs.starfarer.api.combat.MutableShipStatsAPI;
import com.fs.starfarer.api.plugins.ShipSystemStatsScript;
import com.fs.starfarer.api.combat.ShipAPI;
import data.scripts.plugins.CombatUtils;

public class RegenerateHitpoints  implements ShipSystemStatsScript {

   public void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel) {
        ShipAPI ship = CombatUtils.getOwner(stats);

        stats.getMaxHullRepairFraction().modifyPercent(id, 1f); // 100% of the ship is taken in account when reparing

        if(ship.getHullLevel() < 0.2f) //checking if its less then 20% to repair
            stats.getHullRepairRatePercentPerSecond().modifyPercent(id, ship.getHullLevel());
        else
            stats.getHullRepairRatePercentPerSecond().modifyPercent(id, 0.2f);

   }
   public void unapply(MutableShipStatsAPI stats, String id)
        {
            stats.getMaxHullRepairFraction().unmodify(id);
            stats.getHullRepairRatePercentPerSecond().unmodify(id);
   }

   public StatusData getStatusData(int index, State state, float effectLevel) {
      if (index == 0) {
         return new StatusData("Regenerating Hitpoints...", false);
      }
//      else if (index == 1) {
//         return new StatusData("shield upkeep reduced to 0", false);
//      } else if (index == 2) {
//         return new StatusData("shield upkeep reduced to 0", false);
//      }
      return null;
   }
}

The only reason im not using the Math.min is to make to code as simple as possible since i want it to "readable" for anyone who wants to use part of it for their own code

EDIT: since it should return null if the engine is null im guessing the problem is here:

Code
        if (engine == null)
        {
            return null;
        }
« Last Edit: December 15, 2012, 06:50:30 AM by silentstormpt »
Logged

LazyWizard

  • Global Moderator
  • Admiral
  • *****
  • Posts: 1365
    • View Profile
    • GitHub Profile
Re: Making new Shipsystems
« Reply #13 on: December 15, 2012, 07:42:36 PM »

Hmm, engine should never be null since it's automatically set at the start of each battle. It's more likely that it can't find your ship on the field for some reason. Could you send me the mod so I can take a closer look?

The only reason im not using the Math.min is to make to code as simple as possible since i want it to "readable" for anyone who wants to use part of it for their own code
This is what comments are for. ;)
Logged

silentstormpt

  • Admiral
  • *****
  • Posts: 1060
    • View Profile
Re: Making new Shipsystems
« Reply #14 on: December 16, 2012, 06:43:48 AM »

Ok let me upload it, a it will take a while, since, well... its the biggest mod currently in star farer only due to the amount of music 118 mbs :S expect 30 mins of upload:

EDIT finally: http://www.mediafire.com/?5se5by2oyha07o1
« Last Edit: December 16, 2012, 07:05:10 AM by silentstormpt »
Logged
Pages: [1] 2 3