00001 '''
00002 Anoria (c) Gsk 2010
00003 '''
00004
00005
00006 import BigWorld
00007 import time
00008 import math
00009
00010 from Resources import ResourceDB as RDB
00011 import Stats
00012
00013 from gamesys.Combat import CombatResolver
00014 from gamesys.ProcTimer import ProcTimer
00015
00016 from config.consts import *
00017 from config import state
00018 from config import event
00019
00020
00021 class Actor(Stats.StatsManager):
00022
00023 def __init__(self):
00024 print "Actor_%d initializing cell component..." % (self.id)
00025 Stats.StatsManager.__init__(self, isServer=True)
00026
00027
00028 self.initStatsData()
00029
00030 self.aggroFromList = []
00031 self.aggroOnList = []
00032
00033 self.combatResolver = CombatResolver(self.id, self)
00034 delta_t = 1.0 / GAME_UPDATE_RATE
00035 self.addTimer(delta_t, delta_t, 0)
00036
00037 self.states = [state.S_STANCE_IDLE, state.S_MOVE_NONE, state.S_COMBAT_OFF, state.S_ATTACK_OFF]
00038
00039 self.isPlayer = False
00040 self.targetId = None
00041
00042 self.eventProc = { }
00043 self.timeLastHitReceived = 0
00044
00045 self.addEventProc(event.APPLY_STATS_EFFECT, self.applyEffect)
00046 self.addEventProc(event.AGGRO_DROPPED, self.eventAggroDropped)
00047
00048
00049
00050
00051
00052
00053 def aggroOn(self, entityId):
00054 if not entityId in self.aggroOnList:
00055 self.aggroOnList.append(entityId)
00056 print "Actor_%d has aggroed on entity %d" % (self.id, entityId)
00057 print "aggroOnList: ", self.aggroFromList
00058
00059 def deAggroOn(self, entityId):
00060 ''' deAggroOn removes the given entityId to the aggroOn list, means that dont hold aggro on them anymore
00061 @param: entityId the id of the entity to remove from the aggroOn list '''
00062 if entityId in self.aggroOnList:
00063 index = self.aggroOnList.index(entityId)
00064 del self.aggroOnList[index]
00065 print "Actor_%d removed entity %d from aggroOnList" % (self.id, entityId)
00066 print "aggroFromList: ", self.aggroFromList
00067
00068 def haveAggroOn(self):
00069 ''' test whether we have any aggro from anyone
00070 @return: True of False test result '''
00071 if len(self.aggroOnList) > 0:
00072 return True
00073 return False
00074
00075
00076 def aggroFromEntity(self, entityId):
00077 ''' aggroFromEntity adds the given entityId to the aggro list. This signifies that this entity has aggroed us,
00078 we cannot leave combat mode as long as we have any aggro.
00079 @param: entityId the id of the entity to add to the aggro list '''
00080 if not entityId in self.aggroFromList:
00081 self.aggroFromList.append(entityId)
00082 print "Actor_%d has aquired aggro from entity %d" % (self.id, entityId)
00083 print "aggroFromList: ", self.aggroFromList
00084
00085 def deAggroFrom(self, entityId):
00086 ''' deAggroFrom removes the given entityId from the aggroFrom list, means that entity does not hold aggro on us anymore
00087 @param: entityId the id of the entity to remove from the aggro list '''
00088 if entityId in self.aggroFromList:
00089 index = self.aggroFromList.index(entityId)
00090 del self.aggroFromList[index]
00091 print "Actor_%d removed entity %d from aggroFromList" % (self.id, entityId)
00092 print "aggroFromList: ", self.aggroFromList
00093
00094 def haveAggroFrom(self, entityId):
00095 ''' test whether we have aggro on a given entity
00096 @param: entityId the id of the entity to test against the list
00097 @return: True of False test result '''
00098 if entityId in self.aggroFromList:
00099 return True
00100
00101 def haveAggro(self):
00102 ''' test whether we have any aggro from anyone
00103 @return: True of False test result '''
00104 if len(self.aggroFromList) > 0:
00105 return True
00106 return False
00107
00108
00109
00110
00111
00112
00113 def eventAggroDropped(self, sourceId, eventValue):
00114 self.deAggroFrom(sourceId)
00115
00116 def applyEffect(self, sourceId, effectId):
00117 ''' the sole function of this one is to remove the sourceId from the rpcEvent call '''
00118 self.applyStatsEffect(effectId)
00119
00120 def addEventProc(self, eventId, proc):
00121
00122 self.eventProc[eventId]=proc
00123
00124
00125
00126
00127
00128 def getTargetEntity(self):
00129 entity = None
00130 try:
00131 entity = BigWorld.entities[self.targetId]
00132 except:
00133 return None
00134
00135 return entity
00136
00137
00138
00139
00140
00141
00142
00143
00144 def onTimer(self, timerId, userData):
00145
00146
00147 if userData == 1:
00148 self.destroy()
00149
00150
00151
00152 def onWeaponAttackPerformed(self, hitType, dmg):
00153 ''' callback for the combat resolver
00154 called whenever one of the equipped weapons has performed a single attack'''
00155
00156 self.allClients.rpcMeleeAttack(self.targetId, hitType, dmg)
00157 if hitType in [HIT_TYPE_NORMAL, HIT_TYPE_CRIT, HIT_TYPE_CRUSH]:
00158 self.doDamage(hitType, dmg)
00159
00160
00161 def onDestroy(self):
00162 pass
00163
00164
00165
00166
00167
00168
00169
00170 def facePosition(self, pos):
00171 vector = (pos[0] - self.position[0], 0, pos[2] - self.position[2])
00172 yaw = math.atan2(vector[0], vector[2])
00173
00174 self.direction = (0, 0, yaw)
00175
00176
00177
00178
00179
00180
00181 def doDamage(self, hitType, damage):
00182 target = self.getTargetEntity()
00183 if target != None:
00184 target.rpcReceiveDamage(damage, self.id)
00185
00186 def doHealthRegen(self):
00187 hp = self.characterStats.HP
00188 if hp.curValue < hp.maxValue and not self.states[state.STATE_COMBAT] == state.S_COMBAT_ON:
00189 increment = hp.maxValue * HP_REGEN_FACTOR
00190
00191 hp.curValue += increment
00192 self.updateHealthPercent()
00193
00194 def updateHealthPercent(self):
00195 ''' update our public healthPercent property: this will trigger an update to all clients
00196 and cells holding a ghost of us since healthPercent is flagged ALL_CLIENTS and thus available to everyone
00197 as opposed to the character stats themselves '''
00198 hp = self.characterStats.HP
00199 self.healthPercent = ((float(hp.curValue)/float(hp.maxValue))*100.0)
00200
00201 def enterCombat(self, attackerId=None):
00202 ''' enterCombat is called when an Actor enters into combat mode
00203 @param: attackerId is an optional parameter, if not None it indicates that we are entering into
00204 combat mode because someone else (identified by attackerId) attacked us. In this case we'll
00205 automatically target the attacker '''
00206
00207 print "Actor_%d.enterCombat()" % (self.id)
00208
00209 self.timeLastHitReceived = time.time()
00210 self.states[state.STATE_COMBAT] = state.S_COMBAT_ON
00211 self.combatStartTick = time.time()
00212
00213
00214 if attackerId != None:
00215 self.setTarget(attackerId)
00216
00217
00218 target = self.getTargetEntity()
00219 target.rpcProcessEvent(self.id, event.UNDER_ATTACK, event.ATTACK_EVENT_AGGRO)
00220
00221
00222 self.aggroOn(self.targetId)
00223
00224
00225 self.combatResolver.startSlot(0)
00226
00227
00228 self.states[state.STATE_ATTACK] = state.S_ATTACK_ON
00229
00230
00231
00232 def exitCombat(self):
00233 ''' exit combat will restate an Actor's combat state to off, remove a current target from the aggroOn list,
00234 relay a AGGRO_DROPPED event to the target, cancel the target and finally stop autoattack mode '''
00235
00236 print "Actor_%d.exitCombat()" % (self.id)
00237 self.states[state.STATE_COMBAT] = state.S_COMBAT_OFF
00238 if self.targetId:
00239 self.deAggroOn(self.targetId)
00240 target = self.getTargetEntity()
00241 target.rpcProcessEvent(self.id, event.AGGRO_DROPPED, 0 )
00242
00243 self.targetId = None
00244 if self.states[state.STATE_ATTACK] == state.S_ATTACK_ON:
00245 self.stopAutoattack()
00246
00247
00248 def stopAutoattack(self):
00249 print "Actor_%d.stopAutoattack()" % (self.id)
00250 self.combatResolver.stopSlot(0)
00251 self.states[state.STATE_ATTACK] = state.S_ATTACK_OFF
00252 self.allClients.rpcChangeState(state.STATE_ATTACK, state.S_ATTACK_OFF)
00253
00254 def pauseAutoattack(self, restart=False):
00255 ''' pauseAutoattack does not turn the auto attack state off but it stops the combat resolver from
00256 processing weapon swings. This is used when our target is temporarily out off range '''
00257 print "Actor_%d.pauseAutoattack() %s" % (self.id, restart)
00258 if not restart:
00259 self.combatResolver.stopSlot(0)
00260 self.states[state.STATE_ATTACK] = state.S_ATTACK_PAUSED
00261 else:
00262 self.combatResolver.startSlot(0)
00263 self.states[state.STATE_ATTACK] = state.S_ATTACK_ON
00264
00265 def setTarget(self, entityId):
00266 ''' aquire the entity identified by entityId as new target. This also triggers a combatResolver attacktable rebuild '''
00267 self.targetId = entityId
00268 self.combatResolver.setTargetId(entityId)
00269
00270 def targetLost(self):
00271 ''' a targetLost condition this will usually occur if our target leaves the world (dies or logs off)
00272 we do automaticall deAggroOn the former target (remove it from the aggroOn list) and stop auto attacking '''
00273 print "Actor_%d.targetLost()" % (self.id)
00274 self.deAggroOn(self.targetId)
00275 self.stopAutoattack()
00276 self.targetId = None
00277
00278
00279 def die(self, sourceId):
00280 ''' the purpose of this function should be kind of obvious :) '''
00281 self.states[state.STATE_STANCE] = state.S_STANCE_DEAD
00282 self.states[state.STATE_COMBAT] = state.S_COMBAT_OFF
00283 self.states[state.STATE_ATTACK] = state.S_ATTACK_OFF
00284
00285
00286 for entityId in self.aggroOnList:
00287 try:
00288 entity = BigWorld.entities[entityId]
00289 entity.rpcProcessEvent(self.id, event.AGGRO_DROPPED, 0 )
00290 except:
00291 pass
00292
00293
00294
00295 self.base.rpcDeathLoc(self.position, self.direction, self.modelNumber)
00296
00297
00298 self.addTimer(0.5, 0, 1)
00299
00300
00301
00302
00303
00304
00305
00306 def rpcReceiveDamage(self, damage, sourceId):
00307 self.timeLastHitReceived = time.time()
00308
00309
00310 hp = self.characterStats.HP
00311 newValue = hp.curValue - damage
00312 hp.curValue = newValue
00313 self.updateHealthPercent()
00314 if hp.curValue <= 0:
00315
00316
00317
00318 self.die(sourceId)
00319
00320 def _getRpcFunc(self, funcCode):
00321 try:
00322 func = self.eventProc[funcCode]
00323 return func
00324 except:
00325 return None
00326
00327
00328 def rpcProcessEvent(self, sourceId, eventType, eventValue):
00329 print "Actor_%d.rpcProcessEvent(): sourceId=%d, eventType=%s, eventValue=%d" % \
00330 (self.id, sourceId, event.EVENTS[eventType], eventValue)
00331
00332
00333 func = self._getRpcFunc(eventType)
00334 if func != None: func(sourceId, eventValue)
00335 else: print 'Actor_%d RPC event function for %s not found' % (self.id, event.EVENTS[eventType])
00336
00337
00338 def rpcProcessStringEvent(self, sourceId, eventType, eventString):
00339 print "Actor_%d.rpcProcessStringEvent(): sourceId=%d, eventType=%s, eventString=%s" % \
00340 (self.id, sourceId, event.EVENTS[eventType], eventString)
00341
00342
00343 func = self._getRpcFunc(eventType)
00344 if func != None: func(sourceId, eventString)
00345 else: print 'Actor_%d RPC event function for %s not found' % (self.id, event.EVENTS[eventType])
00346