[Release] BonusAdder / LoopTimer (in eigenem Thread)

02/06/2016 16:26 rollback#1
constinfo.py:
Code:
BONUS_ADD_VNUMS = {
	# vnum: bonusRange
	71085: [0, 3]
}
MAX_BONUS_COUNT = 0

for vnum, bonusRange in BONUS_ADD_VNUMS.iteritems():
	if (bonusRange[1] + 1) > MAX_BONUS_COUNT:
		MAX_BONUS_COUNT = bonusRange[1] + 1
bonusadder.py:
Code:
import constInfo
import player
import item
import gc
from threading import Timer


class LoopTimer:
	__timer = None
	
	def __init__(self, interval, runnable, *args):
		self.__timer = LoopTimer.__LoopTimer(interval, runnable, *args)
	
	def __del__(self):
		LoopTimer.__LoopTimer.__del__(self.__timer)
	
	def Start(self):
		if not self.IsRunning():
			self.__timer.Start()
	
	def Stop(self):
		if self.IsRunning():
			self.__timer.Stop()
	
	def IsRunning(self):
		return self.__timer.IsRunning()
	
	
	class __LoopTimer:
		__interval = None
		__runnable = None
		__args = None
		
		__timer = None
		__isAlive = None
		
		def __init__(self, interval, runnable, *args):
			self.__interval = interval
			self.__runnable = runnable
			self.__args = args
			
			self.Stop()
		
		def __del__(self):
			self.Stop()
		
		def __Run(self):
			if self.__isAlive:
				self.__runnable(*self.__args)
				self.__StartNewTimer()
		
		def __StartNewTimer(self):
			self.__ClearTimer()
			
			self.__timer = Timer(self.__interval, self.__Run)
			self.__timer.setDaemon(True)
			self.__timer.start()
		
		def __ClearTimer(self):
			if self.__timer:
				self.__timer.cancel()
				self.__timer = None
				gc.collect()
		
		def Start(self):
			if not self.IsRunning():
				self.__isAlive = True
				self.__StartNewTimer()
		
		def Stop(self):
			if self.IsRunning():
				self.__isAlive = False
			
			self.__ClearTimer()
		
		def IsRunning(self):
			return self.__isAlive


