Rework warp time graph to use new getter approach
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
# noinspection PyUnresolvedReferences
|
||||
from gui.builtinGraphs import ( # noqa: E402,F401
|
||||
fitDamageStats,
|
||||
# fitDmgVsTime,
|
||||
fitShieldRegen,
|
||||
fitCapRegen,
|
||||
fitMobility,
|
||||
fitWarpTime
|
||||
)
|
||||
fitWarpTime)
|
||||
|
||||
@@ -232,13 +232,12 @@ class FitGraph(metaclass=ABCMeta):
|
||||
|
||||
def _getPoints(self, mainParam, miscParams, xSpec, ySpec, fit, tgt):
|
||||
try:
|
||||
fullGetter, singleGetter, cacheGetter = self._getters[(xSpec.handle, ySpec.handle)]
|
||||
getterClass = self._getters[(xSpec.handle, ySpec.handle)]
|
||||
except KeyError:
|
||||
return [], []
|
||||
else:
|
||||
return fullGetter(
|
||||
self, cacheGetter=cacheGetter, singleGetter=singleGetter,
|
||||
mainParam=mainParam, miscParams=miscParams, fit=fit, tgt=tgt)
|
||||
getter = getterClass(graph=self)
|
||||
return getter.getRange(mainParam=mainParam, miscParams=miscParams, fit=fit, tgt=tgt)
|
||||
|
||||
_denormalizers = {}
|
||||
|
||||
@@ -249,11 +248,52 @@ class FitGraph(metaclass=ABCMeta):
|
||||
values = [denormalizer(v, fit, tgt) for v in values]
|
||||
return values
|
||||
|
||||
def _iterLinear(self, valRange, segments=200):
|
||||
|
||||
class PointGetter(metaclass=ABCMeta):
|
||||
|
||||
def __init__(self, graph):
|
||||
self.graph = graph
|
||||
|
||||
@abstractmethod
|
||||
def getRange(self, mainParam, miscParams, fit, tgt):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def getPoint(self, mainParam, miscParams, fit, tgt, cache=None):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class SmoothPointGetter(PointGetter, metaclass=ABCMeta):
|
||||
|
||||
def __init__(self, graph, baseResolution=200):
|
||||
super().__init__(graph)
|
||||
self._baseResolution = baseResolution
|
||||
|
||||
def getRange(self, mainParam, miscParams, fit, tgt):
|
||||
xs = []
|
||||
ys = []
|
||||
commonData = self._getCommonData(miscParams=miscParams, fit=fit, tgt=tgt)
|
||||
for x in self._iterLinear(mainParam[1]):
|
||||
y = self._calculatePoint(
|
||||
mainParam=x, miscParams=miscParams,
|
||||
fit=fit, tgt=tgt, commonData=commonData)
|
||||
xs.append(x)
|
||||
ys.append(y)
|
||||
return xs, ys
|
||||
|
||||
def getPoint(self, mainParam, miscParams, fit, tgt):
|
||||
commonData = self._getCommonData(miscParams=miscParams, fit=fit, tgt=tgt)
|
||||
y = self._calculatePoint(
|
||||
mainParam=mainParam, miscParams=miscParams,
|
||||
fit=fit, tgt=tgt, commonData=commonData)
|
||||
return mainParam, y
|
||||
|
||||
def _iterLinear(self, valRange):
|
||||
rangeLow = min(valRange)
|
||||
rangeHigh = max(valRange)
|
||||
# Amount is amount of ranges between points here, not amount of points
|
||||
step = (rangeHigh - rangeLow) / segments
|
||||
# Resolution defines amount of ranges between points here,
|
||||
# not amount of points
|
||||
step = (rangeHigh - rangeLow) / self._baseResolution
|
||||
if step == 0 or math.isnan(step):
|
||||
yield rangeLow
|
||||
else:
|
||||
@@ -264,6 +304,13 @@ class FitGraph(metaclass=ABCMeta):
|
||||
yield current
|
||||
current += step
|
||||
|
||||
def _getCommonData(self, miscParams, fit, tgt):
|
||||
return {}
|
||||
|
||||
@abstractmethod
|
||||
def _calculatePoint(self, mainParam, miscParams, fit, tgt, commonData):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class FitDataCache:
|
||||
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
|
||||
import math
|
||||
|
||||
from eos.const import FittingModuleState
|
||||
from service.const import GraphCacheCleanupReason
|
||||
from .base import FitGraph, XDef, YDef, Input, FitDataCache
|
||||
|
||||
|
||||
AU_METERS = 149597870700
|
||||
|
||||
|
||||
class FitWarpTimeGraph(FitGraph):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._subspeedCache = SubwarpSpeedCache()
|
||||
|
||||
def _clearInternalCache(self, reason, extraData):
|
||||
if reason in (GraphCacheCleanupReason.fitChanged, GraphCacheCleanupReason.fitRemoved):
|
||||
self._subspeedCache.clearForFit(extraData)
|
||||
elif reason == GraphCacheCleanupReason.graphSwitched:
|
||||
self._subspeedCache.clearAll()
|
||||
|
||||
# UI stuff
|
||||
internalName = 'warpTimeGraph'
|
||||
name = 'Warp Time'
|
||||
xDefs = [
|
||||
XDef(handle='distance', unit='AU', label='Distance', mainInput=('distance', 'AU')),
|
||||
XDef(handle='distance', unit='km', label='Distance', mainInput=('distance', 'km'))]
|
||||
yDefs = [
|
||||
YDef(handle='time', unit='s', label='Warp time')]
|
||||
inputs = [
|
||||
Input(handle='distance', unit='AU', label='Distance', iconID=1391, defaultValue=20, defaultRange=(0, 50)),
|
||||
Input(handle='distance', unit='km', label='Distance', iconID=1391, defaultValue=1000, defaultRange=(150, 5000))]
|
||||
srcExtraCols = ('WarpSpeed', 'WarpDistance')
|
||||
|
||||
# Calculation stuff
|
||||
_normalizers = {
|
||||
('distance', 'AU'): lambda v, fit, tgt: v * AU_METERS,
|
||||
('distance', 'km'): lambda v, fit, tgt: v * 1000}
|
||||
_limiters = {
|
||||
'distance': lambda fit, tgt: (0, fit.maxWarpDistance * AU_METERS)}
|
||||
_denormalizers = {
|
||||
('distance', 'AU'): lambda v, fit, tgt: v / AU_METERS,
|
||||
('distance', 'km'): lambda v, fit, tgt: v / 1000}
|
||||
|
||||
def _distance2timeCache(self, miscParams, fit, tgt):
|
||||
return {
|
||||
'subwarpSpeed': self._subspeedCache.getSubwarpSpeed(fit),
|
||||
'warpSpeed': fit.warpSpeed}
|
||||
|
||||
def _distance2timeFull(self, cacheGetter, singleGetter, mainParam, miscParams, fit, tgt):
|
||||
xs = []
|
||||
ys = []
|
||||
cache = cacheGetter(self, miscParams=miscParams, fit=fit, tgt=tgt)
|
||||
for distance in self._iterLinear(mainParam[1]):
|
||||
time = singleGetter(
|
||||
self, cacheGetter=cacheGetter, mainParam=distance, miscParams=miscParams,
|
||||
fit=fit, tgt=tgt, cache=cache)
|
||||
xs.append(distance)
|
||||
ys.append(time)
|
||||
return xs, ys
|
||||
|
||||
def _distance2timeSingle(self, cacheGetter, mainParam, miscParams, fit, tgt, cache=None):
|
||||
if cache is None:
|
||||
cache = cacheGetter(self, miscParams=miscParams, fit=fit, tgt=tgt)
|
||||
time = calculate_time_in_warp(
|
||||
max_subwarp_speed=cache['subwarpSpeed'],
|
||||
max_warp_speed=cache['warpSpeed'],
|
||||
warp_dist=mainParam)
|
||||
return time
|
||||
|
||||
_getters = {
|
||||
('distance', 'time'): (_distance2timeFull, _distance2timeSingle, _distance2timeCache)}
|
||||
|
||||
|
||||
class SubwarpSpeedCache(FitDataCache):
|
||||
|
||||
def getSubwarpSpeed(self, fit):
|
||||
try:
|
||||
subwarpSpeed = self._data[fit.ID]
|
||||
except KeyError:
|
||||
modStates = {}
|
||||
disallowedGroups = (
|
||||
# Active modules which affect ship speed and cannot be used in warp
|
||||
'Propulsion Module',
|
||||
'Mass Entanglers',
|
||||
'Cloaking Device',
|
||||
# Those reduce ship speed to 0
|
||||
'Siege Module',
|
||||
'Super Weapon',
|
||||
'Cynosural Field Generator',
|
||||
'Clone Vat Bay',
|
||||
'Jump Portal Generator')
|
||||
for mod in fit.modules:
|
||||
if mod.item is not None and mod.item.group.name in disallowedGroups and mod.state >= FittingModuleState.ACTIVE:
|
||||
modStates[mod] = mod.state
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
projFitStates = {}
|
||||
for projFit in fit.projectedFits:
|
||||
projectionInfo = projFit.getProjectionInfo(fit.ID)
|
||||
if projectionInfo is not None and projectionInfo.active:
|
||||
projFitStates[projectionInfo] = projectionInfo.active
|
||||
projectionInfo.active = False
|
||||
projModStates = {}
|
||||
for mod in fit.projectedModules:
|
||||
if not mod.isExclusiveSystemEffect and mod.state >= FittingModuleState.ACTIVE:
|
||||
projModStates[mod] = mod.state
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
projDroneStates = {}
|
||||
for drone in fit.projectedDrones:
|
||||
if drone.amountActive > 0:
|
||||
projDroneStates[drone] = drone.amountActive
|
||||
drone.amountActive = 0
|
||||
projFighterStates = {}
|
||||
for fighter in fit.projectedFighters:
|
||||
if fighter.active:
|
||||
projFighterStates[fighter] = fighter.active
|
||||
fighter.active = False
|
||||
fit.calculateModifiedAttributes()
|
||||
subwarpSpeed = fit.ship.getModifiedItemAttr('maxVelocity')
|
||||
self._data[fit.ID] = subwarpSpeed
|
||||
for projInfo, state in projFitStates.items():
|
||||
projInfo.active = state
|
||||
for mod, state in modStates.items():
|
||||
mod.state = state
|
||||
for mod, state in projModStates.items():
|
||||
mod.state = state
|
||||
for drone, amountActive in projDroneStates.items():
|
||||
drone.amountActive = amountActive
|
||||
for fighter, state in projFighterStates.items():
|
||||
fighter.active = state
|
||||
fit.calculateModifiedAttributes()
|
||||
return subwarpSpeed
|
||||
|
||||
|
||||
# Taken from https://wiki.eveuniversity.org/Warp_time_calculation#Implementation
|
||||
# with minor modifications
|
||||
# Warp speed in AU/s, subwarp speed in m/s, distance in m
|
||||
def calculate_time_in_warp(max_warp_speed, max_subwarp_speed, warp_dist):
|
||||
|
||||
if warp_dist == 0:
|
||||
return 0
|
||||
|
||||
k_accel = max_warp_speed
|
||||
k_decel = min(max_warp_speed / 3, 2)
|
||||
|
||||
warp_dropout_speed = max_subwarp_speed / 2
|
||||
max_ms_warp_speed = max_warp_speed * AU_METERS
|
||||
|
||||
accel_dist = AU_METERS
|
||||
decel_dist = max_ms_warp_speed / k_decel
|
||||
|
||||
minimum_dist = accel_dist + decel_dist
|
||||
|
||||
cruise_time = 0
|
||||
|
||||
if minimum_dist > warp_dist:
|
||||
max_ms_warp_speed = warp_dist * k_accel * k_decel / (k_accel + k_decel)
|
||||
else:
|
||||
cruise_time = (warp_dist - minimum_dist) / max_ms_warp_speed
|
||||
|
||||
accel_time = math.log(max_ms_warp_speed / k_accel) / k_accel
|
||||
decel_time = math.log(max_ms_warp_speed / warp_dropout_speed) / k_decel
|
||||
|
||||
total_time = cruise_time + accel_time + decel_time
|
||||
return total_time
|
||||
|
||||
|
||||
FitWarpTimeGraph.register()
|
||||
1
gui/builtinGraphs/fitWarpTime/__init__.py
Normal file
1
gui/builtinGraphs/fitWarpTime/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
import gui.builtinGraphs.fitWarpTime.graph # noqa: E402,F401
|
||||
74
gui/builtinGraphs/fitWarpTime/getter.py
Normal file
74
gui/builtinGraphs/fitWarpTime/getter.py
Normal file
@@ -0,0 +1,74 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
|
||||
import math
|
||||
|
||||
from gui.builtinGraphs.base import SmoothPointGetter
|
||||
|
||||
|
||||
AU_METERS = 149597870700
|
||||
|
||||
|
||||
class Distance2TimeGetter(SmoothPointGetter):
|
||||
|
||||
def _getCommonData(self, miscParams, fit, tgt):
|
||||
return {
|
||||
'subwarpSpeed': self.graph._subspeedCache.getSubwarpSpeed(fit),
|
||||
'warpSpeed': fit.warpSpeed}
|
||||
|
||||
def _calculatePoint(self, mainParam, miscParams, fit, tgt, commonData):
|
||||
time = calculate_time_in_warp(
|
||||
max_subwarp_speed=commonData['subwarpSpeed'],
|
||||
max_warp_speed=commonData['warpSpeed'],
|
||||
warp_dist=mainParam)
|
||||
return time
|
||||
|
||||
|
||||
# Taken from https://wiki.eveuniversity.org/Warp_time_calculation#Implementation
|
||||
# with minor modifications
|
||||
# Warp speed in AU/s, subwarp speed in m/s, distance in m
|
||||
def calculate_time_in_warp(max_warp_speed, max_subwarp_speed, warp_dist):
|
||||
|
||||
if warp_dist == 0:
|
||||
return 0
|
||||
|
||||
k_accel = max_warp_speed
|
||||
k_decel = min(max_warp_speed / 3, 2)
|
||||
|
||||
warp_dropout_speed = max_subwarp_speed / 2
|
||||
max_ms_warp_speed = max_warp_speed * AU_METERS
|
||||
|
||||
accel_dist = AU_METERS
|
||||
decel_dist = max_ms_warp_speed / k_decel
|
||||
|
||||
minimum_dist = accel_dist + decel_dist
|
||||
|
||||
cruise_time = 0
|
||||
|
||||
if minimum_dist > warp_dist:
|
||||
max_ms_warp_speed = warp_dist * k_accel * k_decel / (k_accel + k_decel)
|
||||
else:
|
||||
cruise_time = (warp_dist - minimum_dist) / max_ms_warp_speed
|
||||
|
||||
accel_time = math.log(max_ms_warp_speed / k_accel) / k_accel
|
||||
decel_time = math.log(max_ms_warp_speed / warp_dropout_speed) / k_decel
|
||||
|
||||
total_time = cruise_time + accel_time + decel_time
|
||||
return total_time
|
||||
66
gui/builtinGraphs/fitWarpTime/graph.py
Normal file
66
gui/builtinGraphs/fitWarpTime/graph.py
Normal file
@@ -0,0 +1,66 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
|
||||
from service.const import GraphCacheCleanupReason
|
||||
from gui.builtinGraphs.base import FitGraph, XDef, YDef, Input
|
||||
from .getter import Distance2TimeGetter, AU_METERS
|
||||
from .subwarpCache import SubwarpSpeedCache
|
||||
|
||||
|
||||
class FitWarpTimeGraph(FitGraph):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._subspeedCache = SubwarpSpeedCache()
|
||||
|
||||
def _clearInternalCache(self, reason, extraData):
|
||||
if reason in (GraphCacheCleanupReason.fitChanged, GraphCacheCleanupReason.fitRemoved):
|
||||
self._subspeedCache.clearForFit(extraData)
|
||||
elif reason == GraphCacheCleanupReason.graphSwitched:
|
||||
self._subspeedCache.clearAll()
|
||||
|
||||
# UI stuff
|
||||
internalName = 'warpTimeGraph'
|
||||
name = 'Warp Time'
|
||||
xDefs = [
|
||||
XDef(handle='distance', unit='AU', label='Distance', mainInput=('distance', 'AU')),
|
||||
XDef(handle='distance', unit='km', label='Distance', mainInput=('distance', 'km'))]
|
||||
yDefs = [
|
||||
YDef(handle='time', unit='s', label='Warp time')]
|
||||
inputs = [
|
||||
Input(handle='distance', unit='AU', label='Distance', iconID=1391, defaultValue=20, defaultRange=(0, 50)),
|
||||
Input(handle='distance', unit='km', label='Distance', iconID=1391, defaultValue=1000, defaultRange=(150, 5000))]
|
||||
srcExtraCols = ('WarpSpeed', 'WarpDistance')
|
||||
|
||||
# Calculation stuff
|
||||
_normalizers = {
|
||||
('distance', 'AU'): lambda v, fit, tgt: v * AU_METERS,
|
||||
('distance', 'km'): lambda v, fit, tgt: v * 1000}
|
||||
_limiters = {
|
||||
'distance': lambda fit, tgt: (0, fit.maxWarpDistance * AU_METERS)}
|
||||
_denormalizers = {
|
||||
('distance', 'AU'): lambda v, fit, tgt: v / AU_METERS,
|
||||
('distance', 'km'): lambda v, fit, tgt: v / 1000}
|
||||
|
||||
_getters = {
|
||||
('distance', 'time'): Distance2TimeGetter}
|
||||
|
||||
|
||||
FitWarpTimeGraph.register()
|
||||
82
gui/builtinGraphs/fitWarpTime/subwarpCache.py
Normal file
82
gui/builtinGraphs/fitWarpTime/subwarpCache.py
Normal file
@@ -0,0 +1,82 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
|
||||
from eos.const import FittingModuleState
|
||||
from gui.builtinGraphs.base import FitDataCache
|
||||
|
||||
|
||||
class SubwarpSpeedCache(FitDataCache):
|
||||
|
||||
def getSubwarpSpeed(self, fit):
|
||||
try:
|
||||
subwarpSpeed = self._data[fit.ID]
|
||||
except KeyError:
|
||||
modStates = {}
|
||||
disallowedGroups = (
|
||||
# Active modules which affect ship speed and cannot be used in warp
|
||||
'Propulsion Module',
|
||||
'Mass Entanglers',
|
||||
'Cloaking Device',
|
||||
# Those reduce ship speed to 0
|
||||
'Siege Module',
|
||||
'Super Weapon',
|
||||
'Cynosural Field Generator',
|
||||
'Clone Vat Bay',
|
||||
'Jump Portal Generator')
|
||||
for mod in fit.modules:
|
||||
if mod.item is not None and mod.item.group.name in disallowedGroups and mod.state >= FittingModuleState.ACTIVE:
|
||||
modStates[mod] = mod.state
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
projFitStates = {}
|
||||
for projFit in fit.projectedFits:
|
||||
projectionInfo = projFit.getProjectionInfo(fit.ID)
|
||||
if projectionInfo is not None and projectionInfo.active:
|
||||
projFitStates[projectionInfo] = projectionInfo.active
|
||||
projectionInfo.active = False
|
||||
projModStates = {}
|
||||
for mod in fit.projectedModules:
|
||||
if not mod.isExclusiveSystemEffect and mod.state >= FittingModuleState.ACTIVE:
|
||||
projModStates[mod] = mod.state
|
||||
mod.state = FittingModuleState.ONLINE
|
||||
projDroneStates = {}
|
||||
for drone in fit.projectedDrones:
|
||||
if drone.amountActive > 0:
|
||||
projDroneStates[drone] = drone.amountActive
|
||||
drone.amountActive = 0
|
||||
projFighterStates = {}
|
||||
for fighter in fit.projectedFighters:
|
||||
if fighter.active:
|
||||
projFighterStates[fighter] = fighter.active
|
||||
fighter.active = False
|
||||
fit.calculateModifiedAttributes()
|
||||
subwarpSpeed = fit.ship.getModifiedItemAttr('maxVelocity')
|
||||
self._data[fit.ID] = subwarpSpeed
|
||||
for projInfo, state in projFitStates.items():
|
||||
projInfo.active = state
|
||||
for mod, state in modStates.items():
|
||||
mod.state = state
|
||||
for mod, state in projModStates.items():
|
||||
mod.state = state
|
||||
for drone, amountActive in projDroneStates.items():
|
||||
drone.amountActive = amountActive
|
||||
for fighter, state in projFighterStates.items():
|
||||
fighter.active = state
|
||||
fit.calculateModifiedAttributes()
|
||||
return subwarpSpeed
|
||||
Reference in New Issue
Block a user