���������GlorpBlobType subclass: #GlorpBlob2Type instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Towergame'! !GlorpBlob2Type methodsFor: 'types' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! converterForStType: aClass aClass = UUID ifTrue: [ ^ UuidConverter new ]. ^ super converterForStType: aClass! ! Object subclass: #TgAct instanceVariableNames: 'agent tool timestamp' classVariableNames: '' poolDictionaries: '' category: 'Towergame'! !TgAct commentStamp: 'HerbyVojcik 8/5/2017 19:23' prior: 0! I represent a relationship between a player (TgAgent) and a device (TgTool). In particular, I am created whenever a player logs in to the game from different device than it was last time (or first time, ever).! !TgAct methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! agent ^ agent! ! !TgAct methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! agent: anObject agent := anObject! ! !TgAct methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! timestamp: anObject timestamp := anObject! ! !TgAct methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! tool ^ tool! ! !TgAct methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! tool: anObject tool := anObject! ! !TgAct methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! timestamp ^ timestamp! ! !TgAct methodsFor: 'initialization' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! initialize super initialize. agent := nil. timestamp := DateAndTime now asUTC. tool := nil.! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! TgAct class instanceVariableNames: ''! !TgAct class methodsFor: 'instance creation' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! agent: aTgAgent tool: aTgTool ^ self new agent: aTgAgent; tool: aTgTool; yourself! ! Object subclass: #TgAgent instanceVariableNames: 'id' classVariableNames: '' poolDictionaries: '' category: 'Towergame'! !TgAgent commentStamp: 'HerbyVojcik 8/5/2017 19:22' prior: 0! I represent a towergame player. I only contain player-related information; the game state itself is in TgState.! !TgAgent methodsFor: 'initialization' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! initialize super initialize. id := nil.! ! !TgAgent methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! id: anObject id := anObject! ! !TgAgent methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! id ^ id! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! TgAgent class instanceVariableNames: ''! !TgAgent class methodsFor: 'instance creation' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! id: aString ^ self new id: aString; yourself! ! Object subclass: #TgAnswers instanceVariableNames: 'good bad' classVariableNames: '' poolDictionaries: '' category: 'Towergame'! !TgAnswers commentStamp: 'HerbyVojcik 8/5/2017 20:23' prior: 0! I represent the answered question stats. I know how many good / bad answered questions there is.! !TgAnswers methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! good ^ good! ! !TgAnswers methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! bad: anObject bad := anObject! ! !TgAnswers methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! bad ^ bad! ! !TgAnswers methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! good: anObject good := anObject! ! !TgAnswers methodsFor: 'initialization' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! initialize super initialize. bad := 0. good := 0.! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! TgAnswers class instanceVariableNames: ''! !TgAnswers class methodsFor: 'instance creation' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! good: anInteger bad: anInteger2 ^ self new good: anInteger; bad: anInteger2; yourself! ! Object subclass: #TgFloors instanceVariableNames: 'total reinforced' classVariableNames: '' poolDictionaries: '' category: 'Towergame'! !TgFloors commentStamp: 'HerbyVojcik 8/5/2017 20:22' prior: 0! I represent the floor building status. I know how many floors are build and how many of them is reinforced.! !TgFloors methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! total ^ total! ! !TgFloors methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! total: anObject total := anObject! ! !TgFloors methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! reinforced ^ reinforced! ! !TgFloors methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! reinforced: anObject reinforced := anObject! ! !TgFloors methodsFor: 'initialization' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! initialize super initialize. reinforced := 0. total := 0.! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! TgFloors class instanceVariableNames: ''! !TgFloors class methodsFor: 'instance creation' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! total: anInteger ^ self total: anInteger reinforced: 0! ! !TgFloors class methodsFor: 'instance creation' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! total: anInteger reinforced: anInteger2 ^ self new total: anInteger; reinforced: anInteger2; yourself! ! Object subclass: #TgState instanceVariableNames: 'agent version packs valuables score bestScore answers' classVariableNames: '' poolDictionaries: '' category: 'Towergame'! !TgState commentStamp: 'HerbyVojcik 8/5/2017 20:20' prior: 0! I represent the game state. I have relation to a player (TgAgent) and have a version. Then, I contain (directly or indirectly) other parts that make up the player's game state. Whenever I am changed by game progress, my version is changed as well.! !TgState methodsFor: 'initialization' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! initialize super initialize. agent := nil. answers := nil. bestScore := nil. packs := Set new. score := nil. valuables := nil. version := nil.! ! !TgState methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! score: anObject score := anObject! ! !TgState methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! bestScore: anObject bestScore := anObject! ! !TgState methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! agent: anObject agent := anObject! ! !TgState methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! score ^ score! ! !TgState methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! packs ^ packs! ! !TgState methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! version ^ version! ! !TgState methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! bestScore ^ bestScore! ! !TgState methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! agent ^ agent! ! !TgState methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! answers: anObject answers := anObject! ! !TgState methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! valuables: anObject valuables := anObject! ! !TgState methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! valuables ^ valuables! ! !TgState methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! version: anObject version := anObject! ! !TgState methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! answers ^ answers! ! !TgState methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! packs: anObject packs := anObject! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! TgState class instanceVariableNames: ''! !TgState class methodsFor: 'instance creation' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! agent: aTgAgent version: aString ^ self new agent: aTgAgent; version: aString; yourself! ! Object subclass: #TgTool instanceVariableNames: 'id' classVariableNames: '' poolDictionaries: '' category: 'Towergame'! !TgTool commentStamp: 'HerbyVojcik 8/5/2017 19:26' prior: 0! I represent the device (mobile phone, web browser, ..) that player uses to connect to game.! !TgTool methodsFor: 'initialization' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! initialize super initialize. id := nil.! ! !TgTool methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! id: anObject id := anObject! ! !TgTool methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! id ^ id! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! TgTool class instanceVariableNames: ''! !TgTool class methodsFor: 'instance creation' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! id: aString ^ self new id: aString; yourself! ! Object subclass: #TgValuables instanceVariableNames: 'coins gems' classVariableNames: '' poolDictionaries: '' category: 'Towergame'! !TgValuables commentStamp: 'HerbyVojcik 8/5/2017 20:22' prior: 0! I represent a purse. I know how many coins and gems there is.! !TgValuables methodsFor: 'initialization' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! initialize super initialize. coins := 0. gems := 0.! ! !TgValuables methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! gems: anObject gems := anObject! ! !TgValuables methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! coins: anObject coins := anObject! ! !TgValuables methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! gems ^ gems! ! !TgValuables methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! coins ^ coins! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! TgValuables class instanceVariableNames: ''! !TgValuables class methodsFor: 'instance creation' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! coins: anInteger gems: anInteger2 ^ self new coins: anInteger; gems: anInteger2; yourself! ! Object subclass: #Towergame instanceVariableNames: 'dao' classVariableNames: '' poolDictionaries: '' category: 'Towergame'! !Towergame commentStamp: 'HerbyVojcik 5/17/2017 17:19' prior: 0! I am the Towergame app class. I configure and start towergame server processing.! !Towergame methodsFor: 'actions' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! clientSync: anObject self dao inUnitOfWorkDo: [ | agent state | agent := self dao findAgentById: anObject agentId. state := self dao findStateByAgent: agent. ^ NeoJSONObject new agentId: agent id; stateVersion: state version; totalAnsweredQuestions: (NeoJSONObject new good: 0; bad: 0; yourself); yourself ]! ! !Towergame methodsFor: 'initialization' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! initialize super initialize. dao := nil. ! ! !Towergame methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! dao: anObject dao := anObject! ! !Towergame methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! dao ^ dao! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! Towergame class instanceVariableNames: 'default'! !Towergame class methodsFor: 'instance creation' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! dao: aDao ^ self new dao: aDao; yourself! ! !Towergame class methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! defaultDbLogin | databaseFile | databaseFile := Smalltalk imageDirectory asFileReference / 'towergame.db'. ^ Login new database: UDBCSQLite3Platform new; host: ''; port: ''; username: ''; password: ''; databaseName: databaseFile fullPath asZnUrl asString; yourself ! ! !Towergame class methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! default ^ default ifNil: [ default := self dao: (self daoForLogin: self defaultDbLogin) ]! ! !Towergame class methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! defaultPort ^ 4998! ! !Towergame class methodsFor: 'configuration' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! configureServer (self serverFor: self default on: self defaultPort) start; register ! ! !Towergame class methodsFor: 'configuration' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! configureSqlite3 PharoDatabaseAccessor DefaultDriver: GlorpSQLite3Driver! ! !Towergame class methodsFor: 'configuration' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! configure self configureSqlite3. self configureServer.! ! !Towergame class methodsFor: 'factory' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! daoForLogin: aLogin ^ TowergameDao forLogin: aLogin! ! !Towergame class methodsFor: 'factory' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! serverFor: aTowergame on: port ^ (ZnServer on: port) delegate: (TowergameDelegate on: aTowergame); yourself! ! Object subclass: #TowergameDao instanceVariableNames: 'glorpSession glorpLogin' classVariableNames: '' poolDictionaries: '' category: 'Towergame'! !TowergameDao methodsFor: 'transactions' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! inUnitOfWorkDo: aBlock ^ self glorpSession inUnitOfWorkDo: aBlock! ! !TowergameDao methodsFor: 'initialization' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! initialize super initialize. glorpLogin := nil. glorpSession := nil.! ! !TowergameDao methodsFor: 'initialize-release' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! reset glorpSession := nil.! ! !TowergameDao methodsFor: 'query' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! findStateByAgent: anAgent ^ self glorpSession readOneOf: TgState where: [ :one | one agent = anAgent ]! ! !TowergameDao methodsFor: 'query' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! findAgentById: anUUID ^ self glorpSession readOneOf: TgAgent where: [ :one | one id = anUUID ]! ! !TowergameDao methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! glorpLogin: anObject glorpLogin := anObject! ! !TowergameDao methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! glorpLogin ^ glorpLogin! ! !TowergameDao methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! glorpSession glorpSession ifNil: [ glorpSession := TowergameDescriptorSystem sessionForLogin: self glorpLogin ]. glorpSession accessor isLoggedIn ifFalse: [ glorpSession accessor login ]. ^ glorpSession! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! TowergameDao class instanceVariableNames: ''! !TowergameDao class methodsFor: 'instance creation' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! forLogin: aLogin ^ self new glorpLogin: aLogin; yourself! ! ZnDispatcherDelegate subclass: #TowergameDelegate instanceVariableNames: 'towergame' classVariableNames: '' poolDictionaries: '' category: 'Towergame'! !TowergameDelegate methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! towergame ^ towergame! ! !TowergameDelegate methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! towergame: anObject towergame := anObject! ! !TowergameDelegate methodsFor: 'initialization' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! initialize super initialize. towergame := nil. self map: '/api/v1/sync' to: [ :request :response | self syncRequest: request toResponse: response ]! ! !TowergameDelegate methodsFor: 'action' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! syncRequest: request toResponse: response | requestPayload responsePayload uuidKeys | uuidKeys := #(agentId stateVersion deviceId). request method == #POST ifFalse: [ ^ ZnResponse methodNotAllowed: request ]. requestPayload := NeoJSONObject fromString: request contents. requestPayload ifNotNil: [ uuidKeys do: [ :each | requestPayload at: each ifPresentPut: [ :s | UUID fromString: s ] ] ]. responsePayload := self towergame clientSync: requestPayload. responsePayload ifNotNil: [ uuidKeys do: [ :each | responsePayload at: each ifPresentPut: #asString ] ]. ^ response entity: (ZnEntity with: (NeoJSONWriter toString: responsePayload) type: ZnMimeType applicationJson); yourself! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! TowergameDelegate class instanceVariableNames: ''! !TowergameDelegate class methodsFor: 'instance creation' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! on: aTowergame ^ self new towergame: aTowergame; yourself! ! DescriptorSystem subclass: #TowergameDescriptorSystem instanceVariableNames: 'uuidConverter' classVariableNames: '' poolDictionaries: '' category: 'Towergame'! !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! tableForAGENT: aTable (aTable createFieldNamed: 'id' type: platform blob2) bePrimaryKey. ! ! !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! classModelForTgAgent: aClassModel aClassModel newAttributeNamed: #id type: UUID! ! !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbyVojcik 8/14/2017 18:24'! tableForSTATE: aTable (aTable createFieldNamed: 'agent' type: platform blob2) in: [ :agentField | agentField bePrimaryKey. aTable addForeignKeyFrom: agentField to: ((self tableNamed: 'AGENT') fieldNamed: 'id') ]. (aTable createFieldNamed: 'version' type: platform blob) beIndexed. ! ! !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! descriptorForTgAgent: aDescriptor | table | table := self tableNamed: 'AGENT'. aDescriptor table: table. (aDescriptor newMapping: DirectMapping) from: #id to: (table fieldNamed: 'id').! ! !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! tableForACT: aTable (aTable createFieldNamed: 'agent' type: platform blob2) beIndexed. (aTable createFieldNamed: 'tool' type: platform blob2) beIndexed. (aTable createFieldNamed: 'timestamp' type: platform timestamp) beIndexed. ! ! !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! descriptorForTgState: aDescriptor | table | table := self tableNamed: 'STATE'. aDescriptor table: table. (aDescriptor newMapping: OneToOneMapping) attributeName: #agent. (aDescriptor newMapping: DirectMapping) from: #version to: (table fieldNamed: 'version').! ! !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! converterBetweenStType: aClass andField: aField (aClass = UUID and: [ aField impliedSmalltalkType = ByteArray]) ifTrue: [ ^ self uuidConverter ]. ^ super converterBetweenStType: aClass andField: aField! ! !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! classModelForTgState: aClassModel "agent version packs valuables score bestScore answers" aClassModel newAttributeNamed: #agent type: TgAgent; newAttributeNamed: #version type: UUID! ! !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! classModelForTgTool: aClassModel aClassModel newAttributeNamed: #id ! ! !TowergameDescriptorSystem methodsFor: 'glorp' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! classModelForTgAct: aClassModel aClassModel newAttributeNamed: #timestamp; newAttributeNamed: #agent type: TgAgent; newAttributeNamed: #tool type: TgTool! ! !TowergameDescriptorSystem methodsFor: 'accessing' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! uuidConverter ^ uuidConverter ifNil: [ uuidConverter := UuidConverter new name: 'uuid'; yourself ]! ! DatabaseConverter subclass: #UuidConverter instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Towergame'! !UuidConverter methodsFor: 'converting' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! convert: anObject toDatabaseRepresentationAs: aDatabaseType ^ anObject ifNotNil: [ ByteArray withAll: anObject ]! ! !UuidConverter methodsFor: 'converting' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! convert: anObject fromDatabaseRepresentationAs: aDatabaseType ^ anObject ifNotNil: [ UUID withAll: anObject ]! ! 'From Pharo6.0 of 13 May 2016 [Latest update: #60510] on 14 August 2017 at 6:26:30.67905 pm'! !DatabasePlatform methodsFor: '*Towergame' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! blob2 ^self typeNamed: #blob ifAbsentPut: [GlorpBlob2Type new].! ! 'From Pharo6.0 of 13 May 2016 [Latest update: #60510] on 14 August 2017 at 6:26:30.68005 pm'! !Dictionary methodsFor: '*Towergame' stamp: 'HerbertVoj������������������������k 8/14/2017 18:09:53'! at: key ifPresentPut: aBlock "Lookup the given key in the receiver. If it is present, update it with the value of evaluating the given block with the value associated with the key. Otherwise, answer nil." ^ self at: key ifPresent: [ :value | self at: key put: (aBlock cull: value) ]! !