Author Topic: Subsystem effects not initializing correctly?  (Read 676 times)

dalolorn

  • Sentient
  • **
  • Posts: 199
  • Karma: +7/-0
  • ABEM Developer
    • View Profile
Subsystem effects not initializing correctly?
« on: June 01, 2015, 09:51:44 AM »
I've been meaning to go on the IRC and ask the question there, seeing as it's (usually) faster, but seeing as I never remember to do it...

I've been working on sensors for ABEM. After a lot of struggling getting the sight modifiers to sort properly, everything (and by 'everything', I really just mean 'nebulae and cheating in a bunch of modifiers') seems to work - EXCEPT the reason the system was redesigned in the first place.

I've got a subsystem effect, AddScalableSubsystemSightModifier (renamed to AddSensor for convenience):

Code: [Select]
class SubsystemSightData {
bool hasLeader = false;
uint id;
float workingPercent;
any data;
}

class AddSensor : SubsystemEffect {
Document doc("Changes the sight range of the ship using a subsystem. Modifier scales based on how much of the subsystem is still operational. If no scaling is required, use AddSightModifier instead.");
Argument priority(AT_Integer, "100", doc="The order in which the modifier is executed. The lower the number, the sooner it is executed. NOTE: Only use positive integers here!");
Argument multiplier(AT_Decimal, "1", doc="The number by which the ship's base sight range and all previously executed modifiers are multiplied. NOTE: Same-priority modifiers do not multiply each other, they just combine their results when they're done!");
Argument addedRange(AT_Decimal, "0", doc="The amount of extra sight range to add, expressed in units. For reference, the sides of the biggest squares on the system grid are 500 units.");

#section server
void start(SubsystemEvent& event) const {
SubsystemSightData info;
if(event.obj is null)
return;
if(event.obj.hasLeaderAI) {
info.hasLeader = true;
info.id = event.obj.addSightModifier(uint(priority.integer), multiplier.decimal, addedRange.decimal);
info.workingPercent = event.workingPercent;
}
event.data.store(@info);
}

void tick(SubsystemEvent& event, double time) const {
SubsystemSightData@ info;
event.data.retrieve(@info);
if(event.obj is null)
return;
if(event.obj.hasLeaderAI) {
if(!info.hasLeader) {
double finalMult = multiplier.decimal * event.workingPercent;
if(finalMult < 1.0)
finalMult += 1;
info.hasLeader = true;
info.id = event.obj.addSightModifier(uint(priority.integer), finalMult, addedRange.decimal * event.workingPercent);
info.workingPercent = event.workingPercent;
}
else if(info.workingPercent != event.workingPercent) {
double finalMult = multiplier.decimal * event.workingPercent;
if(finalMult < 1.0)
finalMult += 1;
event.obj.modifySightModifier(info.id, finalMult, addedRange.decimal * event.workingPercent);
info.workingPercent = event.workingPercent;
}
}
else
info.hasLeader = false;
event.data.store(@info);
}

void end(SubsystemEvent& event) const {
SubsystemSightData@ info;
event.data.retrieve(@info);
if(event.obj is null)
return;
if(event.obj.hasLeaderAI && info.hasLeader) {
event.obj.removeSightModifier(info.id);
}
}

void save(SubsystemEvent& event, SaveFile& file) const {
SubsystemSightData@ info;
event.data.retrieve(@info);
file << info.hasLeader;
file << info.id;
file << info.workingPercent;
}

void load(SubsystemEvent& event, SaveFile& file) const {
SubsystemSightData info;
event.data.store(@info);

file >> info.hasLeader;
file >> info.id;
file >> info.workingPercent;
}
#section all
}

And I've got a BasicSensors subsystem:

Code: [Select]
Subsystem: BasicSensors
Name: Basic Sensors
Description: #S_BASIC_SENSORS_DESC
BaseColor: #9ed6ff
Elevation: 1

Tags: BaseCost, IsSensor, Category:Sensors, HasInternals, ExteriorCore, DefaultUnlock, NonContiguous
Hull: Flagship, Station

Size := HexSize * Hexes
SensorRange := 150 + (Hexes * 12.5) + log(Size/(10 *ShipSize)) * Hexes
SensorPriority := 100
Hex.Mass := HexSize / 2
Hex.Resistance := 0.1
Hex.HP := 15 * HexSize

Ship.ExternalHexes := Ship.ExternalHexes + ExteriorHexes

