2015-03-03 22:10:12 +00:00
|
|
|
import random
|
|
|
|
from pandac.PandaModules import *
|
|
|
|
from direct.interval.IntervalGlobal import Sequence, Func, Parallel, Wait, LerpHprInterval, LerpScaleInterval, LerpFunctionInterval
|
|
|
|
from otp.otpbase import OTPGlobals
|
|
|
|
from toontown.toonbase import ToontownGlobals
|
|
|
|
from CogdoGameGatherable import CogdoGameGatherable, CogdoMemo
|
|
|
|
import CogdoFlyingGameGlobals as Globals
|
|
|
|
import CogdoUtil
|
|
|
|
from direct.particles import ParticleEffect
|
|
|
|
from direct.particles import Particles
|
|
|
|
from direct.particles import ForceGroup
|
|
|
|
|
|
|
|
class CogdoFlyingGatherableFactory:
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self._serialNum = -1
|
|
|
|
self._memoModel = CogdoUtil.loadModel('memo', 'shared').find('**/memo')
|
|
|
|
self._propellerModel = CogdoUtil.loadFlyingModel('propellers').find('**/mesh')
|
|
|
|
self._powerUpModels = {}
|
|
|
|
for type, modelName in Globals.Level.PowerupType2Model.items():
|
|
|
|
model = CogdoUtil.loadFlyingModel(modelName).find('**/' + Globals.Level.PowerupType2Node[type])
|
|
|
|
self._powerUpModels[type] = model
|
|
|
|
model.setTransparency(True)
|
|
|
|
model.setScale(0.5)
|
|
|
|
|
|
|
|
def createMemo(self):
|
|
|
|
self._serialNum += 1
|
|
|
|
return CogdoFlyingMemo(self._serialNum, self._memoModel)
|
|
|
|
|
|
|
|
def createPropeller(self):
|
|
|
|
self._serialNum += 1
|
|
|
|
return CogdoFlyingPropeller(self._serialNum, self._propellerModel)
|
|
|
|
|
|
|
|
def createPowerup(self, type):
|
|
|
|
self._serialNum += 1
|
|
|
|
return CogdoFlyingPowerup(self._serialNum, type, self._powerUpModels[type])
|
|
|
|
|
|
|
|
def createSparkles(self, color1, color2, amp):
|
|
|
|
self.f = ParticleEffect.ParticleEffect('particleEffect_sparkles')
|
|
|
|
p0 = Particles.Particles('particles-1')
|
|
|
|
p0.setFactory('PointParticleFactory')
|
|
|
|
p0.setRenderer('SparkleParticleRenderer')
|
|
|
|
p0.setEmitter('RingEmitter')
|
|
|
|
p0.setPoolSize(15)
|
|
|
|
p0.setBirthRate(0.1)
|
|
|
|
p0.setLitterSize(100)
|
|
|
|
p0.setLitterSpread(0)
|
|
|
|
p0.factory.setLifespanBase(0.6)
|
|
|
|
p0.factory.setLifespanSpread(0.1)
|
|
|
|
p0.factory.setMassBase(1.0)
|
|
|
|
p0.factory.setMassSpread(0.0)
|
|
|
|
p0.factory.setTerminalVelocityBase(3.0)
|
|
|
|
p0.factory.setTerminalVelocitySpread(1.0)
|
|
|
|
p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT)
|
|
|
|
p0.renderer.setUserAlpha(1.0)
|
|
|
|
p0.renderer.setCenterColor(color1)
|
|
|
|
p0.renderer.setEdgeColor(color2)
|
|
|
|
p0.renderer.setBirthRadius(0.3)
|
|
|
|
p0.renderer.setDeathRadius(0.3)
|
|
|
|
p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE)
|
|
|
|
p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE)
|
|
|
|
p0.emitter.setAmplitude(0)
|
|
|
|
p0.emitter.setAmplitudeSpread(0)
|
|
|
|
f0 = ForceGroup.ForceGroup('Gravity')
|
|
|
|
force0 = LinearVectorForce(Vec3(0.0, 0.0, -10.0), 1.0, 0)
|
|
|
|
force0.setVectorMasks(1, 1, 1)
|
|
|
|
force0.setActive(1)
|
|
|
|
f0.addForce(force0)
|
|
|
|
self.f.addForceGroup(f0)
|
|
|
|
p0.emitter.setRadius(2.0)
|
|
|
|
self.f.addParticles(p0)
|
|
|
|
self.f.setPos(0, 0, 0)
|
|
|
|
self.f.setHpr(0, random.random() * 180, random.random() * 180)
|
|
|
|
return self.f
|
|
|
|
|
|
|
|
def destroy(self):
|
|
|
|
self._memoModel.removeNode()
|
|
|
|
del self._memoModel
|
|
|
|
self._propellerModel.removeNode()
|
|
|
|
del self._propellerModel
|
|
|
|
for model in self._powerUpModels.values():
|
|
|
|
model.removeNode()
|
|
|
|
|
|
|
|
del self._powerUpModels
|
|
|
|
if Globals.Level.AddSparkleToPowerups:
|
|
|
|
self.f.cleanup()
|
|
|
|
del self.f
|
|
|
|
|
|
|
|
|
|
|
|
class CogdoFlyingGatherableBase:
|
|
|
|
|
|
|
|
def __init__(self, type):
|
|
|
|
self.type = type
|
|
|
|
self.initFlash()
|
|
|
|
|
|
|
|
def initFlash(self):
|
|
|
|
model = CogdoUtil.loadFlyingModel('gatherableFlash_card')
|
|
|
|
texName = Globals.Level.GatherableType2TextureName[self.type]
|
|
|
|
tex = model.findTexture(texName)
|
|
|
|
tex.setWrapU(Texture.WMRepeat)
|
|
|
|
tex.setWrapV(Texture.WMRepeat)
|
|
|
|
del model
|
|
|
|
self.ts = TextureStage('ts')
|
|
|
|
self.ts.setMode(TextureStage.MCombine)
|
|
|
|
self.ts.setSort(1)
|
|
|
|
self.ts.setCombineRgb(TextureStage.CMInterpolate, TextureStage.CSPrevious, TextureStage.COSrcColor, TextureStage.CSTexture, TextureStage.COSrcColor, TextureStage.CSConstant, TextureStage.COSrcColor)
|
|
|
|
self.ts.setCombineAlpha(TextureStage.CMInterpolate, TextureStage.CSPrevious, TextureStage.COSrcAlpha, TextureStage.CSTexture, TextureStage.COSrcAlpha, TextureStage.CSConstant, TextureStage.COSrcAlpha)
|
|
|
|
self._model.setTexture(self.ts, tex)
|
|
|
|
dur = Globals.Gameplay.GatherableFlashTime
|
|
|
|
self.flashLoop = Sequence(LerpFunctionInterval(self.setTextureAlphaFunc, fromData=1.0, toData=0.25, duration=dur / 2.0, blendType='easeInOut'), LerpFunctionInterval(self.setTextureAlphaFunc, fromData=0.25, toData=1.0, duration=dur / 2.0, blendType='easeInOut'), Wait(1.0), name='%s.flashLoop-%s' % (self.__class__.__name__, self.serialNum))
|
|
|
|
|
|
|
|
def show(self):
|
|
|
|
self.enableFlash()
|
|
|
|
|
|
|
|
def hide(self):
|
|
|
|
self.disableFlash()
|
|
|
|
|
|
|
|
def enable(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def disable(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def enableFlash(self):
|
|
|
|
self.flashLoop.loop()
|
|
|
|
|
|
|
|
def disableFlash(self):
|
|
|
|
self.flashLoop.clearToInitial()
|
|
|
|
|
|
|
|
def destroy(self):
|
|
|
|
self.disableFlash()
|
|
|
|
del self.flashLoop
|
|
|
|
del self.ts
|
|
|
|
|
|
|
|
def setTextureAlphaFunc(self, value):
|
|
|
|
self.ts.setColor(Vec4(value, value, value, value))
|
|
|
|
|
|
|
|
def isPowerUp(self):
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
class CogdoFlyingGatherable(CogdoGameGatherable, CogdoFlyingGatherableBase):
|
|
|
|
|
|
|
|
def __init__(self, type, serialNum, modelToInstance, triggerRadius, animate = True):
|
|
|
|
CogdoGameGatherable.__init__(self, serialNum, modelToInstance, triggerRadius, animate=animate)
|
|
|
|
CogdoFlyingGatherableBase.__init__(self, type)
|
|
|
|
|
|
|
|
def enable(self):
|
|
|
|
CogdoGameGatherable.enable(self)
|
|
|
|
CogdoFlyingGatherableBase.enable(self)
|
|
|
|
|
|
|
|
def disable(self):
|
|
|
|
CogdoGameGatherable.disable(self)
|
|
|
|
CogdoFlyingGatherableBase.disable(self)
|
|
|
|
|
|
|
|
def show(self):
|
|
|
|
CogdoGameGatherable.show(self)
|
|
|
|
CogdoFlyingGatherableBase.show(self)
|
|
|
|
|
|
|
|
def hide(self):
|
|
|
|
CogdoGameGatherable.hide(self)
|
|
|
|
CogdoFlyingGatherableBase.hide(self)
|
|
|
|
|
|
|
|
def destroy(self):
|
|
|
|
CogdoGameGatherable.destroy(self)
|
|
|
|
CogdoFlyingGatherableBase.destroy(self)
|
|
|
|
|
|
|
|
|
|
|
|
class CogdoFlyingMemo(CogdoFlyingGatherableBase, CogdoMemo):
|
|
|
|
|
|
|
|
def __init__(self, serialNum, model):
|
|
|
|
CogdoMemo.__init__(self, serialNum, triggerRadius=Globals.Gameplay.MemoCollisionRadius, spinRate=Globals.Gameplay.MemoSpinRate, model=model)
|
|
|
|
CogdoFlyingGatherableBase.__init__(self, Globals.Level.GatherableTypes.Memo)
|
|
|
|
self.floatTimer = 0.0
|
|
|
|
self.floatSpeed = 1.0
|
|
|
|
self.floatDuration = 2.0
|
|
|
|
|
|
|
|
def _handleEnterCollision(self, collEntry):
|
|
|
|
CogdoGameGatherable._handleEnterCollision(self, collEntry)
|
|
|
|
|
|
|
|
def enable(self):
|
|
|
|
CogdoFlyingGatherableBase.enable(self)
|
|
|
|
CogdoMemo.enable(self)
|
|
|
|
|
|
|
|
def disable(self):
|
|
|
|
CogdoFlyingGatherableBase.disable(self)
|
|
|
|
CogdoMemo.disable(self)
|
|
|
|
|
|
|
|
def show(self):
|
|
|
|
CogdoFlyingGatherableBase.show(self)
|
|
|
|
CogdoMemo.show(self)
|
|
|
|
|
|
|
|
def hide(self):
|
|
|
|
CogdoFlyingGatherableBase.hide(self)
|
|
|
|
CogdoMemo.hide(self)
|
|
|
|
|
|
|
|
def destroy(self):
|
|
|
|
CogdoFlyingGatherableBase.destroy(self)
|
|
|
|
CogdoMemo.destroy(self)
|
|
|
|
|
|
|
|
def update(self, dt):
|
|
|
|
self.floatTimer += dt
|
|
|
|
if self.floatTimer < self.floatDuration:
|
|
|
|
self.setPos(self.getPos() + Vec3(0, 0, dt * self.floatSpeed))
|
|
|
|
elif self.floatTimer < self.floatDuration * 2.0:
|
|
|
|
self.setPos(self.getPos() - Vec3(0, 0, dt * self.floatSpeed))
|
|
|
|
else:
|
|
|
|
self.floatTimer = 0.0
|
|
|
|
self.floatSpeed = random.uniform(0.5, 1.0)
|
|
|
|
self.floatDuration = random.uniform(1.9, 2.1)
|
|
|
|
|
|
|
|
|
|
|
|
class CogdoFlyingPowerup(CogdoFlyingGatherable):
|
|
|
|
|
|
|
|
def __init__(self, serialNum, powerupType, model):
|
|
|
|
self._pickedUpList = []
|
|
|
|
self._isToonLocal = False
|
|
|
|
CogdoFlyingGatherable.__init__(self, powerupType, serialNum, model, Globals.Gameplay.MemoCollisionRadius)
|
|
|
|
self.initInterval()
|
|
|
|
|
|
|
|
def initInterval(self):
|
|
|
|
bouncePercent = 1.2
|
|
|
|
scale = self._model.getScale()
|
|
|
|
shrinkPowerupLerp = LerpScaleInterval(self._model, 0.5, 0.0, startScale=0.0, blendType='easeInOut')
|
|
|
|
growPowerupLerp = LerpScaleInterval(self._model, 0.5, scale * bouncePercent, startScale=0.0, blendType='easeInOut')
|
|
|
|
bouncePowerupLerp = LerpScaleInterval(self._model, 0.25, scale, startScale=scale * bouncePercent, blendType='easeInOut')
|
|
|
|
self.pickUpSeq = Sequence(Func(self.updateLerpStartScale, shrinkPowerupLerp, self._model), shrinkPowerupLerp, Func(self.ghostPowerup), growPowerupLerp, bouncePowerupLerp, name='%s.pickUpSeq-%s' % (self.__class__.__name__, self.serialNum))
|
|
|
|
|
|
|
|
def isPowerUp(self):
|
|
|
|
return True
|
|
|
|
|
|
|
|
def updateLerpStartScale(self, lerp, nodepath):
|
|
|
|
lerp.setStartScale(nodepath.getScale())
|
|
|
|
|
|
|
|
def wasPickedUpByToon(self, toon):
|
|
|
|
if toon.doId in self._pickedUpList:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def ghostPowerup(self):
|
|
|
|
if self._isToonLocal:
|
|
|
|
self._model.setAlphaScale(0.5)
|
|
|
|
if Globals.Level.AddSparkleToPowerups:
|
|
|
|
self.f = self.find('**/particleEffect_sparkles')
|
2015-03-07 02:46:05 +00:00
|
|
|
if not self.f.isEmpty():
|
|
|
|
self.f.hide()
|
2015-03-03 22:10:12 +00:00
|
|
|
|
|
|
|
def pickUp(self, toon, elapsedSeconds = 0.0):
|
|
|
|
if self.wasPickedUpByToon(toon) == True:
|
|
|
|
return
|
|
|
|
self._pickedUpList.append(toon.doId)
|
|
|
|
self._isToonLocal = toon.isLocal()
|
|
|
|
if self._animate:
|
|
|
|
self.pickUpSeq.clearToInitial()
|
|
|
|
self.pickUpSeq.start()
|
|
|
|
else:
|
|
|
|
self.ghostPowerup()
|
|
|
|
|
|
|
|
def destroy(self):
|
|
|
|
del self._pickedUpList[:]
|
|
|
|
self.pickUpSeq.clearToInitial()
|
|
|
|
del self.pickUpSeq
|
|
|
|
CogdoFlyingGatherable.destroy(self)
|
|
|
|
|
|
|
|
def update(self, dt):
|
|
|
|
self._model.setH(self._model.getH() + Globals.Gameplay.MemoSpinRate * dt)
|
|
|
|
|
|
|
|
|
|
|
|
class CogdoFlyingPropeller(CogdoFlyingGatherable):
|
|
|
|
|
|
|
|
def __init__(self, serialNum, model):
|
|
|
|
CogdoFlyingGatherable.__init__(self, Globals.Level.GatherableTypes.Propeller, serialNum, model, Globals.Gameplay.PropellerCollisionRadius, animate=False)
|
|
|
|
self.activePropellers = []
|
|
|
|
self.usedPropellers = []
|
|
|
|
propellers = self._model.findAllMatches('**/propeller*')
|
|
|
|
for prop in propellers:
|
|
|
|
self.activePropellers.append(prop)
|
|
|
|
|
|
|
|
self.initIntervals()
|
|
|
|
|
|
|
|
def initIntervals(self):
|
|
|
|
self.animatedPropellerIval = Parallel(name='%s.object-%i-animatePropellerIval' % (self.__class__.__name__, self.serialNum))
|
|
|
|
for propeller in self.activePropellers:
|
|
|
|
self.animatedPropellerIval.append(LerpHprInterval(propeller, duration=Globals.Level.PropellerSpinDuration, startHpr=Vec3(0.0, 0.0, 0.0), hpr=Vec3(360.0, 0.0, 0.0)))
|
|
|
|
|
|
|
|
def show(self):
|
|
|
|
self.animatedPropellerIval.loop()
|
|
|
|
CogdoFlyingGatherable.show(self)
|
|
|
|
|
|
|
|
def hide(self):
|
|
|
|
self.animatedPropellerIval.clearToInitial()
|
|
|
|
CogdoFlyingGatherable.hide(self)
|
|
|
|
|
|
|
|
def destroy(self):
|
|
|
|
taskMgr.removeTasksMatching('propeller-respawn-*')
|
|
|
|
self.animatedPropellerIval.clearToInitial()
|
|
|
|
del self.animatedPropellerIval
|
|
|
|
CogdoFlyingGatherable.destroy(self)
|
|
|
|
|
|
|
|
def pickUp(self, toon, elapsedSeconds = 0.0):
|
|
|
|
prop = self.removePropeller()
|
|
|
|
if prop != None:
|
|
|
|
respawnTime = Globals.Gameplay.PropellerRespawnTime
|
|
|
|
if elapsedSeconds < respawnTime:
|
|
|
|
taskMgr.doMethodLater(respawnTime - elapsedSeconds, self.addPropeller, 'propeller-respawn-%i' % self.serialNum, extraArgs=[prop])
|
|
|
|
else:
|
|
|
|
self.addPropeller(prop)
|
|
|
|
else:
|
|
|
|
self.disable()
|
|
|
|
return
|
|
|
|
|
|
|
|
def addPropeller(self, prop):
|
|
|
|
if len(self.usedPropellers) > 0:
|
|
|
|
if len(self.activePropellers) == 0:
|
|
|
|
self.enable()
|
|
|
|
self.usedPropellers.remove(prop)
|
|
|
|
prop.show()
|
|
|
|
self.activePropellers.append(prop)
|
|
|
|
self._wasPickedUp = False
|
|
|
|
|
|
|
|
def removePropeller(self):
|
|
|
|
if len(self.activePropellers) > 0:
|
|
|
|
prop = self.activePropellers.pop()
|
|
|
|
prop.hide()
|
|
|
|
self.usedPropellers.append(prop)
|
|
|
|
if len(self.activePropellers) == 0:
|
|
|
|
self._wasPickedUp = True
|
|
|
|
return prop
|
|
|
|
return None
|
|
|
|
|
|
|
|
def isPropeller(self):
|
|
|
|
if len(self.activePropellers) > 0:
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
class CogdoFlyingLevelFog:
|
|
|
|
|
|
|
|
def __init__(self, level, color = Globals.Level.FogColor):
|
|
|
|
self._level = level
|
|
|
|
self.color = color
|
2015-03-07 02:46:05 +00:00
|
|
|
self.defaultFar = None
|
2015-03-03 22:10:12 +00:00
|
|
|
fogDistance = self._level.quadLengthUnits * max(1, self._level.quadVisibiltyAhead * 0.2)
|
|
|
|
self.fog = Fog('RenderFog')
|
|
|
|
self.fog.setColor(self.color)
|
|
|
|
self.fog.setLinearRange(fogDistance * Globals.Level.RenderFogStartFactor, fogDistance)
|
|
|
|
self._visible = False
|
|
|
|
self._clearColor = Vec4(base.win.getClearColor())
|
|
|
|
self._clearColor.setW(1.0)
|
2015-03-07 02:46:05 +00:00
|
|
|
self.defaultFar = base.camLens.getFar()
|
|
|
|
base.camLens.setFar(Globals.Camera.GameCameraFar)
|
|
|
|
base.setBackgroundColor(self.color)
|
2015-03-03 22:10:12 +00:00
|
|
|
|
|
|
|
def destroy(self):
|
|
|
|
self.setVisible(False)
|
|
|
|
if hasattr(self, 'fog'):
|
|
|
|
del self.fog
|
2015-03-07 02:46:05 +00:00
|
|
|
if self.defaultFar is not None:
|
|
|
|
base.camLens.setFar(self.defaultFar)
|
2015-03-03 22:10:12 +00:00
|
|
|
|
|
|
|
def isVisible(self):
|
|
|
|
return self._visible
|
|
|
|
|
|
|
|
def setVisible(self, visible):
|
|
|
|
self._visible = visible
|
|
|
|
if self._visible:
|
|
|
|
base.win.setClearColor(self.color)
|
|
|
|
render.setFog(self.fog)
|
|
|
|
else:
|
|
|
|
base.win.setClearColor(self._clearColor)
|
|
|
|
render.clearFog()
|
|
|
|
|
|
|
|
|
|
|
|
class CogdoFlyingPlatform:
|
|
|
|
CeilingCollName = 'col_ceiling'
|
|
|
|
FloorCollName = 'col_floor'
|
|
|
|
|
|
|
|
def __init__(self, model, type = Globals.Level.PlatformTypes.Platform, parent = None):
|
|
|
|
self._model = model
|
|
|
|
self._type = type
|
|
|
|
if parent is not None:
|
|
|
|
self._model.reparentTo(parent)
|
|
|
|
self._initCollisions()
|
|
|
|
return
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return '<%s model=%s, type=%s>' % (self.__class__.__name__, self._model, self._type)
|
|
|
|
|
|
|
|
def destroy(self):
|
|
|
|
self._floorColl.clearPythonTag('platform')
|
|
|
|
self._model.removeNode()
|
|
|
|
del self._model
|
|
|
|
del self._type
|
|
|
|
del self._floorColl
|
|
|
|
del self._ceilingColl
|
|
|
|
|
|
|
|
def onstage(self):
|
|
|
|
self._model.unstash()
|
|
|
|
|
|
|
|
def offstage(self):
|
|
|
|
self._model.stash()
|
|
|
|
|
|
|
|
def _initCollisions(self):
|
|
|
|
self._floorColl = self._model.find('**/*%s' % CogdoFlyingPlatform.FloorCollName)
|
|
|
|
self._floorColl.setName(CogdoFlyingPlatform.FloorCollName)
|
|
|
|
self._floorColl.node().setIntoCollideMask(ToontownGlobals.FloorEventBitmask | OTPGlobals.FloorBitmask)
|
|
|
|
self._floorColl.setPythonTag('platform', self)
|
|
|
|
self._ceilingColl = self._model.find('**/*%s' % CogdoFlyingPlatform.CeilingCollName)
|
|
|
|
self._ceilingColl.setName(CogdoFlyingPlatform.CeilingCollName)
|
|
|
|
self._ceilingColl.node().setIntoCollideMask(ToontownGlobals.CeilingBitmask)
|
|
|
|
|
|
|
|
def getType(self):
|
|
|
|
return self._type
|
|
|
|
|
|
|
|
def getName(self):
|
|
|
|
return self._model.getName()
|
|
|
|
|
|
|
|
def getModel(self):
|
|
|
|
return self._model
|
|
|
|
|
|
|
|
def isStartPlatform(self):
|
|
|
|
return self._type == Globals.Level.PlatformTypes.StartPlatform
|
|
|
|
|
|
|
|
def isEndPlatform(self):
|
|
|
|
return self._type == Globals.Level.PlatformTypes.EndPlatform
|
|
|
|
|
|
|
|
def isStartOrEndPlatform(self):
|
|
|
|
return self.isStartPlatform() or self.isEndPlatform()
|
|
|
|
|
|
|
|
def getSpawnPosForPlayer(self, playerNum, parent):
|
|
|
|
offset = Globals.Level.PlatformType2SpawnOffset[self._type]
|
|
|
|
spawnLoc = self._model.find('**/spawn_loc')
|
|
|
|
x = (playerNum - 2.0) % 2 * offset
|
|
|
|
y = (playerNum - 1.0) % 2 * offset
|
|
|
|
if not spawnLoc.isEmpty():
|
|
|
|
spawnPos = spawnLoc.getPos(parent) + Vec3(x, y, 0.0)
|
|
|
|
else:
|
|
|
|
spawnPos = self._floorColl.getPos(parent) + Vec3(x, y, 0.0)
|
|
|
|
return spawnPos
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def getFromNode(node):
|
|
|
|
return node.getPythonTag('platform')
|