class BonusAdder:
	__destSlot = None
	__destItemVnum = None
	__wantedCount = None
	__addStopReason = None
	
	__loopTimer = None
	
	#events
	__onAddingChangeEvents = None
	__onAddStopReasonChangeEvents = None
	__onWantedCountChangeEvents = None
	
	
	def __init__(self, destSlot, wantedCount):
		#eventlists zuerst um nullpointer bei settern zu vermeiden
		self.__onAddingChangeEvents = []
		self.__onAddStopReasonChangeEvents = []
		self.__onWantedCountChangeEvents = []
		
		self.__destSlot = destSlot
		self.__destItemVnum = player.GetItemIndex(self.__destSlot)
		self.__SetWantedCount(wantedCount)
		self.__SetAddStopReason(None)
		
		self.__loopTimer = LoopTimer(0.05, lambda: self.__CheckAndAddAttribute())
	
	def __del__(self):
		LoopTimer.__del__(self.__loopTimer)
	
	
	def StartAdding(self):
		self.__SetAdding(True)
		self.__SetAddStopReason(None)
	
	def StopAdding(self, addStopReason = None):
		if not addStopReason:
			addStopReason = BonusAdder.__AddStopReasons.STOPPED_BY_USER
		
		self.__SetAdding(False)
		self.__SetAddStopReason(addStopReason)
	
	def ToggleAdding(self):
		if self.IsAdding():
			self.StopAdding()
		else:
			self.StartAdding()
	
	def IsAdding(self):
		return self.__loopTimer.IsRunning()
	
	def IncreaseWantedCount(self):
		self.__SetWantedCount(self.__wantedCount + 1)
	
	def DecreaseWantedCount(self):
		self.__SetWantedCount(self.__wantedCount - 1)
	
	def __SetWantedCount(self, wantedCount):
		self.__wantedCount = max(1, min(wantedCount, constInfo.MAX_BONUS_COUNT))
		
		for event in self.__onWantedCountChangeEvents:
			event(self.__wantedCount)
	
	def __SetAdding(self, v):
		if v:
			self.__loopTimer.Start()
		else:
			self.__loopTimer.Stop()
		
		adding = self.IsAdding()
		
		for event in self.__onAddingChangeEvents:
			event(adding)
	
	def __SetAddStopReason(self, addStopReason):
		self.__addStopReason = addStopReason
		
		for event in self.__onAddStopReasonChangeEvents:
			event(self.__addStopReason)
	
	def AddOnAddingChangeEvent(self, event):
		if not event:
			return
		
		self.__onAddingChangeEvents.append(event)
	
	def RemoveOnAddingChangeEvent(self, event):
		self.__onAddingChangeEvents.remove(event)
	
	def AddOnAddStopReasonChangeEvent(self, event):
		if not event:
			return
		
		self.__onAddStopReasonChangeEvents.append(event)
	
	def RemoveOnAddStopReasonChangeEvents(self, event):
		self.__onAddStopReasonChangeEvents.remove(event)
	
	def AddOnWantedCountChangeEvent(self, event):
		if not event:
			return
		
		self.__onWantedCountChangeEvents.append(event)
	
	def RemoveOnWantedCountChangeEvent(self, event):
		self.__onWantedCountChangeEvents.remove(event)
	
	def __CheckAndAddAttribute(self):
		if self.__IsBonusCountReached():
			self.StopAdding(BonusAdder.__AddStopReasons.BONUS_COUNT_REACHED)
			return
		
		if not self.__IsRightItemVnum():
			self.StopAdding(BonusAdder.__AddStopReasons.WRONG_ITEM_VNUM)
			return
		
		if self.__GetAddItemCount() < 1:
			self.StopAdding(BonusAdder.__AddStopReasons.NOT_ENOUGH_BONUS_ADDERS)
			return
		
		self.__AddAttribute()
	
	def __AddAttribute(self):
		net.SendItemUseToItemPacket(self.__GetAddItemSlot(), self.__destSlot)
	
	def __IsBonusCountReached(self):
		return self.__GetBonusCount() >= self.__wantedCount
	
	def __IsRightItemVnum(self):
		return player.GetItemIndex(self.__destSlot) == self.__destItemVnum
	
	def __GetAddItemCount(self):
		itemCount = 0
		addItemSlot = self.__GetAddItemSlot()
		
		while addItemSlot:
			itemCount = itemCount + player.GetItemCount(addItemSlot)
			addItemSlot = self.__GetAddItemSlot(addItemSlot)
		
		return itemCount
	
	def __GetAddItemVnum(self):
		currentBonusCount = self.__GetBonusCount()
		
		for vnum, bonusRange in constInfo.BONUS_ADD_VNUMS.iteritems():
			if currentBonusCount >= bonusRange[0] and currentBonusCount <= bonusRange[1]:
				return vnum
		
		return None
	
	def __GetAddItemSlot(self, startSlot = 0):
		addItemVnum = self.__GetAddItemVnum()
		
		for slotNum in xrange(startSlot, player.INVENTORY_PAGE_SIZE * 4):
			if player.GetItemIndex(slotNum) == addItemVnum:
				return slotNum
		
		return None
	
	def __GetBonusCount(self):
		return BonusAdder.GetBonusCount(self.__destSlot)
	
	@staticmethod
	def GetMaxAddingBonusCount(destSlot, adderVnum):
		currentBonusCount = BonusAdder.GetBonusCount(destSlot)
		maxTotalBonusCount = BonusAdder.GetMaxTotalBonusCount(destSlot, adderVnum)
		
		if not maxTotalBonusCount:
			return None
		
		return maxTotalBonusCount - currentBonusCount
	
	@staticmethod
	def GetMaxTotalBonusCount(destSlot, adderVnum):
		currentBonusCount = BonusAdder.GetBonusCount(destSlot)
		
		for vnum, bonusRange in constInfo.BONUS_ADD_VNUMS.iteritems():
			if vnum == adderVnum:
				if currentBonusCount >= bonusRange[0] and currentBonusCount <= bonusRange[1]:
					return bonusRange[1] + 1
		
		return None
	
	@staticmethod
	def GetBonusCount(slot):
		bonusCount = 0
		
		for bonusIndex in xrange(constInfo.MAX_BONUS_COUNT):
			applyType, applyValue = player.GetItemAttribute(slot, bonusIndex)
			
			if applyType != 0:
				bonusCount = bonusCount + 1
		
		return bonusCount
	
	@staticmethod
	def IsBonusAdder(vnum):
		return vnum in constInfo.BONUS_ADD_VNUMS
	
	@staticmethod
	def CanAddBonus(destSlot, adderVnum):
		destVnum = player.GetItemIndex(destSlot)
		item.SelectItem(destVnum)
		itemType = item.GetItemType()
		
		if itemType != item.ITEM_TYPE_WEAPON and itemType != item.ITEM_TYPE_ARMOR:
			return False
		
		return BonusAdder.GetMaxAddingBonusCount(destSlot, adderVnum) >= 1
	
	
	#ADD STOP REASONS
	class __AddStopReasons:
		class AddStopReason:
			__errorCode = None
			__message = None
			
			def __init__(self, errorCode, message):
				self.__errorCode = errorCode
				self.__message = message
			
			def __del__(self):
				self.__errorCode = None
				self.__message = None
			
			
			def GetErrorCode(self):
				return self.__errorCode
			
			def GetMessage(self):
				return self.__message
		
		
		STOPPED_BY_USER = AddStopReason("STOPPED_BY_USER", "Hinzufügen wurde gestoppt.")
		BONUS_COUNT_REACHED = AddStopReason("BONUS_COUNT_REACHED", "Gewünschte Bonianzahl wurde erreicht.")
		WRONG_ITEM_VNUM = AddStopReason("WRONG_ITEM_VNUM", "Das Item auf dem Zielslot hat nicht die richtige Vnum.")
		NOT_ENOUGH_BONUS_ADDERS = AddStopReason("NOT_ENOUGH_BONUS_ADDERS", "Du besitzt nicht genug Items, um weitere Boni hinzuzufügen.")