Modifier: SensorFactor(factor)
SensorRange := SensorRange * factor

Hook: ABEM_sensors::AddSensor(Priority = 100, AddedRange = SensorRange)

Module: Core
Sprite: TechIcons::1

Module: Default

A bunch of print-assisted debugging (keep in mind that I don't have my working copy handy right now, just the GitHub repository) indicates that the following things happen:

When the ship spawns, the subsystem triggers AddSensor.start(). Because I've printed out whether it ran correctly, I know that info gets initialized in the start() function, as designed. For some reason, though, priority.integer, multiplier.decimal and addedRange.decimal are all 0.

Things get even more complicated, though - while the tick() function does ensure that damage to the core forces it to recompute the modifier and figure out what it did wrong, damaging non-core hexes doesn't seem to have any effect. Since it's been at least a week since I did anything, I don't remember if repairing the core to full health sets the values back to 0.

Relevant files are at GitHub:

https://github.com/Alarcarr/ABEMMOD/blob/feature/Sensors/data/subsystems/sensors/BasicSensors.txt - Basic Sensors subsystem
https://github.com/Alarcarr/ABEMMOD/blob/feature/Sensors/scripts/definitions/ABEM_sensors.as - The sight modifier hooks - AddSightModifier seems to work perfectly on my working copy now that I've finished tangling with the sorting algorithms, but the subsystem variant is still in a million pieces
https://github.com/Alarcarr/ABEMMOD/blob/feature/Sensors/scripts/server/orders/LeaderAI.as - The sensor implementation in LeaderAI, in case it matters - the sight modifier functions are at the bottom of the file, just before the write functions.
« Last Edit: June 01, 2015, 09:54:52 AM by dalolorn »

GGLucas

  • Dr. Evil
  • BMS Staff
  • Delusional
  • *
  • Posts: 1877
  • Karma: +300/-6
    • View Profile
Re: Subsystem effects not initializing correctly?
« Reply #1 on: June 01, 2015, 02:36:58 PM »
Named arguments (Priority = ...) in hooks added by subsystems don't function properly due to the way they are interpreted for formulas. If you just specify them by position you should get the right values in the arguments.

I'm not quite sure what you mean by damaging non-core hexes. workingPercent is the percentage of hexes that aren't at 0 health.

dalolorn

  • Sentient
  • **
  • Posts: 199
  • Karma: +7/-0
  • ABEM Developer
    • View Profile
Re: Subsystem effects not initializing correctly?
« Reply #2 on: June 01, 2015, 02:48:52 PM »
Named arguments: Got it. It didn't occur to me to try that...

Core hexes: Ah, that could have been it. I just didn't notice the difference.

dalolorn

  • Sentient
  • **
  • Posts: 199
  • Karma: +7/-0
  • ABEM Developer
    • View Profile
Re: Subsystem effects not initializing correctly?
« Reply #3 on: June 02, 2015, 03:46:35 AM »
EDIT: I'm an idiot. My last log predates my last change to BasicSensors by less than a minute. Everything's perfectly all right now. We're fine. We're all fine here now, thank you. :P

Quote from: My original post
Uhhh, nope, apparently it DID occur to me to try that.

Working copy of BasicSensors:

Code: [Select]
Subsystem: BasicSensors
Name: Basic Sensors
Description: #S_BASIC_SENSORS_DESC
BaseColor: #9ed6ff
Elevation: 1

Tags: BaseCost, IsSensor, Category:Sensors, HasInternals, ExteriorCore, DefaultUnlock, NonContiguous
Hull: Flagship, Station

Size := HexSize * Hexes
SensorRange := 150.0 + (Hexes * 12.5) + log(Size/(10 *ShipSize)) * Hexes
SensorPriority := 100
Hex.Mass := HexSize / 2
Hex.Resistance := 0.1
Hex.HP := 15 * HexSize

Ship.ExternalHexes := Ship.ExternalHexes + ExteriorHexes

Modifier: SensorFactor(factor)
SensorRange := SensorRange * factor

Hook: ABEM_sensors::AddSensor(SensorPriority, 1.0, SensorRange)

Module: Core
Sprite: TechIcons::1

Module: Default

Working copy of AddSensor:

Code: [Select]
class SubsystemSightData {
bool hasLeader = false;
uint id;
float workingPercent;
any data;
}

class AddSensor : SubsystemEffect {
Document doc("Changes the sight range of the ship using a subsystem. Modifier scales based on how much of the subsystem is

still operational. If no scaling is required, use AddSightModifier instead.");
Argument priority(AT_Integer, "100", doc="The order in which the modifier is executed. The lower the number, the sooner it is

executed. NOTE: Only use positive integers here!");
Argument multiplier(AT_Decimal, "1.0", doc="The number by which the ship's base sight range and all previously executed

modifiers are multiplied. NOTE: Same-priority modifiers do not multiply each other, they just combine their results when they're

done!");
Argument addedRange(AT_Decimal, "0.0", doc="The amount of extra sight range to add, expressed in units. For reference, the

sides of the biggest squares on the system grid are 500 units.");

#section server
void start(SubsystemEvent& event) const {
SubsystemSightData info;
event.data.store(@info);
if(event.obj is null) {
print("Object is currently null.");
return;
}
if(event.obj.hasLeaderAI) {
print("Proper initialization done.");
info.hasLeader = true;
info.id = event.obj.addSightModifier(uint(priority.integer), multiplier.decimal, addedRange.decimal);
print(info.hasLeader);
print(event.workingPercent);
print(uint(priority.integer) + "/" + priority.integer);
print(multiplier.decimal);
print(addedRange.decimal);
info.workingPercent = event.workingPercent;
}
}

void tick(SubsystemEvent& event, double time) const {
SubsystemSightData@ info;
event.data.retrieve(@info);
event.data.store(@info);
if(event.obj is null) {
print("Object is null in current tick.");
return;
}
if(event.obj.hasLeaderAI) {
if(!info.hasLeader) {
print("Backup initialization done.");
double finalMult = multiplier.decimal * event.workingPercent;
if(finalMult < 1.0)
finalMult += 1;
info.hasLeader = true;
info.id = event.obj.addSightModifier(uint(priority.integer), finalMult, addedRange.decimal *

event.workingPercent);
print(info.hasLeader);
print(event.workingPercent);
print(uint(priority.integer) + "/" + priority.integer);
print(finalMult + "/" + multiplier.decimal);
print(addedRange.decimal);
info.workingPercent = event.workingPercent;
}
else if(info.workingPercent != event.workingPercent) {
double finalMult = multiplier.decimal * event.workingPercent;
if(finalMult < 1.0)
finalMult += 1;
event.obj.modifySightModifier(info.id, finalMult, addedRange.decimal * event.workingPercent);
info.workingPercent = event.workingPercent;
}
}
else
info.hasLeader = false;
}

void end(SubsystemEvent& event) const {
SubsystemSightData@ info;
event.data.retrieve(@info);
if(event.obj is null)
return;
if(event.obj.hasLeaderAI && info.hasLeader) {
event.obj.removeSightModifier(info.id);
}
}

void save(SubsystemEvent& event, SaveFile& file) const {
SubsystemSightData@ info;
event.data.retrieve(@info);
file << info.hasLeader;
file << info.id;
file << info.workingPercent;
}

void load(SubsystemEvent& event, SaveFile& file) const {
SubsystemSightData info;
event.data.store(@info);

file >> info.hasLeader;
file >> info.id;
file >> info.workingPercent;
}
#section all
}

And a log I just found, detailing what happens when a ship with sensors is spawned in the sandbox:

Code: [Select]
Calculating sight range... base range is 250
Final sight range: 250
Object sight range: 250
Calculating sight range... base range is 250
Final sight range: 250
Object sight range: 250
Proper initialization done.
Priority 0 is the first modifier added to this object...
addSightModifier triggered calculateSightRange...
Calculating sight range... base range is 250
Multiplier: -250/0
Added range: 0
Current bonus: -250
Final sight range: 0
Object sight range: 0
Position: 0
ID: 0
ID of data entry 0: 0
Priority: 0
Multiplier: 0
Added range: 0
Starting order printing...
Destination of index 0: 0
true
1
0/0
0
0

First it dumps the sight range calculations from leaderInit and leaderPostLoad, then a one-liner confirming that the ship DOES have LeaderAI at spawn, followed by another data dump I used to figure out if my sorting algorithm worked (hint: it didn't :D) which also happens to provide a solid indicator that something's gone horribly wrong and BLINDED the poor ship, and the last five lines are from AddSensor again.
« Last Edit: June 02, 2015, 04:31:59 AM by dalolorn »