Help with extending MAP
Monkey Forums/Monkey Beginners/Help with extending MAP
| ||
| I have been trying to create a FSM Manager that will allow me to create FSM's for any objects and add/ execute states on the FSM's. The Idea is any class I need a FSM on I implement IFsmObject ( all code is below) I can then register states against this object: FSM.GetInstance().RegisterState(Self, "normal", New NormalState()) Basic FsmState Class NormalState Extends FsmState Method ExecuteState:String(obj:IFsmObject) Return "" End End This seems to work OK if the IFsmObjects are the same type but as soon as I add a new type of object that implements IFsmObject with Register it looks like it somehow uses that same object type. Could be my Extension of Map or something else ! any ideas ? #REM summary: A FsmMap is a convenience class for mapping IFsmObject objects to StringMap<FsmState>. #END Class FsmMap<V> Extends Map<IFsmObject, V> Method Compare:Int(lhs:IFsmObject, rhs:IFsmObject) If lhs = rhs Return 0 Return 1 End End '------------------------------------------------------------------------------------ #REM summary: interface that defines required properties for a Finite State Machine. #END '------------------------------------------------------------------------------------ Interface IFsmObject Method CurrentState:FsmState() Property Method CurrentState:Void(state:FsmState) Property Method PreviousState:FsmState() Property Method PreviousState:Void(state:FsmState) Property End #REM summary:Abstract class that defines the basic State structure for Finite State Machines. Classes that Extend FsmState must implement ExscuteState #END Class FsmState Abstract Private Field stateName:String Public Method StateName:String() Property Return Self.stateName End Method StateName:Void(stateName:String) Property Self.stateName = stateName End Method EnterState:Void(obj:IFsmObject) End Method ExitState:Void(obj:IFsmObject) End Method ExecuteState:String(obj:IFsmObject) Abstract End
Strict
#REFLECTION_FILTER+="fsm"
Import reflection
'------------------------------------------------------------------------------------
#Rem
summary:The main singleton Finite State Machine manager Class.
The purpose of this Class is To create and manage all FSMState states for all IFSMObject Finite State Machines.
Individual states are instantiated and hashed on a per-FSM basis. The FSM class will also handle transitions
and call all the neccesary transition callbacks on based on the specifications of individual states.
#End
Class FSM
Private
Global instance:FSM = Null
Field stateHash:FsmMap<StringMap<FsmState>> = New FsmMap<StringMap<FsmState>>
Method New()
End
Public
#Rem
summary: The FSM singleton object.
Use GetInstance() to access all FSM functionality.
#End
Function GetInstance:FSM()
If instance = Null Then
instance = New FSM()
EndIf
Return instance
End
#Rem
summary:Executes the specified Finite State Machine.
Specifically, this executes the CurrentState of the specified IFSMObject and performs any neccesary
transitions via the SetState method.
#End
Method Execute:Void(obj:IFsmObject)
If obj.CurrentState <> Null
Local FsmTable:StringMap<FsmState> = StringMap<FsmState>(Self.stateHash.Get(obj))
If FsmTable = Null Then
Return
EndIf
Local targetState:FsmState = GetState(obj, obj.CurrentState().ExecuteState(obj))
SetState(obj, targetState)
EndIf
End
#Rem
summary:Attempt To set the CurrentState of the specified IFSMObject To the state specified and call any appropriate state transitions.
State is specified by name.
#End
Method SetState:Void(obj:IFsmObject, stateName:String)
If stateName = "" Then Return
SetState(obj, GetState(obj, stateName))
End
#Rem
summary:Attempt To set the CurrentState of the specified IFSMObject To the state specified and call any appropriate state transitions.
The actual instance of the desired state is passed. This should normally only be used internally by the FSM class. The preferred
method is SetState(IFSMObject obj, string stateName).
#End
Method SetState:Void(obj:IFsmObject, state:FsmState)
Local FsmTable:StringMap<FsmState> = StringMap<FsmState>(Self.stateHash.Get(obj))
If FsmTable = Null Then Return
If obj <> Null And state <> Null And FsmTable.Contains(state.StateName())
If obj.CurrentState() <> Null Then obj.CurrentState().ExitState(obj)
obj.PreviousState(obj.CurrentState())
obj.CurrentState(state)
obj.CurrentState().EnterState(obj)
EndIf
End
#REM
summary: Register a state To be accessible To the specified Finite State Machine.
This Method will create an instance of the FSMState and hash it under the specified name in a table created specifically
for the specified IFSMObject's class type. The state can later be retrieved by name using GetState.
#END
Method RegisterState:Void(obj:IFsmObject, stateName:String, fsmObject:FsmState)
Print("Adding State : " + stateName + " to " + GetClass(obj).Name)
If Not Self.stateHash.Contains(obj)
Self.stateHash.Add(obj, New StringMap<FsmState>)
EndIf
Local FsmTable:StringMap<FsmState> = StringMap<FsmState>(Self.stateHash.Get(obj))
If FsmTable = Null
' Throw an exception here !!!!
EndIf
If FsmTable.Contains(stateName)
If FsmTable.Get(stateName) = fsmObject Then Return
FsmTable.Remove(stateName)
EndIf
fsmObject.StateName(stateName)
FsmTable.Add(stateName, fsmObject)
End
#REM
summary:Get the instance of the specified state that's registered for the specified IFSMObject.
#END
Method GetState:FsmState(obj:IFsmObject, stateName:String)
Local FsmTable:StringMap<FsmState> = StringMap<FsmState>(Self.stateHash.Get(obj))
If FsmTable = Null Or stateName = ""
Return Null
EndIf
Return FsmState(FsmTable.Get(stateName))
End
End
|
| ||
| Your compare method doesn't meet the documented requirements: "The compare method must return a negative value if lhs < rhs, a positive value if lhs > rhs, or 0 if lhs = rhs. " |
| ||
| Hi muddy_shoes thanks for the reply but how do I compare < or > when its an object of type IFsmObject ? |
| ||
| You need to add your own ordering value/calculation of some kind. This recent thread discussed the same issue: http://www.monkey-x.com/Community/posts.php?topic=8043 |
| ||
| Thanks again ill check that out now. Also when using reflection to see the class name when registering states in RegisterState they all return the same class name monkey.lang.Object. I was expecting the type to be returned. Any ideas why ?
Print("Adding State : " + stateName + " to " + GetClass(obj).Name)
Adding State : normal to monkey.lang.Object Adding State : alert to monkey.lang.Object Adding State : dead to monkey.lang.Object Adding State : normal to monkey.lang.Object Adding State : alert to monkey.lang.Object Adding State : dead to monkey.lang.Object Adding State : refuel to monkey.lang.Object Adding State : attack to monkey.lang.Object Adding State : gotobase to monkey.lang.Object Adding State : find to monkey.lang.Object Adding State : dead to monkey.lang.Object |
| ||
| "Any ideas why?" I haven't used reflection much but I'd guess that you aren't including the appropriate classes under your reflection filter. Maybe check that the classes you're interested in are included with GetClasses? |
| ||
| Ill Check the reflection filters, first time i have attempted to use reflection in monkey. Anyway more to the point I added an fsmid:Int and used that in the comparer, works great now. thanks very much for your time :) |