import uiCommon
import item
import chat

class BonusAdderGUI(uiCommon.QuestionDialog2):
	__bonusAdder = None
	
	def __init__(self, destSlot, draggedVnum):
		uiCommon.QuestionDialog2.__init__(self)
		
		#oldVnum = item.GetItemIndex()
		item.SelectItem(player.GetItemIndex(destSlot))
		itemName = item.GetItemName()
		
		item.SelectItem(draggedVnum)
		itemNameDragged = item.GetItemName()
		#item.SelectItem(oldVnum)
		
		
		self.__bonusAdder = BonusAdder(destSlot, BonusAdder.GetMaxTotalBonusCount(destSlot, draggedVnum))
		self.__bonusAdder.AddOnAddStopReasonChangeEvent(lambda reason: self.__OnAddStopReasonChanged(reason))
		
		self.SetText("Boni hinzufügen")
		self.SetText1("Mit %s kannst du %s bis zu %d weitere Boni hinzufügen." % (itemNameDragged, itemName, BonusAdder.GetMaxAddingBonusCount(destSlot, draggedVnum)))
		self.SetText2("Möchtest du die Boni hinzufügen?")
		
		self.SetAcceptEvent(lambda: self.__OnAcceptClick())
		self.SetCancelEvent(lambda: self.__OnDeclineClick())
	
	def __del__(self):
		BonusAdder.__del__(self.__bonusAdder)
	
	def __OnAcceptClick(self):
		self.__bonusAdder.StartAdding()
	
	def __OnDeclineClick(self):
		self.Close()
	
	def __OnAddStopReasonChanged(self, reason):
		if not reason:
			return
		
		chat.AppendChat(chat.CHAT_TYPE_INFO, reason.GetMessage())
		self.Close()
Aufgerufen wird das ganze das mit dem Slot des Items, auf das hinzugefügt werden soll und der Vnum des Bonushinzufügers:
Code:
import bonusadder
if bonusadder.BonusAdder.IsBonusAdder(draggedVnum) and bonusadder.BonusAdder.CanAddBonus(destSlot, draggedVnum):
	bonusadder.BonusAdderGUI(destSlot, draggedVnum).Open()
Vielleicht könnt ihr das ja gebrauchen, evtl. auch nur den LoopTimer ;)