├── .gitignore
├── Podfile
├── Podfile.lock
├── README.md
├── SwiftRPG.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
├── SwiftRPG.xcworkspace
└── contents.xcworkspacedata
├── SwiftRPG
├── Supporting Files
│ ├── GameScene.sks
│ └── Info.plist
├── resources
│ ├── image
│ │ ├── Character
│ │ │ ├── mob.xcassets
│ │ │ │ ├── Contents.json
│ │ │ │ └── mob.imageset
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── mob.png
│ │ │ └── player.xcassets
│ │ │ │ ├── Contents.json
│ │ │ │ ├── player.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── player.png
│ │ │ │ ├── plr_down.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── plr_down.png
│ │ │ │ ├── plr_down_01.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── plr_down_01.png
│ │ │ │ ├── plr_down_02.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── plr_down_02.png
│ │ │ │ ├── plr_left.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── plr_left.png
│ │ │ │ ├── plr_left_01.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── plr_left_01.png
│ │ │ │ ├── plr_left_02.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── plr_left_02.png
│ │ │ │ ├── plr_right.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── plr_right.png
│ │ │ │ ├── plr_right_01.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── plr_right_01.png
│ │ │ │ ├── plr_right_02.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── plr_right_02.png
│ │ │ │ ├── plr_up.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── plr_up.png
│ │ │ │ ├── plr_up_01.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── plr_up_01.png
│ │ │ │ └── plr_up_02.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── plr_up_02.png
│ │ ├── Images.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ └── start.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── start.png
│ │ └── Map
│ │ │ ├── black.png
│ │ │ ├── kanamonoMap.xcassets
│ │ │ ├── Contents.json
│ │ │ ├── collisions.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── collisions.png
│ │ │ └── kanamono_tile.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── kanamono_tile.png
│ │ │ ├── sample_map01.json
│ │ │ └── sample_map02.json
│ ├── talk.wav
│ └── talking
│ │ └── talk_01.txt
└── src
│ ├── AppDelegate.swift
│ ├── Controller
│ ├── GameViewController.swift
│ ├── MenuViewController.swift
│ └── TitleViewController.swift
│ ├── Model
│ ├── A-star
│ │ ├── AStar.swift
│ │ └── Node.swift
│ ├── DataModel
│ │ └── DataModels.swift
│ ├── Dialog
│ │ ├── Dialog.swift
│ │ └── TalkBodyParser.swift
│ ├── Event
│ │ ├── EventDispatcher.swift
│ │ ├── EventListener.swift
│ │ ├── EventListener
│ │ │ ├── ActivateButtonListener.swift
│ │ │ ├── BackToDefaultStateEventListener.swift
│ │ │ ├── Event Dialog
│ │ │ │ ├── HideEventDialogListener.swift
│ │ │ │ └── ShowEventDialogListener.swift
│ │ │ ├── EventListenerImplement.swift
│ │ │ ├── InvokeNextEventListener.swift
│ │ │ ├── ItemGetEventListener.swift
│ │ │ ├── MoveObjectEventListener.swift
│ │ │ ├── ReloadBehaviorEventListener.swift
│ │ │ ├── RenderDefaultViewEventListener.swift
│ │ │ ├── SceneTransitionEventListener.swift
│ │ │ ├── Talk Event
│ │ │ │ ├── FinishTalkEventListener.swift
│ │ │ │ ├── StartTalkEventListener.swift
│ │ │ │ └── TalkEventListener.swift
│ │ │ ├── WaitEventListener.swift
│ │ │ ├── WalkEventListener.swift
│ │ │ └── WalkOneStepEventListener.swift
│ │ └── EventManager.swift
│ ├── Map
│ │ ├── Map.swift
│ │ ├── TileSheet
│ │ │ ├── MapObject
│ │ │ │ ├── EventObject.swift
│ │ │ │ ├── MapObject.swift
│ │ │ │ ├── Object.swift
│ │ │ │ ├── Tile.swift
│ │ │ │ └── util
│ │ │ │ │ ├── BehaviorPropertyParser.swift
│ │ │ │ │ ├── EventPropertyParser.swift
│ │ │ │ │ ├── ListenerContainer.swift
│ │ │ │ │ └── ListenerGenerator.swift
│ │ │ ├── TileCoordinate.swift
│ │ │ ├── TileSet.swift
│ │ │ └── TileSheet.swift
│ │ └── TiledMapJsonParser.swift
│ ├── MenuSceneModel.swift
│ ├── Setting
│ │ ├── ItemTable.swift
│ │ ├── MapTable.swift
│ │ ├── objectNameTable.swift
│ │ ├── talkerImage.swift
│ │ └── zPositionTable.swift
│ └── common
│ │ ├── DIRECTION.swift
│ │ ├── DialogLabel.swift
│ │ ├── IMAGE_SET.swift
│ │ ├── String+subscript.swift
│ │ ├── UIButton+title.swift
│ │ ├── UIButtonAnimated.swift
│ │ └── myButton.swift
│ └── View
│ ├── FirstGameScene.swift
│ ├── GameScene.swift
│ ├── MenuScene.swift
│ ├── MenuScene.xib
│ ├── TitleScene.swift
│ ├── TitleScene.xib
│ ├── animation
│ └── TransitionToMenuAnimator.swift
│ └── components
│ ├── ItemCell.swift
│ ├── ItemCell.xib
│ └── SecondGameScene.swift
├── SwiftRPGTests
├── Supporting Files
│ └── Info.plist
└── SwiftRPGTests.swift
└── readme_resources
└── movie.gif
/.gitignore:
--------------------------------------------------------------------------------
1 | #
2 | # Xcode
3 | #
4 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
5 |
6 | ## Build generated
7 | build/
8 | DerivedData
9 |
10 | ## Various settings
11 | *.pbxuser
12 | !default.pbxuser
13 | *.mode1v3
14 | !default.mode1v3
15 | *.mode2v3
16 | !default.mode2v3
17 | *.perspectivev3
18 | !default.perspectivev3
19 | xcuserdata
20 |
21 | ## Other
22 | *.xccheckout
23 | *.moved-aside
24 | *.xcuserstate
25 | *.xcscmblueprint
26 |
27 | ## Obj-C/Swift specific
28 | *.hmap
29 | *.ipa
30 |
31 | # CocoaPods
32 | #
33 | # We recommend against adding the Pods directory to your .gitignore. However
34 | # you should judge for yourself, the pros and cons are mentioned at:
35 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
36 | #
37 | Pods/
38 |
39 | # Carthage
40 | #
41 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
42 | # Carthage/Checkouts
43 |
44 | Carthage/Build
45 |
46 | .ruby-version
47 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | source 'https://github.com/CocoaPods/Specs.git'
2 | platform :ios, '8.0'
3 | use_frameworks!
4 |
5 | pod 'SwiftyJSON', :git => 'https://github.com/SwiftyJSON/SwiftyJSON.git'
6 | pod 'JSONSchema', :git => 'https://github.com/kylef/JSONSchema.swift.git', :branch => 'master'
7 | pod 'RealmSwift'
8 | pod "PromiseKit", "~> 4.0"
9 |
10 | target 'SwiftRPG' do
11 |
12 | end
13 |
14 | target 'SwiftRPGTests' do
15 |
16 | end
17 |
18 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - JSONSchema (0.4.0)
3 | - PromiseKit (4.0.4):
4 | - PromiseKit/Foundation (= 4.0.4)
5 | - PromiseKit/QuartzCore (= 4.0.4)
6 | - PromiseKit/UIKit (= 4.0.4)
7 | - PromiseKit/CorePromise (4.0.4)
8 | - PromiseKit/Foundation (4.0.4):
9 | - PromiseKit/CorePromise
10 | - PromiseKit/QuartzCore (4.0.4):
11 | - PromiseKit/CorePromise
12 | - PromiseKit/UIKit (4.0.4):
13 | - PromiseKit/CorePromise
14 | - Realm (2.0.3):
15 | - Realm/Headers (= 2.0.3)
16 | - Realm/Headers (2.0.3)
17 | - RealmSwift (2.0.3):
18 | - Realm (= 2.0.3)
19 | - SwiftyJSON (3.1.1)
20 |
21 | DEPENDENCIES:
22 | - JSONSchema (from `https://github.com/kylef/JSONSchema.swift.git`, branch `master`)
23 | - PromiseKit (~> 4.0)
24 | - RealmSwift
25 | - SwiftyJSON (from `https://github.com/SwiftyJSON/SwiftyJSON.git`)
26 |
27 | EXTERNAL SOURCES:
28 | JSONSchema:
29 | :branch: master
30 | :git: https://github.com/kylef/JSONSchema.swift.git
31 | SwiftyJSON:
32 | :git: https://github.com/SwiftyJSON/SwiftyJSON.git
33 |
34 | CHECKOUT OPTIONS:
35 | JSONSchema:
36 | :commit: 8f8440e6766fe0d34bf50833c1d5e70a71c999d1
37 | :git: https://github.com/kylef/JSONSchema.swift.git
38 | SwiftyJSON:
39 | :commit: 1dc2ce0f913b27380cd5b803af8aa853745b9608
40 | :git: https://github.com/SwiftyJSON/SwiftyJSON.git
41 |
42 | SPEC CHECKSUMS:
43 | JSONSchema: 6983b7ba0b0711e9a92bcc789a4bfc16fc351ec7
44 | PromiseKit: 8e8ee39d33ff199d92a1a883b5396f6285be6c91
45 | Realm: 5f008bfe3c8c47142eddfc30b8c1584cde22db24
46 | RealmSwift: 5afb451f65b682242f272dbb52d5c6f6e343c980
47 | SwiftyJSON: f0be2e604f83e8405a624e9f891898bf6ed4e019
48 |
49 | PODFILE CHECKSUM: 7966587d84cc0c98820010aea99d83db49a241d0
50 |
51 | COCOAPODS: 1.1.1
52 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Swift RPG
2 |
3 | Simple game written in swift with SpriteKit.
4 | *Work in progress*
5 |
6 | ## Document
7 |
8 | [here](https://github.com/tasuwo/SwiftRPG/wiki)
9 |
10 | ## Screen Shot
11 |
12 | 
13 |
14 | ## TODO
15 |
16 | - ~~Move objects~~
17 | - ~~Collision detection management~~
18 | - ~~Stop object's behavior when touch event occured~~
19 | - ~~Loop object's behavior~~
20 | - ~~Disabling walking and behavior~~
21 | - ~~Scene transition~~
22 | - ~~Use only SpriteKit. Use UIKit for using storyboard, but it's not efficiency.~~
23 | - Sound
24 | - Flag management
25 | - Battle
26 | - Document
27 |
28 | ## BUG
29 |
30 | - ~~Talk with npc during npc moving, the game would be crashed.~~
31 |
--------------------------------------------------------------------------------
/SwiftRPG.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SwiftRPG.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/SwiftRPG/Supporting Files/GameScene.sks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/Supporting Files/GameScene.sks
--------------------------------------------------------------------------------
/SwiftRPG/Supporting Files/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UIStatusBarHidden
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationLandscapeLeft
36 | UIInterfaceOrientationLandscapeRight
37 |
38 | UISupportedInterfaceOrientations~ipad
39 |
40 | UIInterfaceOrientationPortrait
41 | UIInterfaceOrientationPortraitUpsideDown
42 | UIInterfaceOrientationLandscapeLeft
43 | UIInterfaceOrientationLandscapeRight
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/mob.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/mob.xcassets/mob.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "mob.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/mob.xcassets/mob.imageset/mob.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Character/mob.xcassets/mob.imageset/mob.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/player.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "player.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/player.imageset/player.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Character/player.xcassets/player.imageset/player.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_down.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "plr_down.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_down.imageset/plr_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Character/player.xcassets/plr_down.imageset/plr_down.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_down_01.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "plr_down_01.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_down_01.imageset/plr_down_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Character/player.xcassets/plr_down_01.imageset/plr_down_01.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_down_02.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "plr_down_02.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_down_02.imageset/plr_down_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Character/player.xcassets/plr_down_02.imageset/plr_down_02.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_left.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "plr_left.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_left.imageset/plr_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Character/player.xcassets/plr_left.imageset/plr_left.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_left_01.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "plr_left_01.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_left_01.imageset/plr_left_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Character/player.xcassets/plr_left_01.imageset/plr_left_01.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_left_02.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "plr_left_02.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_left_02.imageset/plr_left_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Character/player.xcassets/plr_left_02.imageset/plr_left_02.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_right.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "plr_right.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_right.imageset/plr_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Character/player.xcassets/plr_right.imageset/plr_right.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_right_01.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "plr_right_01.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_right_01.imageset/plr_right_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Character/player.xcassets/plr_right_01.imageset/plr_right_01.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_right_02.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "plr_right_02.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_right_02.imageset/plr_right_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Character/player.xcassets/plr_right_02.imageset/plr_right_02.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_up.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "plr_up.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_up.imageset/plr_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Character/player.xcassets/plr_up.imageset/plr_up.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_up_01.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "plr_up_01.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_up_01.imageset/plr_up_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Character/player.xcassets/plr_up_01.imageset/plr_up_01.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_up_02.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "plr_up_02.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Character/player.xcassets/plr_up_02.imageset/plr_up_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Character/player.xcassets/plr_up_02.imageset/plr_up_02.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | }
88 | ],
89 | "info" : {
90 | "version" : 1,
91 | "author" : "xcode"
92 | }
93 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Images.xcassets/start.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "start.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Images.xcassets/start.imageset/start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Images.xcassets/start.imageset/start.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Map/black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Map/black.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Map/kanamonoMap.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Map/kanamonoMap.xcassets/collisions.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "collisions.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Map/kanamonoMap.xcassets/collisions.imageset/collisions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Map/kanamonoMap.xcassets/collisions.imageset/collisions.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Map/kanamonoMap.xcassets/kanamono_tile.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "kanamono_tile.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Map/kanamonoMap.xcassets/kanamono_tile.imageset/kanamono_tile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/image/Map/kanamonoMap.xcassets/kanamono_tile.imageset/kanamono_tile.png
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Map/sample_map01.json:
--------------------------------------------------------------------------------
1 | { "height":20,
2 | "layers":[
3 | {
4 | "data":[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 13, 13, 13, 13, 13, 13, 13, 13, 13, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 13, 13, 13, 13, 13, 13, 13, 13, 13, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 13, 13, 13, 6, 6, 6, 13, 13, 13, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 13, 13, 13, 6, 6, 6, 13, 13, 13, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 13, 13, 13, 6, 6, 6, 13, 13, 13, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 13, 13, 13, 13, 13, 13, 13, 13, 13, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 13, 13, 13, 13, 13, 13, 13, 13, 13, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
5 | "height":20,
6 | "name":"tile",
7 | "opacity":1,
8 | "type":"tilelayer",
9 | "visible":true,
10 | "width":20,
11 | "x":0,
12 | "y":0
13 | },
14 | {
15 | "data":[12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 12, 12, 12, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 12, 12, 12, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 12, 12, 12, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12],
16 | "height":20,
17 | "name":"collision",
18 | "opacity":0.310000002384186,
19 | "type":"tilelayer",
20 | "visible":true,
21 | "width":20,
22 | "x":0,
23 | "y":0
24 | },
25 | {
26 | "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
27 | "height":20,
28 | "name":"object",
29 | "opacity":1,
30 | "type":"tilelayer",
31 | "visible":true,
32 | "width":20,
33 | "x":0,
34 | "y":0
35 | }],
36 | "nextobjectid":1,
37 | "orientation":"orthogonal",
38 | "renderorder":"right-up",
39 | "tileheight":32,
40 | "tilesets":[
41 | {
42 | "columns":6,
43 | "firstgid":1,
44 | "image":"kanamonoMap.xcassets\/collisions.imageset\/collisions.png",
45 | "imageheight":64,
46 | "imagewidth":192,
47 | "margin":0,
48 | "name":"collisions",
49 | "spacing":0,
50 | "tilecount":12,
51 | "tileheight":32,
52 | "tileproperties":
53 | {
54 | "0":
55 | {
56 | "collision":"1"
57 | },
58 | "1":
59 | {
60 | "collision":"1"
61 | },
62 | "11":
63 | {
64 | "event":"scene,{(0,0)},sample_map02.json,10-10,UP"
65 | },
66 | "6":
67 | {
68 | "collision":"1"
69 | }
70 | },
71 | "tilepropertytypes":
72 | {
73 | "0":
74 | {
75 | "collision":"string"
76 | },
77 | "1":
78 | {
79 | "collision":"string"
80 | },
81 | "11":
82 | {
83 | "event":"string"
84 | },
85 | "6":
86 | {
87 | "collision":"string"
88 | }
89 | },
90 | "tilewidth":32
91 | },
92 | {
93 | "columns":1,
94 | "firstgid":13,
95 | "image":"kanamonoMap.xcassets\/kanamono_tile.imageset\/kanamono_tile.png",
96 | "imageheight":32,
97 | "imagewidth":32,
98 | "margin":0,
99 | "name":"kanamono_tile",
100 | "spacing":0,
101 | "tilecount":1,
102 | "tileheight":32,
103 | "tileproperties":
104 | {
105 | "0":
106 | {
107 | "collision":"0"
108 | }
109 | },
110 | "tilepropertytypes":
111 | {
112 | "0":
113 | {
114 | "collision":"string"
115 | }
116 | },
117 | "tilewidth":32
118 | },
119 | {
120 | "columns":1,
121 | "firstgid":14,
122 | "image":"..\/Character\/mob.xcassets\/mob.imageset\/mob.png",
123 | "imageheight":64,
124 | "imagewidth":32,
125 | "margin":0,
126 | "name":"mob",
127 | "spacing":0,
128 | "tilecount":1,
129 | "tileheight":64,
130 | "tileproperties":
131 | {
132 | "0":
133 | {
134 | "behavior":"wait,1\nmove,mob,UP,1,1\nwait,1\nmove,mob,DOWN,1,1",
135 | "collision":"1",
136 | "event":"talk,{(0,-1)},talk_01.txt"
137 | }
138 | },
139 | "tilepropertytypes":
140 | {
141 | "0":
142 | {
143 | "behavior":"string",
144 | "collision":"string",
145 | "event":"string"
146 | }
147 | },
148 | "tilewidth":32
149 | }],
150 | "tilewidth":32,
151 | "version":1,
152 | "width":20
153 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/image/Map/sample_map02.json:
--------------------------------------------------------------------------------
1 | { "height":20,
2 | "layers":[
3 | {
4 | "data":[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 8, 8, 8, 8, 8, 8, 8, 8, 11, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 6, 6, 6, 6, 6, 6, 6, 7, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 6, 6, 6, 6, 6, 6, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 6, 6, 6, 6, 6, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 6, 6, 6, 6, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 6, 6, 6, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 6, 6, 6, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 6, 6, 6, 6, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 6, 6, 6, 6, 6, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 5, 6, 6, 6, 6, 6, 6, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
5 | "height":20,
6 | "name":"tile",
7 | "opacity":1,
8 | "type":"tilelayer",
9 | "visible":false,
10 | "width":20,
11 | "x":0,
12 | "y":0
13 | },
14 | {
15 | "data":[12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12],
16 | "height":20,
17 | "name":"collision",
18 | "opacity":0.430000007152557,
19 | "type":"tilelayer",
20 | "visible":false,
21 | "width":20,
22 | "x":0,
23 | "y":0
24 | },
25 | {
26 | "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
27 | "height":20,
28 | "name":"object",
29 | "opacity":1,
30 | "type":"tilelayer",
31 | "visible":true,
32 | "width":20,
33 | "x":0,
34 | "y":0
35 | }],
36 | "nextobjectid":1,
37 | "orientation":"orthogonal",
38 | "renderorder":"right-up",
39 | "tileheight":32,
40 | "tilesets":[
41 | {
42 | "columns":6,
43 | "firstgid":1,
44 | "image":"kanamonoMap.xcassets\/collisions.imageset\/collisions.png",
45 | "imageheight":64,
46 | "imagewidth":192,
47 | "margin":0,
48 | "name":"collisions",
49 | "spacing":0,
50 | "tilecount":12,
51 | "tileheight":32,
52 | "tileproperties":
53 | {
54 | "0":
55 | {
56 | "collision":"1"
57 | },
58 | "1":
59 | {
60 | "collision":"1"
61 | },
62 | "11":
63 | {
64 | "event":"scene,{(0,0)},sample_map01.json,8-7,UP"
65 | },
66 | "6":
67 | {
68 | "collision":"1"
69 | }
70 | },
71 | "tilepropertytypes":
72 | {
73 | "0":
74 | {
75 | "collision":"string"
76 | },
77 | "1":
78 | {
79 | "collision":"string"
80 | },
81 | "11":
82 | {
83 | "event":"string"
84 | },
85 | "6":
86 | {
87 | "collision":"string"
88 | }
89 | },
90 | "tilewidth":32
91 | },
92 | {
93 | "columns":1,
94 | "firstgid":13,
95 | "image":"kanamonoMap.xcassets\/kanamono_tile.imageset\/kanamono_tile.png",
96 | "imageheight":32,
97 | "imagewidth":32,
98 | "margin":0,
99 | "name":"kanamono_tile",
100 | "spacing":0,
101 | "tilecount":1,
102 | "tileheight":32,
103 | "tileproperties":
104 | {
105 | "0":
106 | {
107 | "collision":"0"
108 | }
109 | },
110 | "tilepropertytypes":
111 | {
112 | "0":
113 | {
114 | "collision":"string"
115 | }
116 | },
117 | "tilewidth":32
118 | },
119 | {
120 | "columns":1,
121 | "firstgid":14,
122 | "image":"..\/Character\/mob.xcassets\/mob.imageset\/mob.png",
123 | "imageheight":64,
124 | "imagewidth":32,
125 | "margin":0,
126 | "name":"mob",
127 | "spacing":0,
128 | "tilecount":1,
129 | "tileheight":64,
130 | "tileproperties":
131 | {
132 | "0":
133 | {
134 | "behavior":"wait,1\nmove,mob,LEFT,1,1\nwait,1\nmove,mob,RIGHT,1,1",
135 | "collision":"1",
136 | "event":"talk,{(0,-1)},talk_01.txt\nitem,{(0,-1)},test"
137 | }
138 | },
139 | "tilepropertytypes":
140 | {
141 | "0":
142 | {
143 | "behavior":"string",
144 | "collision":"string",
145 | "event":"string"
146 | }
147 | },
148 | "tilewidth":32
149 | }],
150 | "tilewidth":32,
151 | "version":1,
152 | "width":20
153 | }
--------------------------------------------------------------------------------
/SwiftRPG/resources/talk.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/SwiftRPG/resources/talk.wav
--------------------------------------------------------------------------------
/SwiftRPG/resources/talking/talk_01.txt:
--------------------------------------------------------------------------------
1 | player:L
2 | あいうえお.
3 | かきくけこ.
4 | !
5 | player:R
6 | さしすせそ.
7 | たちつてと.
8 | !
--------------------------------------------------------------------------------
/SwiftRPG/src/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2015/06/27.
6 | // Copyright (c) 2015年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 | /**
17 | * アプリケーション起動時に呼ばれる
18 | */
19 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
20 |
21 | // window 生成
22 | window = UIWindow(frame: UIScreen.main.bounds)
23 | if let window = window {
24 | window.backgroundColor = UIColor.white
25 | // rootViewController の割り当て
26 | window.rootViewController = TitleViewController()
27 | window.makeKeyAndVisible()
28 | }
29 |
30 | return true
31 | }
32 |
33 | func applicationWillResignActive(_ application: UIApplication) {
34 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
35 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
36 | }
37 |
38 | func applicationDidEnterBackground(_ application: UIApplication) {
39 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
40 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
41 | }
42 |
43 | func applicationWillEnterForeground(_ application: UIApplication) {
44 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
45 | }
46 |
47 | func applicationDidBecomeActive(_ application: UIApplication) {
48 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
49 | }
50 |
51 | func applicationWillTerminate(_ application: UIApplication) {
52 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
53 | }
54 |
55 |
56 | }
57 |
58 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Controller/GameViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GameViewController.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2015/06/27.
6 | // Copyright (c) 2015年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SpriteKit
11 | import SwiftyJSON
12 | import PromiseKit
13 |
14 | class GameViewController: UIViewController {
15 | var viewInitiated: Bool = false
16 | var eventManager: EventManager!
17 | var eventObjectIds: Set? = nil
18 | var currentGameScene: GameScene? = nil
19 |
20 | override func loadView() {
21 | self.view = SKView()
22 | }
23 |
24 | override func viewDidLoad() {
25 | super.viewDidLoad()
26 | self.view.isMultipleTouchEnabled = false
27 | self.eventManager = EventManager()
28 | }
29 |
30 | override func viewWillLayoutSubviews() {
31 | super.viewWillLayoutSubviews()
32 |
33 | if (!viewInitiated) {
34 | let scene = FirstGameScene(size: self.view.frame.size, playerCoordiante: TileCoordinate(x:7,y:7), playerDirection: .down)
35 | scene.gameSceneDelegate = self
36 | scene.container = self.eventManager
37 | self.currentGameScene = scene
38 |
39 | let skView = self.view as! SKView
40 | skView.presentScene(self.currentGameScene)
41 |
42 | self.viewInitiated = true
43 | }
44 | }
45 | }
46 |
47 | extension GameViewController: GameSceneDelegate {
48 | func frameTouched(_ location: CGPoint) {}
49 |
50 | func gameSceneTouched(_ location: CGPoint) {
51 | let args = JSON(["touchedPoint": NSStringFromCGPoint(location)])
52 | let gameScene = self.currentGameScene!
53 |
54 | do {
55 | try self.eventManager.trigger(.touch, sender: gameScene, args: args)
56 | } catch EventManagerError.FailedToTrigger(let string) {
57 | print("Failed to trigger touch event: " + string)
58 | } catch {
59 | print("Unexpected error has occurred during triggering touch event")
60 | }
61 | }
62 |
63 | func actionButtonTouched() {
64 | let gameScene = self.currentGameScene!
65 |
66 | do {
67 | try self.eventManager.trigger(.button, sender: gameScene, args: nil)
68 | } catch EventManagerError.FailedToTrigger(let string) {
69 | print("Failed to trigger action event: " + string)
70 | } catch {
71 | print("Unexpected error has occurred during triggering action event")
72 | }
73 | }
74 |
75 | func menuButtonTouched() {
76 | let viewController = MenuViewController()
77 | self.present(viewController, animated: true, completion: nil)
78 | }
79 |
80 | // TODO: Error handling when adding or removing event listener has failed
81 |
82 | // This function is executed cyclically
83 | // The role of this function is as following
84 | // - Update object's z-index position.
85 | // Front objects should render as looking like upper than back objects.
86 | // - Check player collision with events.
87 | // If the collision was occurred, invoke event.
88 | // - Trigger cyclic event listeners.
89 | func viewUpdated() {
90 | let gameScene = self.currentGameScene!
91 | let map = gameScene.map
92 |
93 | // Update z-index of objects
94 | map?.updateObjectsZPosition()
95 |
96 | // If player was on the event object, the listeners which has the placed event's
97 | // id should be in event dispatcher. But if player left from the event object,
98 | // the listeners should be removed from dispatcher as soon as possible.
99 | // To realize above, the placed event's id should be stored in somewhere.
100 | //
101 | // - The role of Object class is to store information about self and generate some
102 | // components (e.g. animation) for dealing with self by others.
103 | // - The role of Map class is to store the state of placement of objects and tiles,
104 | // and provide methods for manipulating them.
105 | //
106 | // I cannot judge this, so this role add to this controller for now.
107 | let events_ = map?.getEventsOnPlayerPosition()
108 | if events_ != nil && map?.getObjectByName(objectNameTable.PLAYER_NAME)?.isAnimated == false {
109 | let events: [EventListener] = events_!
110 |
111 | // Update eventObjectIds value
112 | if self.eventObjectIds == nil {
113 | self.eventObjectIds = []
114 | for event in events {
115 | self.eventObjectIds?.insert(event.eventObjectId!)
116 | }
117 | } else {
118 | var newIdSets: Set = []
119 | for event in events {
120 | newIdSets.insert(event.eventObjectId!)
121 | }
122 | let unregisteredIds = newIdSets.subtracting(self.eventObjectIds!)
123 | for id in unregisteredIds {
124 | self.eventObjectIds?.insert(id)
125 | }
126 | let removedIds = self.eventObjectIds?.subtracting(newIdSets)
127 | for id in removedIds! {
128 | self.eventManager.remove(id, sender: gameScene)
129 | self.eventObjectIds!.remove(id)
130 | }
131 | }
132 |
133 | // Invoke events
134 | for event in events {
135 | self.eventManager.add(event)
136 | }
137 | } else {
138 | if self.eventObjectIds != nil {
139 | for id in self.eventObjectIds! {
140 | self.eventManager.remove(id, sender: gameScene)
141 | }
142 | self.eventObjectIds = nil
143 | }
144 | }
145 |
146 | // Trigger cyclic events
147 | do {
148 | try self.eventManager.trigger(.immediate, sender: gameScene, args: nil)
149 | } catch EventManagerError.FailedToTrigger(let string) {
150 | print("Failed to trigger cyclic event: " + string)
151 | } catch {
152 | print("Unexpected error has occurred during triggering cyclic event")
153 | }
154 | }
155 |
156 | func startBehaviors(_ behaviors: Dictionary) {
157 | if self.eventManager.isBlockingBehavior == false { return }
158 |
159 | self.eventManager.unblockBehavior()
160 | for behavior in behaviors.values {
161 | behavior.isExecuting = false
162 | self.eventManager.add(behavior)
163 | }
164 | }
165 |
166 | func stopBehaviors() {
167 | self.eventManager.blockBehavior()
168 | }
169 |
170 | func startWalking() {
171 | if self.eventManager.isBlockingWalking == false { return }
172 |
173 | self.eventManager.unblockWalking()
174 | self.eventManager.add(WalkEventListener.init(params: nil, chainListeners: nil))
175 | }
176 |
177 | func stopWalking() {
178 | self.eventManager.blockWalking()
179 | }
180 |
181 | func unavailableAllListeners() {
182 | self.eventManager.unavailableAllListeners()
183 | }
184 |
185 | func transitionTo(_ newScene: GameScene.Type, playerCoordinate coordinate: TileCoordinate, playerDirection direction: DIRECTION) -> Promise {
186 | let scene = newScene.init(size: self.view.bounds.size, playerCoordiante: coordinate, playerDirection: direction)
187 | scene.gameSceneDelegate = self
188 | scene.container = self.eventManager
189 | self.currentGameScene = scene
190 |
191 | let delay: TimeInterval = 2
192 | let skView = self.view as! SKView
193 | let skScene = skView.scene!
194 | skScene.view?.presentScene(scene, transition: SKTransition.fade(withDuration: delay))
195 |
196 | return Promise { fulfill, reject in
197 | UIView.animate(
198 | withDuration: delay,
199 | animations: { () -> Void in }
200 | ) { (animationCompleted: Bool) -> Void in fulfill()}
201 | }
202 | }
203 | }
204 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Controller/MenuViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MenuViewController.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/12/21.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SpriteKit
11 |
12 | class MenuViewController: UIViewController {
13 | var viewInitiated: Bool = false
14 | fileprivate var model: MenuSceneModel!
15 | let transition = TransitionBetweenGameAndMenuSceneAnimator()
16 |
17 | override func loadView() {
18 | self.view = SKView()
19 | }
20 |
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 | self.view.isMultipleTouchEnabled = false
24 | }
25 |
26 | override func viewWillLayoutSubviews() {
27 | super.viewWillLayoutSubviews()
28 |
29 | if (!viewInitiated) {
30 | let scene = MenuScene(size: self.view.bounds.size)
31 | scene.menuSceneDelegate = self
32 |
33 | self.model = MenuSceneModel()
34 | self.model.delegate = scene
35 | self.model.updateItems()
36 | scene.model = self.model
37 |
38 | self.view = scene.sceneView
39 | let skView = self.view as! SKView
40 | skView.presentScene(scene)
41 |
42 | self.viewInitiated = true
43 | }
44 | }
45 | }
46 |
47 | extension MenuViewController: MenuSceneDelegate {
48 | func didPressBackButton() {
49 | self.dismiss(animated: true, completion: nil)
50 | }
51 |
52 | func didSelectedItem(_ indexPath: IndexPath) {
53 | self.model.selectItem(indexPath)
54 | }
55 | }
56 |
57 | extension MenuViewController: UIViewControllerTransitioningDelegate {
58 | func animationController(
59 | forPresented presented: UIViewController,
60 | presenting: UIViewController,
61 | source: UIViewController
62 | ) -> UIViewControllerAnimatedTransitioning?
63 | {
64 | self.transition.originFrame = self.view.frame
65 | self.transition.presenting = true
66 | return self.transition
67 | }
68 |
69 | func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
70 | self.transition.presenting = false
71 | return self.transition
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Controller/TitleViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TitleViewController.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2015/07/15.
6 | // Copyright (c) 2015年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SpriteKit
11 |
12 | class TitleViewController: UIViewController {
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 | let view = TitleScene(frame: self.view.frame)
16 | view.titleSceneDelegate = self
17 |
18 | self.view.addSubview(view)
19 | }
20 |
21 | override func didReceiveMemoryWarning() {
22 | super.didReceiveMemoryWarning()
23 | }
24 | }
25 |
26 | extension TitleViewController: TitleSceneDelegate {
27 | func newGameTouched() {
28 | let gameViewController: UIViewController = GameViewController()
29 | self.present(gameViewController, animated: false, completion: nil)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/A-star/AStar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AStar.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2015/08/10.
6 | // Copyright © 2015年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import SpriteKit
12 |
13 | class AStar {
14 | /// 基準ノードのインデックス
15 | var iBaseNode: Int!
16 |
17 | /// 探索対象のマップ
18 | let map: Map
19 |
20 | /// 出発地点のタイル座標
21 | var departure: TileCoordinate!
22 |
23 | /// 目的地点のタイル座標
24 | var destination: TileCoordinate!
25 |
26 | /// 生成したノードを格納しておくリスト
27 | var nodeList: [Node] = []
28 |
29 | init(map: Map) {
30 | self.map = map
31 | }
32 |
33 | /// A*アルゴリズムの初期化
34 | ///
35 | /// - parameter departure: 出発するタイル座標
36 | /// - parameter destination: 目的地のタイル座標
37 | func initialize(_ departure: TileCoordinate, destination: TileCoordinate) {
38 | self.departure = departure
39 | self.destination = destination
40 | nodeList = []
41 | }
42 |
43 | /// A*アルゴリズム開始
44 | ///
45 | /// - returns: 失敗の場合はnil,成功の場合は経路を表したタイルの配列が返る
46 | func main() -> [TileCoordinate]? {
47 | /*** 終了判定 ***/
48 | // initialize されていない
49 | if departure == nil || destination == nil {
50 | print("A* is not initialized")
51 | return nil
52 | }
53 | // 目標のタイルが到達不可能,もしくは通行不可能
54 | if !canPass(destination) {
55 | print("Target tile cannnot pass or reach")
56 | return nil
57 | }
58 |
59 | // 基準ノードの取得
60 | if nodeList.isEmpty {
61 | iBaseNode = 0
62 | nodeList.append(Node(coordinates: departure))
63 | nodeList[iBaseNode].open(nil, destination: destination)
64 | } else {
65 | iBaseNode = chooseBaseNodeIndex()
66 | }
67 | // 基準ノードが存在しない場合は,移動失敗
68 | if iBaseNode == nil {
69 | print("There are no base node")
70 | return nil
71 | }
72 |
73 | // 基準ノードの周囲からOpen可能なノードを探す
74 | let indexes = searchCanOpenNodeIndexes(iBaseNode)
75 | // 各ノードをOpenする
76 | for index in indexes {
77 | nodeList[index].open(nodeList[iBaseNode], destination: destination)
78 | // 終了判定
79 | if nodeList[index].coordinates == destination {
80 | return getAStarResult()
81 | }
82 | }
83 | // 基準ノードを閉じる
84 | nodeList[iBaseNode].close()
85 |
86 | return main()
87 | }
88 |
89 | /// 基準ノードのインデックスを選ぶ
90 | ///
91 | /// - returns: 基準ノードのインデックス
92 | fileprivate func chooseBaseNodeIndex() -> Int? {
93 | var min = -1
94 | var iMinNode: Int? = nil
95 |
96 | // Open なノードを選ぶ
97 | for i in 0 ..< nodeList.count {
98 | if nodeList[i].state == Node.STATE.open {
99 | // スコアが最小のものを選ぶ
100 | if min == -1 {
101 | min = nodeList[i].score
102 | iMinNode = i
103 | continue
104 | }
105 | if nodeList[i].score < min {
106 | min = nodeList[i].score
107 | iMinNode = i
108 | }
109 | }
110 | }
111 | return iMinNode
112 | }
113 |
114 | /// 基準ノードの周りの Open 可能なノードを探す
115 | ///
116 | /// - parameter iBaseNode: 基準ノードのインデックス
117 | ///
118 | /// - returns: open 可能なノードのインデックス
119 | fileprivate func searchCanOpenNodeIndexes(_ iBaseNode: Int) -> [Int] {
120 | var checkCoordinates: [TileCoordinate] = []
121 | var indexes: [Int] = []
122 | let baseX = nodeList[iBaseNode].coordinates.x
123 | let baseY = nodeList[iBaseNode].coordinates.y
124 |
125 | // 基準ノードの上下左右のノードを調べる
126 | checkCoordinates.append(TileCoordinate(x: baseX - 1, y: baseY))
127 | checkCoordinates.append(TileCoordinate(x: baseX + 1, y: baseY))
128 | checkCoordinates.append(TileCoordinate(x: baseX, y: baseY - 1))
129 | checkCoordinates.append(TileCoordinate(x: baseX, y: baseY + 1))
130 |
131 | for coordinate in checkCoordinates {
132 | // 通行不可ならば,無視する
133 | if !canPass(coordinate) {
134 | continue
135 | }
136 | // ノードリストにノードとして存在するか
137 | let i_node = nodeList.index() {
138 | $0.coordinates == coordinate
139 | }
140 | if (i_node != nil) {
141 | if (nodeList[i_node!].state == Node.STATE.none) {
142 | indexes.append(i_node!)
143 | }
144 | } else {
145 | // ノードを新たに生成・追加
146 | let new_node = Node(coordinates: coordinate)
147 | nodeList.append(new_node)
148 | indexes.append(nodeList.count - 1)
149 | }
150 | }
151 |
152 | return indexes
153 | }
154 |
155 | /// 探索結果を取得する
156 | ///
157 | /// - returns: 移動経路を表すタイル座標の配列
158 | fileprivate func getAStarResult() -> [TileCoordinate] {
159 | var result: [TileCoordinate] = []
160 | var node: Node
161 |
162 | let index = nodeList.index() {
163 | $0.coordinates == destination
164 | }
165 | node = nodeList[index!]
166 | while node.parentNode != nil {
167 | result.append(node.coordinates)
168 | node = node.parentNode!
169 | }
170 |
171 | return result.reversed()
172 | }
173 |
174 | /// タイルの通行判定
175 | ///
176 | /// - parameter coordinate: タイルの座標
177 | ///
178 | /// - returns: 通行可能なら true, そうでなければ false
179 | fileprivate func canPass(_ coordinate: TileCoordinate) -> Bool {
180 | return self.map.canPass(coordinate)
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/A-star/Node.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Node.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2015/08/08.
6 | // Copyright © 2015年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import SpriteKit
12 |
13 | /// A*アルゴリズムのためのノード
14 | class Node {
15 |
16 | enum STATE {
17 | case none, open, closed
18 | }
19 |
20 | /// ステータス
21 | fileprivate(set) var state: STATE!
22 |
23 | /// XY座標
24 | fileprivate(set) var coordinates: TileCoordinate!
25 |
26 | /// 親ノード
27 | fileprivate(set) var parentNode: Node!
28 |
29 | /// 推定コスト
30 | fileprivate var heuristicCost: Int!
31 |
32 | /// 移動コスト
33 | fileprivate(set) var moveCost: Int!
34 |
35 | var score: Int {
36 | get {
37 | return self.heuristicCost + self.moveCost
38 | }
39 | }
40 |
41 | /// コンストラクタ
42 | ///
43 | /// - parameter coordinates: タイル座標
44 | init(coordinates: TileCoordinate) {
45 | self.state = STATE.none
46 | self.coordinates = TileCoordinate(x: coordinates.x,
47 | y: coordinates.y)
48 | }
49 |
50 | /// ノードを開く
51 | ///
52 | /// - parameter parentNode: 親ノード
53 | /// - parameter destination: 目的地のタイル座標
54 | func open(_ parentNode: Node?, destination: TileCoordinate) {
55 | // ノードをOpen状態にする
56 | self.state = STATE.open
57 |
58 | // 実コストを求める. スタート地点だった場合には 0
59 | if parentNode == nil {
60 | moveCost = 0
61 | } else {
62 | moveCost = parentNode!.moveCost + 1
63 | }
64 |
65 | // 推定コストを求める
66 | let dx = abs(destination.x - coordinates.x)
67 | let dy = abs(destination.y - coordinates.y)
68 | heuristicCost = dx + dy
69 |
70 | // 親ノードを保持する
71 | self.parentNode = parentNode
72 | }
73 |
74 | /// ノードを閉じる
75 | func close() {
76 | self.state = STATE.closed
77 | }
78 |
79 | /// ノードの現在位置を確認する
80 | ///
81 | /// - parameter coordinates: 確認する座標
82 | ///
83 | /// - returns: 指定した座標にノードが存在しなければ false, 存在すれば true
84 | func isPositioned(_ coordinates: TileCoordinate) -> Bool {
85 | if coordinates == coordinates {
86 | return true
87 | } else {
88 | return false
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/DataModel/DataModels.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DataModels.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/08/04.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Realm
11 | import RealmSwift
12 |
13 | class StoredItems: RealmSwift.Object {
14 | dynamic var key: String = ""
15 | dynamic var name: String = ""
16 | dynamic var text: String = ""
17 | dynamic var image_name: String = ""
18 | dynamic var num: Int = 0
19 |
20 | override static func primaryKey() -> String? {
21 | return "key"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Dialog/TalkBodyParser.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TalkBodyParser.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2016/02/26.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 |
12 | class TalkBodyParser {
13 | fileprivate var body: String!
14 |
15 | init?(talkFileName: String) {
16 | if let path: String = Bundle.main.path(forResource: talkFileName, ofType: nil),
17 | let fileHandle: FileHandle = FileHandle(forReadingAtPath: path)
18 | {
19 | let data: Data = fileHandle.readDataToEndOfFile()
20 | self.body = NSString(data:data, encoding:String.Encoding.utf8.rawValue) as! String
21 | } else {
22 | self.body = nil
23 | return nil
24 | }
25 | }
26 |
27 | /// パース状態
28 | ///
29 | /// - CONFIG: プレイヤー情報読み込み
30 | /// - BODY: 会話内容読み込み
31 | fileprivate enum PARSING {
32 | case config
33 | case body
34 | }
35 |
36 | func parse() -> JSON {
37 | var index: Int = 0
38 | var state: PARSING = .config
39 | var didReadBody = false
40 | var talksInfo = [[String: String]]()
41 | var talkInfo = [String: String]()
42 | var tmpTalkBody: String = ""
43 |
44 | self.body.enumerateLines {
45 | (line, stop) -> () in
46 |
47 | if line == "!" {
48 | talkInfo["talk_body"] = tmpTalkBody
49 | talksInfo.append(talkInfo)
50 | tmpTalkBody = ""
51 | index += 1
52 | state = .config
53 | didReadBody = false
54 | return
55 | }
56 |
57 | switch state {
58 | case .config:
59 | var config = line.characters.split(separator: ":").map{ String($0) }
60 | talkInfo["talker"] = config[0]
61 | talkInfo["talk_side"] = config[1]
62 | state = .body
63 | break
64 | case .body:
65 | if didReadBody {
66 | tmpTalkBody.append(line)
67 | return
68 | }
69 | tmpTalkBody.append(Dialog.NEWLINE_CHAR)
70 | tmpTalkBody.append(line)
71 | break
72 | }
73 | }
74 |
75 | return JSON(talksInfo)
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventDispatcher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Event.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2015/08/12.
6 | // Copyright © 2015年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 | import UIKit
12 | import PromiseKit
13 |
14 | enum EventDispacherError: Error {
15 | case FiledToInvokeListener(String)
16 | }
17 |
18 | protocol NotifiableFromListener {
19 | func invoke(_ listener: EventListener, invoker: EventListener)
20 | }
21 |
22 | class EventDispatcher : NotifiableFromListener {
23 | typealias ListenerType = EventListener
24 | typealias IdType = UInt64
25 |
26 | fileprivate var listeners = Dictionary()
27 | fileprivate var uniqueId: UInt64 = 0
28 |
29 | let triggerType: TriggerType
30 |
31 | var delegate: NotifiableFromDispacher?
32 |
33 | init(_ type: TriggerType) {
34 | self.triggerType = type
35 | }
36 |
37 | @discardableResult
38 | func add(_ listener: ListenerType) -> Bool {
39 | if listener.id != nil { return false }
40 | let id = issueId()
41 | listener.delegate = self
42 | listeners[id] = listener
43 | listener.id = id
44 | return true
45 | }
46 |
47 | @discardableResult
48 | func remove(_ listener: ListenerType, sender: GameSceneProtocol? = nil) -> Bool {
49 | if listener.id == nil { return false }
50 |
51 | do {
52 | try listener.rollback?(sender, nil).catch { error in
53 | // TODO
54 | }
55 | } catch {
56 | // TODO
57 | }
58 |
59 | let id = listener.id!
60 | listeners.removeValue(forKey: id)
61 | listener.id = nil
62 | self.delegate?.removed(id, sender: self)
63 |
64 | return true
65 | }
66 |
67 | func getAllListeners() -> [EventListener] {
68 | var listeners: [EventListener] = []
69 | for listener in self.listeners.values {
70 | listeners.append(listener)
71 | }
72 | return listeners
73 | }
74 |
75 | // Invoke all event listenrs in this dispacher.
76 | // If exception has thrown during executing, remove the listener which thrown exception.
77 | func trigger(_ sender: GameSceneProtocol!, args: JSON!) throws {
78 | for listener in listeners.values {
79 | if !listener.isExecuting {
80 | try listener.invoke!(sender, args).then { _ -> Void in
81 | // TODO:
82 | // Should use remove() function
83 | // But the function execute rollback()
84 | // Need to prepare different remove function which is not executing rollback()
85 | if let id_ = listener.id {
86 | self.listeners.removeValue(forKey: id_)
87 | listener.id = nil
88 | self.delegate?.removed(id_, sender: self)
89 | } else {
90 | // If the listener was removed before removing in this block, here is executed
91 | print("Failed to listener at the time of end of trigger")
92 | }
93 | }.catch { error in
94 | // TODO:
95 | }
96 | }
97 | }
98 | }
99 |
100 | func hasListener() -> Bool {
101 | return self.listeners.count > 0
102 | }
103 |
104 | fileprivate func issueId() -> IdType {
105 | repeat {
106 | uniqueId += 1
107 | if listeners[uniqueId] == nil {
108 | return uniqueId
109 | }
110 | } while (true) // ToDo
111 | }
112 |
113 | // MARK: NotifilableFromListener
114 |
115 | func invoke(_ nextListener: EventListener, invoker: EventListener) {
116 | self.delegate?.invoke(nextListener, invoker: invoker)
117 | }
118 | }
119 |
120 |
121 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EventListener.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2015/09/04.
6 | // Copyright © 2015年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 | import SpriteKit
12 | import PromiseKit
13 |
14 | enum EventListenerError: Error {
15 | case illegalArguementFormat(String)
16 | case illegalParamFormat([String])
17 | case invalidParam(String)
18 | }
19 |
20 | enum TriggerType {
21 | case touch
22 | case immediate
23 | case button
24 | }
25 |
26 | protocol GameSceneProtocol {
27 | init(size: CGSize, playerCoordiante: TileCoordinate, playerDirection: DIRECTION)
28 | var playerInitialCoordinate: TileCoordinate? { get }
29 | var playerInitialDirection: DIRECTION? { get }
30 |
31 | var actionButton: SKSpriteNode { get set }
32 | var menuButton: SKSpriteNode { get set }
33 | var eventDialog: SKSpriteNode { get set }
34 | var actionButtonLabel: SKLabelNode { get set }
35 | var menuButtonLabel: SKLabelNode { get set }
36 | var eventDialogLabel: SKLabelNode { get set }
37 | var map: Map? { get set }
38 | var textBox: Dialog! { get set }
39 |
40 | func movePlayer(_ actions: [SKAction], departure: TileCoordinate, destination: TileCoordinate, screenAction: SKAction, invoker: EventListener)
41 | -> Promise
42 | func moveObject(_ name: String, actions: [SKAction], departure: TileCoordinate, destination: TileCoordinate, invoker: EventListener)
43 | -> Promise
44 | func hideAllButtons() -> Promise
45 | func showDefaultButtons() -> Promise
46 | func showEventDialog() -> Promise
47 |
48 | func stopBehaviors()
49 | func startBehaviors()
50 | func enableWalking()
51 | func disableWalking()
52 | func removeAllEvetListenrs()
53 |
54 | func enableTouchEvents()
55 | func disableTouchEvents()
56 |
57 | func transitionTo(_ newScene: GameScene.Type, playerCoordinate: TileCoordinate, playerDirection: DIRECTION) -> Promise
58 | }
59 |
60 | typealias EventMethod = (_ sender: GameSceneProtocol?, _ args: JSON?) throws -> Promise
61 | protocol EventHandler: class {
62 | var invoke: EventMethod? { get set }
63 | var rollback: EventMethod? { get set }
64 | var triggerType: TriggerType { get }
65 | }
66 |
67 | typealias ListenerChain = [(listener: EventListener.Type, params: JSON?)]
68 | protocol EventListener: EventHandler {
69 | var id: UInt64! { get set }
70 | var delegate: NotifiableFromListener? { get set }
71 | var listeners: ListenerChain? { get }
72 | var isExecuting: Bool { get set }
73 | var isBehavior: Bool { get set }
74 | var eventObjectId: MapObjectId? { get set }
75 |
76 | func chain(listeners: ListenerChain)
77 | init(params: JSON?, chainListeners: ListenerChain?) throws
78 | }
79 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/ActivateButtonListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ActivateButtonListener.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/08/06.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 | import JSONSchema
12 | import SpriteKit
13 | import PromiseKit
14 |
15 | class ActivateButtonListener: EventListenerImplement {
16 | required init(params: JSON?, chainListeners listeners: ListenerChain?) throws {
17 | try! super.init(params: params, chainListeners: listeners)
18 |
19 | let schema = Schema([
20 | "type": "object",
21 | "properties": [
22 | "text": ["type": "string"],
23 | ],
24 | "required": ["text"],
25 | ])
26 | let result = schema.validate(params?.rawValue ?? [])
27 | if result.valid == false {
28 | throw EventListenerError.illegalParamFormat(result.errors!)
29 | }
30 |
31 | self.triggerType = .immediate
32 | self.invoke = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
33 | sender!.actionButtonLabel.text = self.params!["text"].string!
34 | sender!.actionButton.isHidden = false
35 |
36 | sender?.stopBehaviors()
37 |
38 | do {
39 | let nextEventListener = try InvokeNextEventListener(params: self.params, chainListeners: self.listeners)
40 | nextEventListener.eventObjectId = self.eventObjectId
41 | nextEventListener.isBehavior = self.isBehavior
42 | self.delegate?.invoke(nextEventListener, invoker: self)
43 | } catch {
44 | throw error
45 | }
46 |
47 | return Promise { fullfill, reject in fullfill() }
48 | }
49 | }
50 | }
51 |
52 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/BackToDefaultStateEventListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EnableWalkingEventListener.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2017/01/29.
6 | // Copyright © 2017年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 | import JSONSchema
12 | import SpriteKit
13 | import PromiseKit
14 |
15 | class BackToDefaultStateEventListener: EventListenerImplement {
16 | required init(params: JSON?, chainListeners listeners: ListenerChain?) throws {
17 | try! super.init(params: params, chainListeners: listeners)
18 | self.triggerType = .immediate
19 | self.invoke = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
20 |
21 | sender!.enableWalking()
22 | sender!.startBehaviors()
23 | sender!.menuButton.isHidden = false
24 |
25 | return Promise { fullfill, reject in fullfill() }
26 | }
27 | }
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/Event Dialog/HideEventDialogListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HideEventDialogListener.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2017/01/26.
6 | // Copyright © 2017年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 | import JSONSchema
12 | import SpriteKit
13 | import PromiseKit
14 |
15 | class HideEventDialogListener: EventListenerImplement {
16 | required init(params: JSON?, chainListeners listeners: ListenerChain?) {
17 | try! super.init(params: params, chainListeners: listeners)
18 |
19 | self.triggerType = .touch
20 | self.rollback = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
21 | sender?.eventDialog.isHidden = true
22 | return Promise { fullfill, reject in fullfill() }
23 | }
24 | self.invoke = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
25 | sender!.eventDialog.isHidden = true
26 |
27 | do {
28 | let nextEventListener = try InvokeNextEventListener(params: self.params, chainListeners: self.listeners)
29 | nextEventListener.eventObjectId = self.eventObjectId
30 | nextEventListener.isBehavior = self.isBehavior
31 | self.delegate?.invoke(nextEventListener, invoker: self)
32 | } catch {
33 | throw error
34 | }
35 |
36 | return Promise { fullfill, reject in fullfill() }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/Event Dialog/ShowEventDialogListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ActivateEventDialogListener.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2017/01/23.
6 | // Copyright © 2017年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 | import JSONSchema
12 | import SpriteKit
13 | import PromiseKit
14 |
15 | class ShowEventDialogListener: EventListenerImplement {
16 | required init(params: JSON?, chainListeners listeners: ListenerChain?) throws {
17 | try! super.init(params: params, chainListeners: listeners)
18 |
19 | let schema = Schema([
20 | "type": "object",
21 | "properties": [
22 | "text": ["type": "string"],
23 | ],
24 | "required": ["text"],
25 | ])
26 | let result = schema.validate(params?.rawValue ?? [])
27 | if result.valid == false {
28 | throw EventListenerError.illegalParamFormat(result.errors!)
29 | }
30 |
31 | self.triggerType = .immediate
32 | self.rollback = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
33 | sender?.eventDialog.isHidden = true
34 | return Promise { fullfill, reject in fullfill() }
35 | }
36 | self.invoke = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
37 | sender!.eventDialogLabel.text = params!["text"].string!
38 | sender!.eventDialog.isHidden = false
39 |
40 | // Stop All Object's behavior
41 | sender?.stopBehaviors()
42 | sender?.disableWalking()
43 |
44 | let nextEventListener = HideEventDialogListener(params: self.params, chainListeners: self.listeners)
45 | nextEventListener.eventObjectId = self.eventObjectId
46 | nextEventListener.isBehavior = self.isBehavior
47 | self.delegate?.invoke(nextEventListener, invoker: self)
48 |
49 | return Promise { fullfill, reject in fullfill() }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/EventListenerImplement.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EventListenerImplement.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2017/01/29.
6 | // Copyright © 2017年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 | import JSONSchema
12 | import SpriteKit
13 | import PromiseKit
14 |
15 | class EventListenerImplement: EventListener {
16 | var id: UInt64!
17 | var delegate: NotifiableFromListener? = nil
18 | var invoke: EventMethod? = nil
19 | var rollback: EventMethod? = nil
20 | var listeners: ListenerChain? = nil
21 | var params: JSON? = nil
22 | var eventObjectId: MapObjectId? = nil
23 | var isExecuting: Bool = false
24 | var isBehavior: Bool = false
25 | var triggerType: TriggerType
26 |
27 | required init(params: JSON?, chainListeners listeners: ListenerChain?) throws {
28 | self.params = params
29 | self.listeners = listeners
30 | self.triggerType = .immediate
31 | }
32 |
33 | internal func chain(listeners: ListenerChain) {
34 | self.listeners = listeners
35 | }
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/InvokeNextEventListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InvokeNextEventListener.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/12/23.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 | import PromiseKit
12 |
13 | class InvokeNextEventListener: EventListenerImplement {
14 | required init(params: JSON?, chainListeners listeners: ListenerChain?) throws {
15 | try! super.init(params: params, chainListeners: listeners)
16 |
17 | self.triggerType = .immediate
18 | self.invoke = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
19 | // If there are no registered listener, exit
20 | if listeners == nil || listeners?.count == 0 {
21 | return Promise { fullfill, reject in fullfill() }
22 | }
23 |
24 | // If there are registered listener, invoke it
25 | let nextListener = listeners!.first!.listener
26 | let nextListenerChain: ListenerChain? = listeners!.count == 1 ? nil : Array(listeners!.dropFirst())
27 | do {
28 | let nextListenerInstance = try nextListener.init(params: listeners!.first!.params, chainListeners: nextListenerChain)
29 | nextListenerInstance.eventObjectId = self.eventObjectId
30 | nextListenerInstance.isBehavior = self.isBehavior
31 | self.delegate?.invoke(nextListenerInstance, invoker: self)
32 | } catch {
33 | throw error
34 | }
35 |
36 | return Promise { fullfill, reject in fullfill() }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/ItemGetEventListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ItemGetEvent.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/08/04.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 | import JSONSchema
12 | import SpriteKit
13 | import RealmSwift
14 | import PromiseKit
15 |
16 | class ItemGetEventListener: EventListener {
17 | var id: UInt64!
18 | var delegate: NotifiableFromListener?
19 | var invoke: EventMethod?
20 | var rollback: EventMethod?
21 | var isExecuting: Bool = false
22 | var isBehavior: Bool = false
23 | var eventObjectId: MapObjectId? = nil
24 | let triggerType: TriggerType
25 |
26 | internal var listeners: ListenerChain?
27 | fileprivate let params: JSON
28 | fileprivate let itemKey: String
29 | fileprivate let itemName: String
30 | fileprivate let itemText: String
31 | fileprivate let itemImageName: String
32 |
33 | required init(params: JSON?, chainListeners listeners: ListenerChain?) throws {
34 |
35 | let schema = Schema([
36 | "type": "object",
37 | "properties": [
38 | "key": ["type": "string"],
39 | "name": ["type": "string"],
40 | "description": ["type": "string"],
41 | "image_name": ["type": "string"],
42 | ],
43 | "required": ["key", "name", "description", "image_name"],
44 | ])
45 | let result = schema.validate(params?.rawValue ?? [])
46 | if result.valid == false {
47 | throw EventListenerError.illegalParamFormat(result.errors!)
48 | }
49 |
50 | self.triggerType = .immediate
51 | self.params = params!
52 | self.listeners = listeners
53 | self.itemKey = params!["key"].string!
54 | self.itemName = params!["name"].string!
55 | self.itemText = params!["description"].string!
56 | self.itemImageName = params!["image_name"].string!
57 | self.invoke = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
58 | // Insert data to database
59 | let realm = try! Realm()
60 | try! realm.write {
61 | var item = realm.objects(StoredItems.self).filter("key == \"\(self.itemKey)\"").first
62 | if item != nil {
63 | item!.num += 1
64 | } else {
65 | item = StoredItems()
66 | item!.key = self.itemKey
67 | item!.name = self.itemName
68 | item!.text = self.itemText
69 | item!.image_name = self.itemImageName
70 | }
71 | realm.add(item!, update: true)
72 | }
73 |
74 | do {
75 | let nextEventListener = try InvokeNextEventListener(params: self.params, chainListeners: self.listeners)
76 | nextEventListener.eventObjectId = self.eventObjectId
77 | nextEventListener.isBehavior = self.isBehavior
78 | self.delegate?.invoke(nextEventListener, invoker: self)
79 | } catch {
80 | throw error
81 | }
82 |
83 | return Promise { fullfill, reject in fullfill() }
84 | }
85 | }
86 |
87 | internal func chain(listeners: ListenerChain) {
88 | self.listeners = listeners
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/MoveObjectEventListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MoveObjectEventListener.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2017/01/23.
6 | // Copyright © 2017年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import SpriteKit
12 | import SwiftyJSON
13 | import JSONSchema
14 | import PromiseKit
15 |
16 | class MoveObjectEventListener: EventListenerImplement {
17 | required init(params: JSON?, chainListeners listeners: ListenerChain?) throws {
18 | try! super.init(params: params, chainListeners: listeners)
19 |
20 | let schema = Schema([
21 | "type": "object",
22 | "properties": [
23 | "name": ["type": "string"],
24 | "direction": [
25 | "type":"string",
26 | "enum": ["LEFT", "RIGHT", "UP", "DOWN"]
27 | ],
28 | "step_num": ["type":"string"],
29 | "speed": ["type":"string"]
30 | ],
31 | "required": ["name", "direction", "step_num", "speed"],
32 | ])
33 | let result = schema.validate(params?.rawValue ?? [])
34 | if result.valid == false {
35 | throw EventListenerError.illegalParamFormat(result.errors!)
36 | }
37 | // TODO: Validation as following must be executed as a part of validation by JSONSchema
38 | if (Int(params!["step_num"].string!) == nil) {
39 | throw EventListenerError.illegalParamFormat(["The parameter 'step_num' couldn't convert to integer"])
40 | }
41 | if (Int(params!["speed"].string!) == nil) {
42 | throw EventListenerError.illegalParamFormat(["The parameter 'speed' couldn't convert to integer"])
43 | }
44 |
45 | self.triggerType = .immediate
46 | self.invoke = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
47 | self.isExecuting = true
48 |
49 | let map = sender!.map!
50 |
51 | let objectName = self.params?["name"].string!
52 | let direction = DIRECTION.fromString((self.params?["direction"].string!)!)
53 | let step_num = Int((self.params?["step_num"].string!)!)
54 | let speed = Int((self.params?["speed"].string!)!)
55 |
56 | let object = map.getObjectByName(objectName!)!
57 | object.setDirection(direction!)
58 | object.setSpeed(CGFloat(speed!))
59 |
60 | // Route search by A* algorithm
61 | let departure = object.coordinate
62 | let destination = self.calcDestination(departure, direction: direction!, step_num: step_num!)
63 |
64 | let aStar = AStar(map: map)
65 | aStar.initialize(departure, destination: destination)
66 | let route = aStar.main()
67 | if route == nil {
68 | // If there are no route to destination, finish this listener and invoke next listener
69 | do {
70 | let nextEventListener = try InvokeNextEventListener(params: self.params, chainListeners: self.listeners)
71 | nextEventListener.eventObjectId = self.eventObjectId
72 | nextEventListener.isBehavior = self.isBehavior
73 | self.delegate?.invoke(nextEventListener, invoker: self)
74 | } catch {
75 | throw error
76 | }
77 | return Promise { fullfill, reject in fullfill() }
78 | }
79 |
80 | // Disable events
81 | // Remove all events related to object from map
82 | map.removeEventsOfObject(object.id)
83 |
84 | // Define action for moving
85 | var objectActions: Array = []
86 | var preStepCoordinate = object.coordinate
87 | var preStepPoint = object.position
88 | for nextStepCoordinate: TileCoordinate in route! {
89 | let nextStepPoint: CGPoint = TileCoordinate.getSheetCoordinateFromTileCoordinate(nextStepCoordinate)
90 | objectActions += object.getActionTo(
91 | preStepPoint,
92 | destination: nextStepPoint,
93 | preCallback: {
94 | map.setCollisionOn(coordinate: nextStepCoordinate)
95 | },
96 | postCallback: {
97 | map.removeCollisionOn(coordinate: nextStepCoordinate)
98 | map.updateObjectPlacement(object, departure: preStepCoordinate, destination: nextStepCoordinate)
99 | }
100 | )
101 |
102 | preStepCoordinate = nextStepCoordinate
103 | preStepPoint = nextStepPoint
104 | }
105 |
106 | return Promise { fullfill, reject in
107 | firstly {
108 | sender!.moveObject(
109 | objectName!,
110 | actions: objectActions,
111 | departure: departure,
112 | destination: destination,
113 | invoker: self
114 | )
115 | }.then { _ -> Void in
116 | do {
117 | let nextEventListener = try InvokeNextEventListener(params: self.params, chainListeners: self.listeners)
118 | nextEventListener.eventObjectId = self.eventObjectId
119 | nextEventListener.isBehavior = self.isBehavior
120 | self.delegate?.invoke(nextEventListener, invoker: self)
121 | } catch {
122 | throw error
123 | }
124 | }.then {
125 | fullfill()
126 | }.catch { error in
127 | print(error.localizedDescription)
128 | }
129 | }
130 | }
131 | }
132 |
133 | fileprivate func calcDestination(_ departure: TileCoordinate, direction: DIRECTION, step_num: Int) -> TileCoordinate {
134 | var diff: TileCoordinate = TileCoordinate(x: 0, y: 0)
135 | switch direction {
136 | case .up:
137 | diff = diff + TileCoordinate(x: 0, y: 1)
138 | case .down:
139 | diff = diff + TileCoordinate(x: 0, y: -1)
140 | case .right:
141 | diff = diff + TileCoordinate(x: 1, y: 0)
142 | case .left:
143 | diff = diff + TileCoordinate(x: -1, y: 0)
144 | }
145 | return (departure + diff)
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/ReloadBehaviorEventListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoadBehaviorEventListener.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2017/01/28.
6 | // Copyright © 2017年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 | import JSONSchema
12 | import SpriteKit
13 | import PromiseKit
14 |
15 | class ReloadBehaviorEventListener: EventListenerImplement {
16 | required init(params: JSON?, chainListeners listeners: ListenerChain?) throws {
17 | try! super.init(params: params, chainListeners: listeners)
18 |
19 | let schema = Schema([
20 | "type": "object",
21 | "properties": [
22 | "text": ["eventObjectId": "int"],
23 | ],
24 | "required": ["eventObjectId"],
25 | ])
26 | let result = schema.validate(params?.rawValue ?? [])
27 | if result.valid == false {
28 | throw EventListenerError.illegalParamFormat(result.errors!)
29 | }
30 | if params?["eventObjectId"].int == nil {
31 | throw EventListenerError.illegalParamFormat(["parameter 'eventObjectId' cannot convert to int"])
32 | }
33 |
34 | self.triggerType = .immediate
35 | self.invoke = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
36 | let map = sender!.map!
37 | let id = self.params?["eventObjectId"].int!
38 |
39 | // TODO: Implement utility function for generate listener from behavior listener chain
40 | var listener: EventListener? = nil
41 | let listenerChain = map.getObjectBehavior(id!)
42 | let listenerType = listenerChain?.first?.listener
43 | let params = listenerChain?.first?.params
44 | do {
45 | listener = try listenerType?.init(
46 | params: params,
47 | chainListeners: ListenerChain(listenerChain!.dropFirst(1)))
48 | listener?.eventObjectId = id
49 | listener?.isBehavior = true
50 | } catch {
51 | // TODO
52 | }
53 |
54 | self.delegate?.invoke(listener!, invoker: self)
55 |
56 | return Promise { fullfill, reject in fullfill() }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/RenderDefaultViewEventListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RenderDefaultViewEventListener.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2017/01/23.
6 | // Copyright © 2017年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 | import JSONSchema
12 | import SpriteKit
13 | import PromiseKit
14 |
15 | class RenderDefaultViewEventListener: EventListenerImplement {
16 | required init(params: JSON?, chainListeners listeners: ListenerChain?) throws {
17 | try! super.init(params: params, chainListeners: listeners)
18 |
19 | self.triggerType = .immediate
20 | self.invoke = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
21 | self.isExecuting = true
22 |
23 | return Promise { fullfill, reject in
24 | firstly {
25 | sender!.hideAllButtons()
26 | }.then { _ in
27 | sender!.showDefaultButtons()
28 | }.then { _ -> Void in
29 | do {
30 | let nextEventListener = try InvokeNextEventListener(params: self.params, chainListeners: self.listeners)
31 | nextEventListener.eventObjectId = self.eventObjectId
32 | nextEventListener.isBehavior = self.isBehavior
33 | self.delegate?.invoke(nextEventListener, invoker: self)
34 | } catch {
35 | throw error
36 | }
37 | }.then {
38 | fullfill()
39 | }.catch { error in
40 | print(error.localizedDescription)
41 | }
42 | }
43 | }
44 | }
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/SceneTransitionEventListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneTransitionEventListener.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2017/01/29.
6 | // Copyright © 2017年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 | import JSONSchema
12 | import SpriteKit
13 | import PromiseKit
14 |
15 | class SceneTransitionEventListener: EventListenerImplement {
16 | required init(params: JSON?, chainListeners listeners: ListenerChain?) throws {
17 | try! super.init(params: params, chainListeners: listeners)
18 |
19 | let schema = Schema([
20 | "type": "object",
21 | "properties": [
22 | "map_file_name": ["type": "string"],
23 | "player_place_coordinate": ["type": "string"],
24 | "player_direction": [
25 | "type":"string",
26 | "enum": ["LEFT", "RIGHT", "UP", "DOWN"]
27 | ]
28 | ],
29 | "required": ["map_file_name", "player_place_coordinate", "player_direction"],
30 | ])
31 | let result = schema.validate(params?.rawValue ?? [])
32 | if result.valid == false {
33 | throw EventListenerError.illegalParamFormat(result.errors!)
34 | }
35 |
36 | self.triggerType = .immediate
37 | self.invoke = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
38 | let mapFileName = params!["map_file_name"].string!
39 | let playerCoordinate = self.convertToCoordinate(params!["player_place_coordinate"].string!)
40 | let playerDirection = DIRECTION.fromString((self.params?["player_direction"].string!)!)
41 |
42 | // Clean event manager: remove all event listener from event manager
43 | sender?.removeAllEvetListenrs()
44 | sender?.disableTouchEvents()
45 |
46 | // Scene transition
47 | let gameSceneType = MapTable.fromJsonFileName[mapFileName]
48 |
49 | return Promise { fullfill, reject in
50 | firstly {
51 | sender!.transitionTo(gameSceneType!, playerCoordinate: playerCoordinate!, playerDirection: playerDirection!)
52 | }.then { _ -> Void in
53 | sender!.enableTouchEvents()
54 | }.then {
55 | fullfill()
56 | }.catch { error in
57 | print(error.localizedDescription)
58 | }
59 | }
60 | }
61 | }
62 |
63 | fileprivate func convertToCoordinate(_ string: String) -> TileCoordinate? {
64 | let coordinates = string.components(separatedBy: "-")
65 | if let x = Int(coordinates[0]),
66 | let y = Int(coordinates[1]) {
67 | return TileCoordinate(x: x, y: y)
68 | }
69 | return nil
70 | }
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/Talk Event/FinishTalkEventListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FinishTalkEventListener.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2017/01/26.
6 | // Copyright © 2017年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 | import JSONSchema
12 | import SpriteKit
13 | import PromiseKit
14 |
15 | class FinishTalkEventListener: EventListenerImplement {
16 | required init(params: JSON?, chainListeners listeners: ListenerChain?) throws {
17 | try! super.init(params: params, chainListeners: listeners)
18 |
19 | self.triggerType = .touch
20 | self.rollback = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
21 | sender?.actionButton.isHidden = true
22 | sender?.textBox.hide()
23 | return Promise { fullfill, reject in fullfill() }
24 | }
25 | self.invoke = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
26 | self.isExecuting = true
27 |
28 | sender!.textBox.clean()
29 |
30 | return Promise { fullfill, reject in
31 | firstly {
32 | sender!.textBox.hide(duration: 0)
33 | }.then { _ -> Void in
34 | do {
35 | let nextEventListener = try InvokeNextEventListener(params: self.params, chainListeners: self.listeners)
36 | nextEventListener.eventObjectId = self.eventObjectId
37 | nextEventListener.isBehavior = self.isBehavior
38 | self.delegate?.invoke(nextEventListener, invoker: self)
39 | } catch {
40 | throw error
41 | }
42 | }.then {
43 | fullfill()
44 | }.catch { error in
45 | print(error.localizedDescription)
46 | }
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/Talk Event/StartTalkEventListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StartTalkEventListener.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2017/01/26.
6 | // Copyright © 2017年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 | import JSONSchema
12 | import SpriteKit
13 | import PromiseKit
14 |
15 | class StartTalkEventListener: EventListenerImplement {
16 | fileprivate var directionString: String
17 |
18 | required init(params: JSON?, chainListeners listeners: ListenerChain?) throws {
19 | if params == nil { throw EventListenerError.illegalParamFormat(["Parameter is nil"]) }
20 |
21 | let maxIndex = params?.arrayObject?.count
22 | if maxIndex == nil { throw EventListenerError.illegalParamFormat(["No properties in json parameter"]) }
23 |
24 | let directionString = params![maxIndex!-1]["direction"].string
25 | // Remove "direction" from params
26 | var array = params!.arrayObject as? [[String:String]]
27 | array?.removeLast()
28 |
29 | self.directionString = directionString!
30 |
31 | try! super.init(params: params, chainListeners: listeners)
32 |
33 | self.triggerType = .button
34 | self.params = JSON(array!)
35 | self.rollback = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
36 | sender?.actionButton.isHidden = true
37 | sender?.textBox.hide()
38 | sender?.startBehaviors()
39 | return Promise { fullfill, reject in fullfill() }
40 | }
41 | self.invoke = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
42 | self.isExecuting = true
43 |
44 | // Initialize dialog
45 | sender?.textBox.clean()
46 |
47 | // Stop All Object's behavior
48 | sender?.stopBehaviors()
49 | sender?.disableWalking()
50 |
51 | // Change direction of player
52 | let map = sender!.map!
53 | let player = map.getObjectByName(objectNameTable.PLAYER_NAME)
54 | if let playerDirection = DIRECTION.fromString(self.directionString) {
55 | player?.setDirection(playerDirection)
56 | }
57 |
58 | return Promise { fullfill, reject in
59 | firstly {
60 | sender!.hideAllButtons()
61 | }.then {
62 | sender!.textBox.show(duration: 0.2)
63 | }.then { _ -> Void in
64 | do {
65 | // Render next conversation
66 | let moveConversation = try TalkEventListener.generateMoveConversationMethod(0, params: self.params!)
67 | try moveConversation(_: sender, args).catch { error in
68 | // TODO
69 | }
70 |
71 | // TODO: If there are no need to invoke following (i.e. The conversation would finish in only one(above) step),
72 | // should deal with it well.
73 | let nextEventListener = try TalkEventListener(params: self.params, chainListeners: self.listeners)
74 | nextEventListener.eventObjectId = self.eventObjectId
75 | nextEventListener.isBehavior = self.isBehavior
76 | self.delegate?.invoke(nextEventListener, invoker: self)
77 | } catch {
78 | throw error
79 | }
80 | }.then {
81 | fullfill()
82 | }.catch { error in
83 | print(error.localizedDescription)
84 | }
85 | }
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/Talk Event/TalkEventListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TalkEventListeners.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/08/04.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 | import JSONSchema
12 | import SpriteKit
13 | import PromiseKit
14 |
15 | class TalkEventListener: EventListenerImplement {
16 | fileprivate var index: Int? = nil
17 | fileprivate var talkContentsMaxNum: Int? = nil
18 |
19 | required convenience init(params: JSON?, chainListeners listeners: ListenerChain?) throws {
20 | do {
21 | try self.init(params: params, chainListeners: listeners, index: 1)
22 | } catch {
23 | throw error
24 | }
25 | }
26 |
27 | init(params: JSON?, chainListeners listeners: ListenerChain?, index: Int) throws {
28 | try! super.init(params: params, chainListeners: listeners)
29 |
30 | // TODO:
31 | // The truth is validation as follows should be executed by JSONSchema,
32 | // but JSONSchema doesn't work well with nested JSON.
33 | // This is because that determine whether it's object or not by whether it's castable to NSDicationary
34 | // let schema = Schema([
35 | // "type": "object",
36 | // "minProperties": 1,
37 | // ])
38 |
39 | if params == nil { throw EventListenerError.illegalParamFormat(["Parameter is nil"]) }
40 |
41 | let maxIndex = params?.arrayObject?.count
42 | if maxIndex == nil { throw EventListenerError.illegalParamFormat(["No properties in json parameter"]) }
43 |
44 | self.index = index
45 | self.triggerType = .touch
46 | // TODO: The value of following variable is determined by the number of property.
47 | // It might have to be defined specifically.
48 | // a number of conversation times
49 | self.talkContentsMaxNum = (params?.arrayObject?.count)!
50 | self.rollback = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
51 | sender?.actionButton.isHidden = true
52 | sender?.textBox.hide()
53 | return Promise { fullfill, reject in fullfill() }
54 | }
55 | self.invoke = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
56 | do {
57 | let moveConversation = try TalkEventListener.generateMoveConversationMethod(self.index!, params: self.params!)
58 | try moveConversation(sender, args).catch { error in
59 | // TODO
60 | }
61 |
62 | // Determine the Event Listener which executed in next
63 | // depending on whether conversation is continued or not.
64 | let nextEventListener: EventListener
65 | if index < self.talkContentsMaxNum! - 1 {
66 | nextEventListener = try TalkEventListener(params: self.params, chainListeners: self.listeners, index: self.index!+1)
67 | } else {
68 | nextEventListener = try FinishTalkEventListener(params: self.params, chainListeners: self.listeners)
69 | }
70 | nextEventListener.eventObjectId = self.eventObjectId
71 | nextEventListener.isBehavior = self.isBehavior
72 |
73 | self.delegate?.invoke(nextEventListener, invoker: self)
74 | } catch {
75 | throw error
76 | }
77 |
78 | return Promise { fullfill, reject in fullfill() }
79 | }
80 | }
81 |
82 | static func generateMoveConversationMethod(_ index: Int, params: JSON) throws -> EventMethod {
83 | let schema = Schema([
84 | "type": "object",
85 | "properties": [
86 | "talker": ["type": "string"],
87 | "talk_body": ["type": "string"],
88 | "talk_side": [
89 | "type": "string",
90 | "enum": ["L", "R"]
91 | ],
92 | ],
93 | "required": ["talker", "talk_body", "talk_side"],
94 | ])
95 | let result = schema.validate(params[index].rawValue)
96 | if result.valid == false {
97 | throw EventListenerError.illegalParamFormat(result.errors!)
98 | }
99 | if TALKER_IMAGE.index(forKey: params[index]["talker"].string!) == nil {
100 | throw EventListenerError.invalidParam("Talker image name specified at param `talker` is not declared in configuration file")
101 | }
102 |
103 | let talker = params[index]["talker"].string!
104 | let talkBody = params[index]["talk_body"].string!
105 | let talkSideString = params[index]["talk_side"].string!
106 | let talkSide = talkSideString == "L" ? Dialog.TALK_SIDE.left : Dialog.TALK_SIDE.right
107 | let talkerImageName = TALKER_IMAGE[talker]
108 |
109 | return { sender, args in
110 | sender!.textBox.drawText(talkerImageName, body: talkBody, side: talkSide)
111 | return Promise { fullfill, reject in fullfill() }
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/WaitEventListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WaitEventListener.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2017/01/25.
6 | // Copyright © 2017年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import SpriteKit
12 | import SwiftyJSON
13 | import JSONSchema
14 | import PromiseKit
15 |
16 | class WaitEventListener: EventListenerImplement {
17 | required init(params: JSON?, chainListeners listeners: ListenerChain?) throws {
18 | try! super.init(params: params, chainListeners: listeners)
19 |
20 | let schema = Schema([
21 | "type": "object",
22 | "properties": [
23 | "time": ["type": "string"]
24 | ],
25 | "required": ["time"],
26 | ]
27 | )
28 | let result = schema.validate(params?.rawValue ?? [])
29 | if result.valid == false {
30 | throw EventListenerError.illegalParamFormat(result.errors!)
31 | }
32 | // TODO: Validation as following must be executed as a part of validation by JSONSchema
33 | if (Int(params!["time"].string!) == nil) {
34 | throw EventListenerError.illegalParamFormat(["The parameter 'time' couldn't convert to integer"])
35 | }
36 |
37 | self.triggerType = .immediate
38 | self.invoke = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
39 | self.isExecuting = true
40 |
41 | let map = sender!.map!
42 | let time = Int((self.params?["time"].string!)!)!
43 |
44 | return Promise { fullfill, reject in
45 | firstly {
46 | self.generatePromiseClojureForWaiting(map: map, time: time)
47 | }.then { _ -> Void in
48 | do {
49 | let nextEventListener = try InvokeNextEventListener(params: self.params, chainListeners: self.listeners)
50 | nextEventListener.eventObjectId = self.eventObjectId
51 | nextEventListener.isBehavior = self.isBehavior
52 | self.delegate?.invoke(nextEventListener, invoker: self)
53 | } catch {
54 | throw error
55 | }
56 | }.then {
57 | fullfill()
58 | }.catch { error in
59 | print(error.localizedDescription)
60 | }
61 | }
62 | }
63 | }
64 |
65 | // TODO: Currently, for delaying animated, run delay action for Map SKSpriteNode.
66 | // This might be problem if we wanted to animate map.
67 | fileprivate func generatePromiseClojureForWaiting(map: Map, time: Int) -> Promise {
68 | return Promise {
69 | fullfill, reject in
70 | map.wait(time, callback: { () in fullfill() })
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/WalkEventListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TouchEventListener.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/07/29.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import SpriteKit
12 | import SwiftyJSON
13 | import JSONSchema
14 | import PromiseKit
15 |
16 | /// プレイヤー移動のリスナー
17 | class WalkEventListener: EventListenerImplement {
18 | required init(params: JSON?, chainListeners listeners: ListenerChain?) {
19 | try! super.init(params: params, chainListeners: listeners)
20 |
21 | self.triggerType = .touch
22 | self.invoke = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
23 | let schema = Schema([
24 | "type": "object",
25 | "properties": [
26 | "touchedPoint": ["type": "string"],
27 | ],
28 | "required": ["touchedPoint"],
29 | ])
30 | let result = schema.validate(args?.rawValue ?? [])
31 | if result.valid == false {
32 | throw EventListenerError.illegalParamFormat(result.errors!)
33 | }
34 | // TODO: The following method end up throw exception even in the case the 'touchedPoint' value is (0,0).
35 | if CGPointFromString((args?["touchedPoint"].string)!) == CGPoint.init(x: 0, y: 0) {
36 | throw EventListenerError.illegalParamFormat(["The parameter 'touchedPoint' isn't castable to CGPoint."])
37 | }
38 |
39 | let map = sender!.map!
40 | let sheet = map.sheet!
41 |
42 | let touchedPoint = CGPointFromString((args?["touchedPoint"].string)!)
43 | let player = map.getObjectByName(objectNameTable.PLAYER_NAME)!
44 | let departure = TileCoordinate.getTileCoordinateFromSheetCoordinate(player.position)
45 | let destination = TileCoordinate.getTileCoordinateFromScreenCoordinate(
46 | sheet.getSheetPosition(),
47 | screenCoordinate: touchedPoint
48 | )
49 |
50 | // Route search
51 | let aStar = AStar(map: map)
52 | aStar.initialize(departure, destination: destination)
53 | let path = aStar.main()
54 | if path == nil {
55 | let nextEventListener = WalkEventListener.init(params: nil, chainListeners: nil)
56 | nextEventListener.eventObjectId = self.eventObjectId
57 | nextEventListener.isBehavior = self.isBehavior
58 | self.delegate?.invoke(nextEventListener, invoker: self)
59 | return Promise { fullfill, reject in fullfill() }
60 | }
61 |
62 | // Generate event listener chain for walking animation
63 | var chain: ListenerChain = []
64 | for step: TileCoordinate in path! {
65 | chain = chain + self.generateOneStepWalkListener(step)
66 | }
67 |
68 | let nextListener = chain.first!.listener
69 | let nextListenerChain: ListenerChain? = chain.count == 1 ? nil : Array(chain.dropFirst())
70 | do {
71 | let nextListenerInstance = try nextListener.init(params: chain.first!.params, chainListeners: nextListenerChain)
72 | nextListenerInstance.eventObjectId = self.eventObjectId
73 | nextListenerInstance.isBehavior = self.isBehavior
74 | self.delegate?.invoke(nextListenerInstance, invoker: self)
75 | } catch {
76 | throw error
77 | }
78 |
79 | return Promise { fullfill, reject in fullfill() }
80 | }
81 | }
82 |
83 | fileprivate func generateOneStepWalkListener(_ destination: TileCoordinate) -> ListenerChain {
84 | return [ (listener: WalkOneStepEventListener.self, params: JSON(["destination": destination.description])) ]
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventListener/WalkOneStepEventListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WalkOneStepEventListener.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2017/01/26.
6 | // Copyright © 2017年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import SpriteKit
12 | import SwiftyJSON
13 | import JSONSchema
14 | import PromiseKit
15 |
16 | class WalkOneStepEventListener: EventListenerImplement {
17 | required init(params: JSON?, chainListeners listeners: ListenerChain?) throws {
18 | try! super.init(params: params, chainListeners: listeners)
19 |
20 | let schema = Schema([
21 | "type": "object",
22 | "properties": [
23 | "destination": ["type": "string"],
24 | ],
25 | "required": ["destination"],
26 | ])
27 | let result = schema.validate(params?.rawValue ?? [])
28 | if result.valid == false {
29 | throw EventListenerError.illegalParamFormat(result.errors!)
30 | }
31 |
32 | self.triggerType = .immediate
33 | self.invoke = { (sender: GameSceneProtocol?, args: JSON?) -> Promise in
34 | self.isExecuting = true
35 |
36 | let map = sender!.map!
37 | let sheet = map.sheet!
38 |
39 | let player = map.getObjectByName(objectNameTable.PLAYER_NAME)!
40 | let destination = TileCoordinate.parse(from: (self.params?["destination"].string)!)
41 |
42 | // Generate SKAction for moving
43 | let action = player.getActionTo(
44 | player.position,
45 | destination: TileCoordinate.getSheetCoordinateFromTileCoordinate(destination),
46 | preCallback: {
47 | map.setCollisionOn(coordinate: destination)
48 | },
49 | postCallback: {
50 | map.removeCollisionOn(coordinate: destination)
51 | map.updateObjectPlacement(player, departure: player.coordinate, destination: destination)
52 | }
53 | )
54 |
55 | let screenAction = sheet.getActionTo(
56 | player.position,
57 | destination: TileCoordinate.getSheetCoordinateFromTileCoordinate(destination),
58 | speed: player.speed
59 | )
60 |
61 | // If player can't reach destination tile because of collision, stop
62 | if !map.canPass(destination) {
63 | self.delegate?.invoke(WalkEventListener.init(params: nil, chainListeners: nil), invoker: self)
64 | return Promise { fullfill, reject in fullfill() }
65 | }
66 |
67 | return Promise { fullfill, reject in
68 | firstly {
69 | sender!.movePlayer(
70 | action,
71 | departure: player.coordinate,
72 | destination: destination,
73 | screenAction: screenAction,
74 | invoker: self
75 | )
76 | }.then { _ -> Void in
77 | // If reached at destination, stop walking and set WalkEvetListener as touch event again
78 | if self.listeners == nil || self.listeners?.count == 0
79 | || map.getEventsOn(destination).isEmpty == false {
80 | let nextEventListener = WalkEventListener.init(params: nil, chainListeners: nil)
81 | nextEventListener.eventObjectId = self.eventObjectId
82 | nextEventListener.isBehavior = self.isBehavior
83 | self.delegate?.invoke(nextEventListener, invoker: self)
84 | return
85 | }
86 |
87 | // If player don't reach at destination, invoke next step animation listener
88 | let nextListener = self.listeners!.first!.listener
89 | let nextListenerChain: ListenerChain? = self.listeners!.count == 1 ? nil : Array(self.listeners!.dropFirst())
90 | do {
91 | let nextListenerInstance = try nextListener.init(params: self.listeners!.first!.params, chainListeners: nextListenerChain)
92 | nextListenerInstance.isBehavior = self.isBehavior
93 | self.delegate?.invoke(nextListenerInstance, invoker: self)
94 | } catch {
95 | throw error
96 | }
97 | }.then {
98 | fullfill()
99 | }.catch { error in
100 | print(error.localizedDescription)
101 | }
102 | }
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Event/EventManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EventManager.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/08/02.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 |
12 | protocol NotifiableFromDispacher {
13 | func invoke(_ listener: EventListener, invoker: EventListener)
14 | func removed(_ listenerId: UInt64, sender: EventDispatcher)
15 | }
16 |
17 | protocol UnavailabledCyclicEventIdsContainable {
18 | var unavailabledCyclicEventIds: [UInt64] { get }
19 | }
20 |
21 | enum EventManagerError: Error {
22 | case FailedToTrigger(String)
23 | }
24 |
25 | class EventManager: NotifiableFromDispacher, UnavailabledCyclicEventIdsContainable {
26 | fileprivate var touchEventDispacher: EventDispatcher
27 | fileprivate var actionButtonEventDispacher: EventDispatcher
28 | fileprivate var cyclicEventDispacher: EventDispatcher
29 |
30 | fileprivate(set) var isBlockingBehavior: Bool = true
31 | fileprivate(set) var isBlockingWalking: Bool = true
32 | fileprivate(set) var isBlockingTrigger: Bool = false
33 | internal var unavailabledCyclicEventIds: [UInt64] = []
34 |
35 | init() {
36 | self.touchEventDispacher = EventDispatcher(.touch)
37 | self.actionButtonEventDispacher = EventDispatcher(.button)
38 | self.cyclicEventDispacher = EventDispatcher(.immediate)
39 |
40 | self.touchEventDispacher.delegate = self
41 | self.actionButtonEventDispacher.delegate = self
42 | self.cyclicEventDispacher.delegate = self
43 | }
44 |
45 | @discardableResult
46 | func add(_ listener: EventListener) -> Bool {
47 | let listeners = self.getAllListeners()
48 | if listener.eventObjectId != nil {
49 | for listener_ in listeners {
50 | if listener_.eventObjectId == listener.eventObjectId
51 | && listener_.isBehavior == listener.isBehavior {
52 | return false
53 | }
54 | }
55 | }
56 | let dispacher = self.getDispacherOf(listener.triggerType)
57 | if dispacher.add(listener) == false {
58 | return false
59 | } else {
60 | return true
61 | }
62 | }
63 |
64 | @discardableResult
65 | func remove(_ id: MapObjectId, sender: GameSceneProtocol? = nil) -> Bool {
66 | let listeners = self.getAllListeners()
67 | var targetListener: EventListener? = nil
68 | var targetDispacher: EventDispatcher? = nil
69 |
70 | for listener in listeners {
71 | if listener.eventObjectId == id
72 | && listener.isBehavior == false {
73 | targetDispacher = self.getDispacherOf(listener.triggerType)
74 | targetListener = listener
75 | break
76 | }
77 | }
78 |
79 | if let l = targetListener,
80 | let d = targetDispacher {
81 | return d.remove(l, sender: sender)
82 | }
83 |
84 | return false
85 | }
86 |
87 | func trigger(_ type: TriggerType, sender: GameSceneProtocol!, args: JSON!) throws {
88 | if isBlockingTrigger { return }
89 |
90 | let dispacher = self.getDispacherOf(type)
91 | do {
92 | try dispacher.trigger(sender, args: args)
93 | } catch EventDispacherError.FiledToInvokeListener(let string) {
94 | throw EventManagerError.FailedToTrigger(string)
95 | }
96 | }
97 |
98 | // MARK: - Unavailable, Block/Unblock methods
99 |
100 | // Permanently block event listeners which existed at the time of called this function
101 | // TODO: Block touch event during executing this function
102 | func unavailableAllListeners() {
103 | self.isBlockingTrigger = true
104 |
105 | // Make target listeners already added to unavailable
106 | let listeners = self.getAllListeners()
107 | for listener in listeners {
108 | if listener.triggerType == .touch {
109 | if self.touchEventDispacher.remove(listener) == false {
110 | print("Failed to remove listener")
111 | }
112 | continue
113 | }
114 | if listener.triggerType == .button {
115 | if self.actionButtonEventDispacher.remove(listener) == false {
116 | print("Failed to remove listener")
117 | }
118 | continue
119 | }
120 | if listener.triggerType == .immediate {
121 | self.unavailabledCyclicEventIds.append(listener.id)
122 | continue
123 | }
124 | }
125 |
126 | // The controller decide by these flags that whether must add listeners or not.
127 | // So if remove listeners of walking or behavior, these flags should be true.
128 | self.isBlockingWalking = true
129 | self.isBlockingBehavior = true
130 |
131 | self.isBlockingTrigger = false
132 | }
133 |
134 | func blockBehavior() {
135 | self.isBlockingTrigger = true
136 |
137 | // Make target listeners already added to unavailable
138 | let listeners = self.cyclicEventDispacher.getAllListeners()
139 | for l in listeners {
140 | if l.isBehavior {
141 | self.unavailabledCyclicEventIds.append(l.id)
142 | }
143 | }
144 |
145 | // Prevent to invoke other listener for target listener
146 | self.isBlockingBehavior = true
147 |
148 | self.isBlockingTrigger = false
149 | }
150 |
151 | func unblockBehavior() {
152 | self.isBlockingBehavior = false
153 | }
154 |
155 | func blockWalking() {
156 | self.isBlockingTrigger = true
157 |
158 | // Make target listeners already added to unavailable
159 | let tListeners = self.touchEventDispacher.getAllListeners()
160 | for l in tListeners {
161 | if (l as? WalkEventListener) != nil {
162 | self.touchEventDispacher.remove(l)
163 | }
164 | }
165 | let cListeners = self.cyclicEventDispacher.getAllListeners()
166 | for l in cListeners {
167 | if (l as? WalkOneStepEventListener) != nil {
168 | self.unavailabledCyclicEventIds.append(l.id)
169 | }
170 | }
171 |
172 | // Prevent to invoke other listener for target listener
173 | self.isBlockingWalking = true
174 |
175 | self.isBlockingTrigger = false
176 | }
177 |
178 | func unblockWalking() {
179 | self.isBlockingWalking = false
180 | }
181 |
182 | // MARK: -
183 |
184 | func existsListeners(_ type: TriggerType) -> Bool {
185 | let dispathcer = self.getDispacherOf(type)
186 | let listeners = dispathcer.getAllListeners()
187 | return !listeners.isEmpty
188 | }
189 |
190 | func shouldActivateButton() -> Bool {
191 | let listeners = self.getDispacherOf(.immediate).getAllListeners()
192 | for listener in listeners {
193 | if let _ = listener as? ActivateButtonListener {
194 | return true
195 | }
196 | }
197 | return false
198 | }
199 |
200 | // MARK: - Private methods
201 |
202 | fileprivate func getAllListeners() -> [EventListener] {
203 | var listenersDic: Dictionary = [:]
204 | var listeners: [EventListener] = []
205 | listeners += self.touchEventDispacher.getAllListeners()
206 | listeners += self.actionButtonEventDispacher.getAllListeners()
207 | listeners += self.cyclicEventDispacher.getAllListeners()
208 |
209 | for listener in listeners {
210 | listenersDic[listener.id] = listener
211 | }
212 |
213 | for (id, _) in listenersDic {
214 | if self.unavailabledCyclicEventIds.contains(id) {
215 | listenersDic.removeValue(forKey: id)
216 | }
217 | }
218 |
219 | listeners = []
220 | for listener in listenersDic.values {
221 | listeners.append(listener)
222 | }
223 |
224 | return listeners
225 | }
226 |
227 | fileprivate func getDispacherOf(_ type: TriggerType) -> EventDispatcher {
228 | switch type {
229 | case .touch:
230 | return self.touchEventDispacher
231 | case .button:
232 | return self.actionButtonEventDispacher
233 | case .immediate:
234 | return self.cyclicEventDispacher
235 | }
236 | }
237 |
238 | // MARK: - NotifiableFromDispacher
239 |
240 | func invoke(_ listener: EventListener, invoker: EventListener) {
241 | let nextListenersDispacher = self.getDispacherOf(listener.triggerType)
242 |
243 | for id in self.unavailabledCyclicEventIds {
244 | if id == invoker.id && invoker.triggerType == .immediate {
245 | return
246 | }
247 | }
248 |
249 | if isBlockingWalking && (listener as? WalkOneStepEventListener != nil) {
250 | return
251 | }
252 |
253 | if isBlockingBehavior && listener.isBehavior {
254 | return
255 | }
256 |
257 | if !nextListenersDispacher.add(listener) {
258 | print("Failed to add listener" )
259 | }
260 | }
261 |
262 | func removed(_ listenerId: UInt64, sender: EventDispatcher) {
263 | if sender.triggerType == .immediate {
264 | if self.unavailabledCyclicEventIds.contains(listenerId) {
265 | self.unavailabledCyclicEventIds = self.unavailabledCyclicEventIds.filter { $0 != listenerId }
266 | }
267 | }
268 | }
269 | }
270 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Map/Map.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Map.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2016/02/22.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SpriteKit
11 |
12 | open class Map {
13 | /// タイルシート
14 | fileprivate(set) var sheet: TileSheet? = nil
15 |
16 | /// コンストラクタ
17 | ///
18 | /// - parameter mapName: マップである JSON ファイルの名前
19 | /// - parameter frameWidth: フレームの幅
20 | /// - parameter frameHeight: フレームの高さ
21 | ///
22 | /// - returns:
23 | init?(mapName: String,
24 | frameWidth: CGFloat,
25 | frameHeight: CGFloat)
26 | {
27 | let parser: TiledMapJsonParser
28 | do {
29 | parser = try TiledMapJsonParser(fileName: mapName)
30 | } catch ParseError.illegalJsonFormat {
31 | print("Invalid JSON format in \(mapName)")
32 | return nil
33 | } catch ParseError.jsonFileNotFound {
34 | print("JSON file \(mapName) is not found")
35 | return nil
36 | } catch {
37 | return nil
38 | }
39 |
40 | let tiles: Dictionary
41 | var objects: Dictionary
42 | let tileEvents: Dictionary
43 | let objectEvents: Dictionary
44 | do {
45 | let cols, rows: Int
46 | (cols, rows) = try parser.getLayerSize()
47 | let tileProperties = try parser.getTileProperties()
48 | let tileSets = try parser.getTileSets()
49 | let collisionLayer = try parser.getInfoFromLayer(cols, layerTileRows: rows, kind: .collision)
50 | let tileLayer = try parser.getInfoFromLayer(cols, layerTileRows: rows, kind: .tile)
51 | let objectLayer = try parser.getInfoFromLayer(cols, layerTileRows: rows, kind: .object)
52 | (tiles, tileEvents) = try Tile.createTiles(
53 | rows,
54 | cols: cols,
55 | properties: tileProperties,
56 | tileSets: tileSets,
57 | collisionPlacement: collisionLayer,
58 | tilePlacement: tileLayer)
59 | (objects, objectEvents) = try Object.createObjects(
60 | tiles,
61 | properties: tileProperties,
62 | tileSets: tileSets,
63 | objectPlacement: objectLayer)
64 | } catch ParseError.invalidValueError(let string) {
65 | print(string)
66 | return nil
67 | } catch ParseError.swiftyJsonError(let errors) {
68 | for error in errors { print(error!) }
69 | return nil
70 | } catch MapObjectError.failedToGenerate(let string) {
71 | print(string)
72 | return nil
73 | } catch {
74 | return nil
75 | }
76 |
77 | // Merge events of tile and events of object
78 | var events: Dictionary = [:]
79 | for (coordinate, event) in tileEvents {
80 | if events[coordinate] != nil {
81 | events[coordinate]!.append(event)
82 | } else {
83 | events[coordinate] = [event]
84 | }
85 | }
86 | for (coordinate, event) in objectEvents {
87 | if events[coordinate] != nil {
88 | events[coordinate]! = events[coordinate]! + event
89 | } else {
90 | events[coordinate] = event
91 | }
92 | }
93 |
94 | let sheet = TileSheet(parser: parser,
95 | frameWidth: frameWidth,
96 | frameHeight: frameHeight,
97 | tiles: tiles,
98 | objects: objects,
99 | events: events)
100 | self.sheet = sheet!
101 | }
102 |
103 | func wait(_ time: Int, callback: @escaping () -> Void) {
104 | self.sheet?.runAction([SKAction.wait(forDuration: TimeInterval(time))], callback: callback)
105 | }
106 |
107 | func addSheetTo(_ scene: SKScene) {
108 | self.sheet!.addTo(scene)
109 | }
110 |
111 | func getObjectBehavior(_ id: MapObjectId) -> ListenerChain? {
112 | return self.sheet?.getObjectBehavior(id)
113 | }
114 |
115 | func getObjectByName(_ name: String) -> Object? {
116 | return self.sheet?.getObjectByName(name)
117 | }
118 |
119 | func getObjectCoordinateByName(_ name: String) -> TileCoordinate? {
120 | return self.sheet?.getObjectCoordinateByName(name)
121 | }
122 |
123 | func removeEventsOfObject(_ objectId: MapObjectId) {
124 | self.sheet?.removeEventsOfObject(objectId)
125 | }
126 |
127 | func getAllObjects() -> [Object] {
128 | return (self.sheet?.getAllObjects())!
129 | }
130 |
131 | func setObject(_ object: Object) {
132 | let coordinate = TileCoordinate.getTileCoordinateFromSheetCoordinate(object.position)
133 | self.sheet?.setObject(object: object, coordinate: coordinate)
134 | }
135 |
136 | func setEventsOf(_ objectId: MapObjectId, coordinate: TileCoordinate) {
137 | self.sheet?.setEventsOf(objectId, coordinate: coordinate)
138 | }
139 |
140 | func getMapObjectsOn(_ coordinate: TileCoordinate) -> [MapObject]? {
141 | return self.sheet?.getMapObjectsOn(coordinate)
142 | }
143 |
144 | func getEventsOn(_ coordinate: TileCoordinate) -> [EventListener] {
145 | return (self.sheet?.getEventsOn(coordinate))!
146 | }
147 |
148 | func setCollisionOn(coordinate: TileCoordinate) {
149 | self.sheet?.getTileOn(coordinate)!.setCollision()
150 | }
151 |
152 | func removeCollisionOn(coordinate: TileCoordinate) {
153 | self.sheet?.getTileOn(coordinate)!.removeCollision()
154 | }
155 |
156 | func canPass(_ coordinate: TileCoordinate) -> Bool {
157 | if let mapObjects = self.sheet?.getMapObjectsOn(coordinate) {
158 | for mapObject in mapObjects {
159 | if mapObject.hasCollision { return false }
160 | }
161 | }
162 | return true
163 | }
164 |
165 | func updateObjectPlacement(_ object: Object, departure: TileCoordinate, destination: TileCoordinate) {
166 | self.sheet?.replaceObject(object.id, departure: departure, destination: destination)
167 | }
168 |
169 | /// オブジェクトのZ方向の位置を更新する
170 | func updateObjectsZPosition() {
171 | var objects: [(Object, CGFloat)] = []
172 |
173 | for object in (self.sheet?.getAllObjects())! {
174 | objects.append((object, object.position.y))
175 | }
176 |
177 | // Y座標に基づいてオブジェクトを並べ替え,zPosition を更新する
178 | objects.sort { $0.1 > $1.1 }
179 | let base = zPositionTable.BASE_OBJECT_POSITION
180 | var incremental: CGFloat = 0.0
181 | for (obj, _) in objects {
182 | obj.setZPosition(base + incremental)
183 | incremental += 1
184 | }
185 | }
186 |
187 | func getEventsOnPlayerPosition() -> [EventListener]? {
188 | let player = self.getObjectByName(objectNameTable.PLAYER_NAME)
189 | let events = self.getEventsOn((player?.coordinate)!)
190 | if events.isEmpty {
191 | return nil
192 | } else {
193 | return events
194 | }
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Map/TileSheet/MapObject/EventObject.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EventObject.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2017/01/26.
6 | // Copyright © 2017年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import SpriteKit
12 |
13 | open class EventObject: MapObject {
14 | fileprivate(set) var eventListener: EventListener
15 | fileprivate(set) var relativeCoordinateFromParent: TileCoordinate
16 | fileprivate(set) var relatedEventListenerId: Int?
17 |
18 | // MARK: - MapObject
19 |
20 | fileprivate(set) var id: MapObjectId
21 | fileprivate(set) var hasCollision: Bool
22 | fileprivate var parent_: MapObjectId?
23 | var parent: MapObjectId? {
24 | get {
25 | return self.parent_
26 | }
27 | set {
28 | self.parent_ = newValue
29 | }
30 | }
31 | fileprivate var children_: [MapObjectId] = []
32 | var children: [MapObjectId] {
33 | get {
34 | return self.children_
35 | }
36 | set {
37 | self.children_ = newValue
38 | }
39 | }
40 | func setCollision() {
41 | self.hasCollision = true
42 | }
43 | static var nextId = 0
44 | static func generateId() -> MapObjectId {
45 | nextId += 1
46 | return nextId
47 | }
48 |
49 | // MARK: -
50 |
51 | init(parentId: MapObjectId, relativeCoordinate: TileCoordinate, event: EventListener) {
52 | self.id = EventObject.generateId()
53 | self.parent_ = parentId
54 | self.relativeCoordinateFromParent = relativeCoordinate
55 | event.eventObjectId = self.id
56 | self.eventListener = event
57 | self.hasCollision = false
58 | }
59 |
60 | func registerListenerId(listenerId: Int) {
61 | self.relatedEventListenerId = listenerId
62 | }
63 |
64 | func removeListenerId() {
65 | self.relatedEventListenerId = nil
66 | }
67 | }
68 |
69 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Map/TileSheet/MapObject/MapObject.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MapObject.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2016/02/15.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum MapObjectError: Error {
12 | case failedToGenerate(String)
13 | }
14 |
15 | typealias MapObjectId = Int
16 | protocol MapObject {
17 | // TODO: MapObjectId will confused because it could contain different class's id which is inherit MapObject
18 | /// Map object could have parent-child relationship.
19 | /// There are some usage for these...
20 | /// - Sometimes you want to tying events to the map object.
21 | /// This could realize by adding map objects having event to target map object as children,
22 | /// and manage the child map object's as "event object"
23 | /// - MapObject should have only one tile coordinate.
24 | /// But sometimes you want to add large map object (i.e. the map object acrossing some tiles
25 | /// In above situation, parent-child relationship of map object could use for grouping for map objects.
26 |
27 | var parent: MapObjectId? { get set }
28 |
29 | var children: [MapObjectId] { get set }
30 |
31 | /// For collision detection
32 |
33 | var hasCollision: Bool { get }
34 |
35 | func setCollision()
36 |
37 | /// Each map object has id.
38 | /// This id should be used for identification of objects
39 | /// which inherit same *class*, not same *protocol*.
40 |
41 | var id: MapObjectId { get }
42 |
43 | static var nextId: MapObjectId { get }
44 |
45 | static func generateId() -> MapObjectId
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Map/TileSheet/MapObject/Tile.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Tile.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2015/08/03.
6 | // Copyright © 2015年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import SpriteKit
12 |
13 | typealias TileID = Int
14 | typealias TileSetID = Int
15 | typealias TileProperty = Dictionary
16 |
17 | /// マップ上に敷かれる各タイルに対応した SKSpriteNode のラッパークラス
18 | open class Tile: MapObject {
19 | static var TILE_SIZE: CGFloat = 32.0
20 | fileprivate let tileId: Int
21 | fileprivate let node: SKSpriteNode
22 | fileprivate(set) var coordinate: TileCoordinate
23 | fileprivate(set) var property: TileProperty
24 |
25 | // MARK: - MapObject
26 |
27 | fileprivate(set) var id: MapObjectId
28 | fileprivate(set) var hasCollision: Bool
29 | fileprivate var parent_: MapObjectId?
30 | var parent: MapObjectId? {
31 | get {
32 | return self.parent_
33 | }
34 | set {
35 | self.parent_ = newValue
36 | }
37 | }
38 | fileprivate var children_: [MapObjectId] = []
39 | var children: [MapObjectId] {
40 | get {
41 | return self.children_
42 | }
43 | set {
44 | self.children_ = newValue
45 | }
46 | }
47 | func setCollision() {
48 | self.hasCollision = true
49 | }
50 | static var nextId = 0
51 | static func generateId() -> MapObjectId {
52 | nextId += 1
53 | return nextId
54 | }
55 |
56 | // MARK: -
57 |
58 | /// コンストラクタ
59 | ///
60 | /// - parameter coordinate: タイルの座標
61 | /// - parameter event: タイルに配置するイベント
62 | ///
63 | /// - returns: なし
64 | init(id: TileID, coordinate: TileCoordinate, property: TileProperty) {
65 | self.id = Tile.generateId()
66 | let x = coordinate.x
67 | let y = coordinate.y
68 | self.tileId = id
69 | self.node = SKSpriteNode()
70 | self.node.size = CGSize(width: CGFloat(Tile.TILE_SIZE),
71 | height: CGFloat(Tile.TILE_SIZE))
72 | self.node.position = CGPoint(x: CGFloat(x - 1) * Tile.TILE_SIZE,
73 | y: CGFloat(y - 1) * Tile.TILE_SIZE)
74 | self.node.anchorPoint = CGPoint(x: 0.0, y: 0.0)
75 | self.node.zPosition = zPositionTable.TILE
76 | self.coordinate = TileCoordinate(x: x, y: y)
77 | self.hasCollision = false
78 | self.property = property
79 | }
80 |
81 | /// タイルにテクスチャ画像を付加する
82 | ///
83 | /// - parameter imageName: 付加するテクスチャ画像名
84 | func setImageWithName(_ imageName: String) {
85 | node.texture = SKTexture(imageNamed: imageName)
86 | }
87 |
88 | /// タイルにテクスチャ画像を付加する
89 | ///
90 | /// - parameter image: 付加するテクスチャ画像
91 | func setImageWithUIImage(_ image: UIImage) {
92 | node.texture = SKTexture(image: image)
93 | }
94 |
95 | /// タイルのノードに子ノードを追加する
96 | ///
97 | /// - parameter node: 追加する子ノード
98 | func addTo(_ node: SKSpriteNode) {
99 | node.addChild(self.node)
100 | }
101 |
102 | func removeCollision() {
103 | self.hasCollision = false
104 | }
105 |
106 | // MARK: - class method
107 |
108 | /// タイル群を生成する
109 | ///
110 | /// - parameter rows: タイルを敷き詰める列数
111 | /// - parameter cols: タイルを敷き詰める行数
112 | /// - parameter properties: タイル及びオブジェクトのプロパティ
113 | /// - parameter tileSets: タイルセットの情報
114 | /// - parameter collisionPlacement: マップにおける当たり判定の配置
115 | /// - parameter tilePlacement: マップにおけるタイルの配置
116 | ///
117 | /// - throws:
118 | ///
119 | /// - returns: 生成したタイル群
120 | class func createTiles(
121 | _ rows: Int,
122 | cols: Int,
123 | properties: Dictionary,
124 | tileSets: Dictionary,
125 | collisionPlacement: Dictionary,
126 | tilePlacement: Dictionary
127 | ) throws ->
128 | (tiles: Dictionary, events: Dictionary)
129 | {
130 | var tiles: Dictionary = [:]
131 | var eventObjects: Dictionary = [:]
132 | for (coordinate, tileID) in tilePlacement {
133 | let tileProperty = properties[tileID]
134 | if tileProperty == nil {
135 | throw MapObjectError.failedToGenerate("tileID \(tileID.description)'s property is not defined in properties(\(properties.description))")
136 | }
137 |
138 | // タイルを作成する
139 | let tile = Tile(
140 | id: tileID,
141 | coordinate: coordinate,
142 | property: tileProperty!
143 | )
144 |
145 | // 当たり判定を付加する
146 | let hasCollision = collisionPlacement[coordinate]
147 | if hasCollision == nil {
148 | throw MapObjectError.failedToGenerate("Coordinate(\(coordinate.description)) specified in tilePlacement is not defined at collisionPlacement(\(collisionPlacement.description))")
149 | }
150 | if hasCollision != 0 {
151 | tile.setCollision()
152 | }
153 |
154 | // 画像を付与する
155 | let tileSetID = Int(tile.property["tileSetID"]!)
156 | if tileSetID == nil {
157 | throw MapObjectError.failedToGenerate("tileSetID is not defined in tile \(tile)'s property(\(tile.property.description))")
158 | }
159 |
160 | let tileSet = tileSets[tileSetID!]
161 | if tileSet == nil {
162 | throw MapObjectError.failedToGenerate("tileSet(ID = \(tileSetID?.description)) is not defined in tileSets(\(tileSets.description))")
163 | }
164 |
165 | let tileImage: UIImage?
166 | do {
167 | tileImage = try tileSet!.cropTileImage(tileID)
168 | } catch {
169 | throw MapObjectError.failedToGenerate("Failed to crop image of object which tileID is \(tileID)")
170 | }
171 | tile.setImageWithUIImage(tileImage!)
172 |
173 | if let action = tile.property["event"] {
174 | let listeners: Dictionary
175 | do {
176 | let properties = try EventPropertyParser.parse(from: action)
177 | listeners = try ListenerGenerator.generate(properties: properties)
178 | } catch EventParserError.invalidProperty(let string) {
179 | throw MapObjectError.failedToGenerate("Failed to generate event listener: " + string)
180 | } catch ListenerGeneratorError.failed(let string) {
181 | throw MapObjectError.failedToGenerate("Failed to generate event listener: " + string)
182 | }
183 |
184 | if listeners.count == 0 {
185 | break
186 | }
187 | if listeners.count > 1 {
188 | throw MapObjectError.failedToGenerate("Tile event should has only one relative coordinate")
189 | }
190 | if listeners.count == 1 && listeners.first?.key != TileCoordinate(x:0,y:0) {
191 | throw MapObjectError.failedToGenerate("Tile event should has (0,0) relative coordinate")
192 | }
193 |
194 | let listener = listeners.first!.value
195 |
196 | // Create event object for this tile
197 | let eventObject = EventObject(parentId: tile.id, relativeCoordinate: coordinate, event: listener)
198 | tile.children.append(eventObject.id)
199 | eventObjects[tile.coordinate] = eventObject
200 | }
201 |
202 | tiles[coordinate] = tile
203 | }
204 | return (tiles: tiles, events: eventObjects)
205 | }
206 |
207 | // MARK: -
208 | }
209 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Map/TileSheet/MapObject/util/BehaviorPropertyParser.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BehaviorPropertyParser.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/12/25.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SpriteKit
11 | import SwiftyJSON
12 |
13 | class BehaviorPropertyParser {
14 | class func parse(from properties: String, parentId: MapObjectId) throws -> ListenerChain? {
15 | var chain: ListenerChain = []
16 | let lines = properties.components(separatedBy: "\n")
17 | for line in lines {
18 | let params = line.components(separatedBy: ",")
19 | let type = params[0]
20 | let args = Array(params.dropFirst(1))
21 | do {
22 | chain += try ListenerContainer.getBy(type, directionToParent: nil, params: args)
23 | } catch {
24 | // TODO
25 | }
26 | }
27 |
28 | // For looping animation
29 | chain = chain + [
30 | (listener: ReloadBehaviorEventListener.self, params: JSON(["eventObjectId":parentId]) as JSON?)
31 | ]
32 |
33 | return chain
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Map/TileSheet/MapObject/util/EventPropertyParser.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EventPropertyParser.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/12/25.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum EventParserError: Error {
12 | case invalidProperty(String)
13 | }
14 |
15 | struct EventProperty {
16 | var type: String
17 | var relativeCoordinates: [TileCoordinate]
18 | var args: [String]
19 |
20 | // FIXME: 斜め方向に対応していない
21 | func calcDirection(from relativeCoordinate: TileCoordinate) -> DIRECTION {
22 | if relativeCoordinate.x > 0 {
23 | return .left
24 | } else if relativeCoordinate.x < 0 {
25 | return .right
26 | } else if relativeCoordinate.y > 0 {
27 | return .down
28 | } else if relativeCoordinate.y < 0 {
29 | return .up
30 | }
31 |
32 | // FIXME: 上記のいずれにもマッチしなかった場合にどうするか
33 | return .down
34 | }
35 | }
36 |
37 | class EventPropertyParser {
38 | class func parse(from eventProperties: String) throws -> [EventProperty] {
39 | let eventProperty = eventProperties.components(separatedBy: "\n")
40 | var properties: [EventProperty] = []
41 |
42 | for property in eventProperty {
43 | properties.append(try EventPropertyParser.parseProperty(from: property))
44 | }
45 |
46 | return properties
47 | }
48 |
49 | fileprivate class func parseProperty(from eventProperty: String) throws -> EventProperty {
50 | var property = EventProperty(type: "", relativeCoordinates: [], args: [])
51 |
52 | // プロパティを (イベント種別, イベント配置, イベント引数) に分割
53 | // ex) talk,{(0,-1),(-1,0),(1,0)},params
54 | let eventCoordinatesPattern = "(\\(-?[0-9]+,-?[0-9]+\\),?)+"
55 | let matchedEventCoordinatesSets = EventPropertyParser.matches(for: eventCoordinatesPattern, in: eventProperty)
56 | if matchedEventCoordinatesSets == [] {
57 | throw EventParserError.invalidProperty("Invalid Property: \(eventProperty)")
58 | }
59 | let eventCoordinatesSetString = matchedEventCoordinatesSets[0]
60 |
61 | let matchedEventCoordinates = self.matches(for: "\\(-?[0-9]+,-?[0-9]+\\)", in: eventCoordinatesSetString)
62 | for eventCoordinateString in matchedEventCoordinates {
63 | property.relativeCoordinates.append(TileCoordinate.parse(from: eventCoordinateString))
64 | }
65 |
66 | let params = eventProperty.replacingOccurrences(of: "{"+eventCoordinatesSetString+"},", with: "")
67 | let tmp = params.components(separatedBy: ",")
68 | property.type = tmp[0]
69 | property.args = Array(tmp.dropFirst())
70 |
71 | return property
72 | }
73 |
74 | fileprivate class func matches(for regex: String, in text: String) -> [String] {
75 | do {
76 | let regex = try NSRegularExpression(pattern: regex)
77 | let nsString = text as NSString
78 | let results = regex.matches(in: text, range: NSRange(location: 0, length: nsString.length))
79 | return results.map { nsString.substring(with: $0.range)}
80 | } catch let error {
81 | print("invalid regex: \(error.localizedDescription)")
82 | return []
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Map/TileSheet/MapObject/util/ListenerContainer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EventListenerGenerator.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/08/04.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 |
12 | enum ListenerContainerError: Error {
13 | case eventIdNotFound
14 | case invalidParams(String)
15 | }
16 |
17 | class ListenerContainer {
18 | class func getBy(_ id: String, directionToParent: DIRECTION?, params: [String]) throws -> ListenerChain {
19 | switch id {
20 | case "talk":
21 | let parser = TalkBodyParser(talkFileName: params[0])
22 | if parser == nil {
23 | throw ListenerContainerError.invalidParams("Specified talk file (\(params[0])) is not found. Check your params (\(params.description) format)")
24 | }
25 | var paramsJson = parser?.parse()
26 |
27 | // direction を追加
28 | var array = paramsJson!.arrayObject as? [[String:String]]
29 | let directionString = directionToParent == nil ? "" : directionToParent!.toString
30 | array!.append(["direction":directionString!])
31 | paramsJson = JSON(array!)
32 |
33 | return [
34 | (listener: ActivateButtonListener.self, params: JSON(["text":"はなす"]) as JSON?),
35 | (listener: StartTalkEventListener.self, params: paramsJson),
36 | ]
37 | case "item":
38 | let item = ItemTable.get(params[0])
39 | if item == nil {
40 | throw ListenerContainerError.invalidParams("Specified item key (\(params[0])) in params(\(params.description)) is not found. Check ItemTable definition")
41 | }
42 |
43 | return [
44 | (listener: ItemGetEventListener.self, params: item!.getJSON() as JSON?),
45 | (listener: ShowEventDialogListener.self, params: JSON(["text":"test"])),
46 | ]
47 | case "move":
48 | let objectName = params[0] as String
49 | let direction = params[1] as String
50 | let step_num = params[2] as String
51 | let speed = params[3] as String
52 |
53 | return [
54 | (listener: MoveObjectEventListener.self,
55 | params: JSON([
56 | "name":objectName,
57 | "direction":direction,
58 | "step_num":step_num,
59 | "speed":speed
60 | ]) as JSON?)
61 | ]
62 | case "wait":
63 | let time = params[0] as String
64 | return [
65 | (listener: WaitEventListener.self, params: JSON(["time":time]))
66 | ]
67 | case "scene":
68 | let map_file_name = params[0] as String
69 | let player_place_coordiante = params[1] as String
70 | let player_direction = params[2] as String
71 |
72 | return [
73 | (listener: SceneTransitionEventListener.self,
74 | params: JSON([
75 | "map_file_name":map_file_name,
76 | "player_place_coordinate":player_place_coordiante,
77 | "player_direction":player_direction
78 | ]))
79 | ]
80 | default:
81 | throw ListenerContainerError.eventIdNotFound
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Map/TileSheet/MapObject/util/ListenerGenerator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LGenerator.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2017/01/23.
6 | // Copyright © 2017年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum ListenerGeneratorError: Error {
12 | case failed(String)
13 | }
14 |
15 | class ListenerGenerator {
16 | class func generate(properties: [EventProperty]) throws -> Dictionary {
17 | var listenerChains: Dictionary = [:]
18 |
19 | // イベントプロパティからイベントリスナーを生成する
20 | // relative Coordinates 毎にリスナーチェーンを保存していく
21 | for property in properties {
22 | for relativeCoordinate in property.relativeCoordinates {
23 | do {
24 | let listenerChain = try ListenerContainer.getBy(
25 | property.type,
26 | directionToParent: property.calcDirection(from: relativeCoordinate),
27 | params: property.args)
28 | if listenerChains[relativeCoordinate] != nil {
29 | listenerChains[relativeCoordinate]? += listenerChain
30 | } else {
31 | listenerChains[relativeCoordinate] = listenerChain
32 | }
33 | } catch ListenerContainerError.eventIdNotFound {
34 | throw ListenerGeneratorError.failed("不正なイベントIDです")
35 | } catch ListenerContainerError.invalidParams(let string) {
36 | throw ListenerGeneratorError.failed(string)
37 | }
38 | }
39 | }
40 |
41 | var listeners: Dictionary = [:]
42 | // イベントリスナーの生成
43 | for (coordinate, listenerChain) in listenerChains {
44 | let listenerType = listenerChain.first?.listener
45 | let params = listenerChain.first?.params
46 | do {
47 | // When the listener chain has finished, the player and objects
48 | // should be able to move.
49 | let chain: ListenerChain = ListenerChain(listenerChain.dropFirst(1)) + [
50 | (listener: BackToDefaultStateEventListener.self, params: nil)
51 | ]
52 | let listener = try listenerType?.init(
53 | params: params,
54 | chainListeners: chain)
55 | listeners[coordinate] = listener
56 | } catch EventListenerError.illegalArguementFormat(let string) {
57 | throw ListenerGeneratorError.failed("Illegal arguement for listener: " + string)
58 | } catch EventListenerError.illegalParamFormat(let array) {
59 | throw ListenerGeneratorError.failed("Illegal parameter for listener: " + array.joined(separator: ","))
60 | } catch EventListenerError.invalidParam(let string) {
61 | throw ListenerGeneratorError.failed("Invalid parameter for listener: " + string)
62 | } catch EventParserError.invalidProperty(let string) {
63 | throw ListenerGeneratorError.failed("Invalid property for listener: " + string)
64 | }
65 | }
66 |
67 | return listeners
68 | }
69 | }
70 |
71 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Map/TileSheet/TileCoordinate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TileCoordinate.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2015/08/10.
6 | // Copyright © 2015年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import SpriteKit
12 |
13 | /// タイル座標.座標には以下の三種類がある
14 | /// - タイル座標 TileCoordinate : タイル
15 | /// - 画面座標 ScreenCoordinate : 画面左下を原点とした相対座標
16 | /// - シート座標 SheetCoordinate : シート左下を原点とした相対座標
17 | class TileCoordinate: Hashable {
18 | let x: Int
19 | let y: Int
20 |
21 | init(x: Int, y: Int) {
22 | self.x = x
23 | self.y = y
24 | }
25 |
26 | /// タッチされた位置に最も近いタイルの中心の画面上の座標を返す
27 | ///
28 | /// - parameter sheetPosition: タイルシートの位置
29 | /// - parameter touchedPosition: タッチされた座標
30 | ///
31 | /// - returns: タイルの中心の画面上の座標
32 | class func getSheetCoordinateOfTouchedTile(_ sheetPosition: CGPoint, touchedPosition: CGPoint) -> CGPoint {
33 | return TileCoordinate.getSheetCoordinateFromTileCoordinate(
34 | TileCoordinate.getTileCoordinateFromScreenCoordinate(sheetPosition, screenCoordinate: touchedPosition)
35 | )
36 | }
37 |
38 | /// 画面座標をタイル座標に変換する
39 | ///
40 | /// - parameter sheetPosition: タイルシートの位置
41 | /// - parameter touchedPosition: タッチされた座標
42 | ///
43 | /// - returns: 最も近いタイルのタイル座標
44 | class func getTileCoordinateFromScreenCoordinate(_ sheetPosition: CGPoint, screenCoordinate: CGPoint) -> TileCoordinate {
45 | return TileCoordinate(x: Int(floor((screenCoordinate.x - sheetPosition.x) / CGFloat(Tile.TILE_SIZE) + 1)),
46 | y: Int(floor((screenCoordinate.y - sheetPosition.y) / CGFloat(Tile.TILE_SIZE) + 1)))
47 | }
48 |
49 | /// シート座標をタイル座標に変換する
50 | ///
51 | /// - parameter sheetPosition: シート座標
52 | ///
53 | /// - returns: タイル座標
54 | class func getTileCoordinateFromSheetCoordinate(_ sheetCoordinate: CGPoint) -> TileCoordinate {
55 | return TileCoordinate(x: Int(floor(sheetCoordinate.x / CGFloat(Tile.TILE_SIZE) + 1)),
56 | y: Int(floor(sheetCoordinate.y / CGFloat(Tile.TILE_SIZE) + 1)))
57 | }
58 |
59 | class func getSheetCoordinateFromScreenCoordinate(_ sheetPosition: CGPoint, screenCoordinate: CGPoint) -> CGPoint {
60 | return CGPoint(x: screenCoordinate.x + sheetPosition.x, y: screenCoordinate.y + sheetPosition.y)
61 | }
62 |
63 | /// タイル座標をシート座標に変換する
64 | /// シート座標は,該当タイルの中心の値を返す
65 | ///
66 | /// - parameter coordinate: タイル座標
67 | ///
68 | /// - returns: シート座標
69 | class func getSheetCoordinateFromTileCoordinate(_ tileCoordinate: TileCoordinate) -> CGPoint {
70 | return CGPoint(x: CGFloat(tileCoordinate.x) * Tile.TILE_SIZE - Tile.TILE_SIZE / 2,
71 | y: CGFloat(tileCoordinate.y) * Tile.TILE_SIZE - Tile.TILE_SIZE / 2)
72 | }
73 |
74 | /// 文字列を座標に変換する
75 | class func parse(from coordinateString: String) -> TileCoordinate {
76 | let coordinateValues = coordinateString
77 | .replacingOccurrences(of: "(", with: "")
78 | .replacingOccurrences(of: ")", with: "")
79 | .components(separatedBy: ",")
80 | let x: Int? = Int(coordinateValues[0])
81 | let y: Int? = Int(coordinateValues[1])
82 |
83 | if x == nil || y == nil {
84 | print("Invalid params: \(coordinateString)")
85 | return TileCoordinate(x: 0, y: 0)
86 | }
87 |
88 | return TileCoordinate(x: x!, y: y!)
89 | }
90 |
91 | // MARK: - Hashable
92 |
93 | var hashValue : Int {
94 | get {
95 | return "\(self.x),\(self.y)".hashValue
96 | }
97 | }
98 |
99 | var description : String {
100 | get {
101 | return "\(self.x),\(self.y)"
102 | }
103 | }
104 | }
105 |
106 | // MARK: - Equatable
107 |
108 | func ==(lhs: TileCoordinate, rhs: TileCoordinate) -> Bool {
109 | return lhs.hashValue == rhs.hashValue
110 | }
111 |
112 | func -(lhs: TileCoordinate, rhs: TileCoordinate) -> TileCoordinate {
113 | return TileCoordinate(x: lhs.x-rhs.x, y: lhs.y-rhs.y)
114 | }
115 |
116 | func +(lhs: TileCoordinate, rhs: TileCoordinate) -> TileCoordinate {
117 | return TileCoordinate(x: lhs.x+rhs.x, y: lhs.y+rhs.y)
118 | }
119 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Map/TileSheet/TileSet.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TileSet.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2016/02/22.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import SpriteKit
12 |
13 | enum TileSetError: Error {
14 | case failedToCrop
15 | }
16 |
17 | /// タイルセット : 1画像ファイル内のタイル群 = 1タイルセットに対応
18 | class TileSet {
19 | /// タイルセットを一意に識別するID
20 | fileprivate let tileSetID: Int!
21 |
22 | /// 画像ファイル名
23 | fileprivate let imageName: String!
24 |
25 | /// セット内のタイル数
26 | fileprivate let count: Int!
27 |
28 | /// 一番若いタイルID
29 | fileprivate let firstTileID: Int!
30 |
31 | /// タイルセット(画像ファイル)の横幅
32 | fileprivate let imageWidth: Int!
33 |
34 | /// タイルセット(画像ファイル)の縦幅
35 | fileprivate let imageHeight: Int!
36 |
37 | /// タイルセット内の各タイルの横幅
38 | fileprivate let tileWidth: Int!
39 |
40 | /// タイルセット内の各タイルの縦幅
41 | fileprivate let tileHeight: Int!
42 |
43 | init?(id: Int,
44 | imageName: String,
45 | nTile: Int,
46 | firstTileID: Int,
47 | width: Int,
48 | height: Int,
49 | tileWidth: Int,
50 | tileHeight: Int) {
51 | self.tileSetID = id
52 | self.imageName = imageName
53 | self.count = nTile
54 | self.firstTileID = firstTileID
55 | self.imageWidth = width
56 | self.imageHeight = height
57 | self.tileWidth = tileWidth
58 | self.tileHeight = tileHeight
59 | }
60 |
61 | /// タイルの画像を,タイルセット(1画像ファイル)から切り出し,返す
62 | ///
63 | /// - parameter tileSetID: 対象タイルが含まれるタイルセットID
64 | /// - parameter tileID: 対象タイルのタイルID
65 | ///
66 | /// - throws: otherError
67 | ///
68 | /// - returns: タイル画像
69 | func cropTileImage(_ tileID: Int) throws -> UIImage {
70 | let tileSetRows = self.imageWidth / self.tileWidth
71 | let firstTileID = self.firstTileID
72 | var iTargetTileInSet: Int
73 |
74 | // ID は左上から順番
75 | // TODO: tileSet の中に tileID が含まれていない場合の validation
76 | if firstTileID! >= tileID {
77 | iTargetTileInSet = firstTileID! - tileID
78 | } else {
79 | iTargetTileInSet = tileID - 1
80 | }
81 |
82 | // 対象タイルの,タイルセット内における位置(行数,列数)を調べる
83 | let targetCol: Int
84 | let targetRow: Int
85 | if iTargetTileInSet == 0 {
86 | targetCol = 1
87 | targetRow = 1
88 | } else {
89 | targetRow = Int(iTargetTileInSet % tileSetRows) + 1
90 | targetCol = Int(iTargetTileInSet / tileSetRows) + 1
91 | }
92 |
93 | // 画像の切り抜き
94 | let tileSize = CGRect(
95 | x: CGFloat(tileWidth) * CGFloat(targetRow - 1),
96 | y: CGFloat(tileHeight) * CGFloat(targetCol - 1),
97 | width: CGFloat(tileWidth),
98 | height: CGFloat(tileHeight))
99 | if let image = UIImage(named: self.imageName),
100 | let cropCGImageRef = image.cgImage?.cropping(to: tileSize) {
101 | return UIImage(cgImage: cropCGImageRef)
102 | } else {
103 | throw TileSetError.failedToCrop
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Map/TiledMapJsonParser.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TiledMapJsonParser.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2015/10/12.
6 | // Copyright © 2015年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 | import SpriteKit
12 |
13 | enum ParseError: Error {
14 | case jsonFileNotFound
15 | case illegalJsonFormat
16 | case swiftyJsonError([NSError?])
17 | case invalidValueError(String)
18 | }
19 |
20 | /// タイルの配置や種類の情報を記述したJSONファイルのパーサ
21 | class TiledMapJsonParser {
22 | fileprivate var json : JSON = JSON.null
23 |
24 | /// タイルサイズ
25 | fileprivate let TILE_SIZE = Int(Tile.TILE_SIZE)
26 |
27 | /// コンストラクタ
28 | ///
29 | /// - throws: JsonFileNotFound, IllegalJsonFormat
30 | ///
31 | /// - parameter fileName: パース対象のjsonファイル名
32 | init(fileName: String) throws {
33 | let path: String? = Bundle.main.path(forResource: fileName, ofType: "json")
34 | if path == nil {
35 | throw ParseError.jsonFileNotFound
36 | }
37 |
38 | let fileHandle: FileHandle? = FileHandle(forReadingAtPath: path!)
39 | if fileHandle == nil {
40 | throw ParseError.jsonFileNotFound
41 | }
42 |
43 | let data: Data = fileHandle!.readDataToEndOfFile()
44 |
45 | self.json = JSON(data: data)
46 | if self.json.type != Type.dictionary {
47 | throw ParseError.illegalJsonFormat
48 | }
49 | }
50 |
51 | /// タイルセットの各情報を取得する
52 | ///
53 | /// - throws: SwiftyJsonError
54 | ///
55 | /// - returns: タイルセットの情報.画像ファイル名やセット内のタイル数等.詳しくは TileSetInfo を参照
56 | func getTileSets() throws -> Dictionary {
57 | var tileSets: Dictionary = [:]
58 |
59 | let tileSetsJson = json["tilesets"].array
60 | if tileSetsJson == nil {
61 | throw ParseError.swiftyJsonError([json["tilesets"].error])
62 | }
63 |
64 | var tileSetID = 1
65 | for tileSetJson in tileSetsJson! {
66 | // パスからファイル名を抽出
67 | let imageName = tileSetJson["image"].string!.components(separatedBy: "/").last!
68 | tileSets[tileSetID] = TileSet(id: tileSetID,
69 | imageName: imageName,
70 | nTile: tileSetJson["tilecount"].int!,
71 | firstTileID: tileSetJson["firstgid"].int!,
72 | width: tileSetJson["imagewidth"].int!,
73 | height: tileSetJson["imageheight"].int!,
74 | tileWidth: tileSetJson["tilewidth"].int!,
75 | tileHeight: tileSetJson["tileheight"].int!)
76 | tileSetID += 1
77 | }
78 | return tileSets
79 | }
80 |
81 | /// 各タイルのプロパティを取得する
82 | ///
83 | /// - throws: SwiftyJsonError, otherError
84 | ///
85 | /// - returns: プロパティのディクショナリ
86 | func getTileProperties() throws -> Dictionary {
87 | var properties: Dictionary = [:]
88 |
89 | let tileSets = json["tilesets"].array
90 | if tileSets == nil {
91 | throw ParseError.swiftyJsonError([json["tilesets"].error])
92 | }
93 |
94 | var tileSetID = 1
95 | for tileSet in tileSets! {
96 | /// tileSet 内の最初のタイルの gid
97 | let firstTileID = tileSet["firstgid"].int
98 | /// tileSet 内に存在するタイルの数
99 | let nTileInSet = tileSet["tilecount"].int
100 | if firstTileID == nil || nTileInSet == nil {
101 | throw ParseError.swiftyJsonError([tileSet["firstgid"].error, tileSet["tilecount"].error])
102 | }
103 |
104 | // tileSet 内の各タイルについて,そのプロパティに tileSet の情報を追加する
105 | for tileID in firstTileID! ... firstTileID! + nTileInSet! - 1 {
106 | properties[tileID] = [
107 | "tileSetID": tileSetID.description,
108 | "tileSetName": tileSet["name"].string!
109 | ]
110 | }
111 |
112 | // 各タイル毎にその他のプロパティを保持
113 | if tileSet["tileproperties"] == JSON.null {
114 | throw ParseError.swiftyJsonError([tileSet["tileproperties"].error])
115 | }
116 | for (cor, tileproperties) in tileSet["tileproperties"] {
117 | for (property, value) in tileproperties {
118 | let tileID = firstTileID! + Int(cor)!
119 | if properties[tileID] != nil {
120 | properties[tileID]![property] = value.string
121 | } else {
122 | throw ParseError.invalidValueError("TileID is not found in properties")
123 | }
124 | }
125 | }
126 | tileSetID += 1
127 | }
128 | return properties
129 | }
130 |
131 | /// レイヤーの種類を表す enum 型
132 | ///
133 | /// - TILE: タイル情報
134 | /// - COLLISION: 当たり判定情報
135 | /// - OBJECT: オブジェクト情報
136 | enum LAYER: Int {
137 | case tile = 0
138 | case collision = 1
139 | case object = 2
140 | }
141 |
142 | /// レイヤから得られる情報(タイルの配置情報)を返す
143 | ///
144 | /// - parameter layerTileCols: レイヤ上のタイルの行数
145 | /// - parameter layerTileRows: レイヤ上のタイルの列数
146 | /// - parameter kind: 読み込むレイヤの種類
147 | ///
148 | /// - throws: SwiftyJsonError, otherError
149 | ///
150 | /// - returns: レイヤ情報(タイル座標とタイルIDの組)
151 | func getInfoFromLayer(
152 | _ layerTileCols: Int,
153 | layerTileRows: Int,
154 | kind: LAYER
155 | ) throws -> Dictionary {
156 | var info: Dictionary = [:]
157 |
158 | if (layerTileCols < 1 || layerTileRows < 1) {
159 | throw ParseError.invalidValueError("Invalid layer size: cols or rows is fewer than 1")
160 | }
161 |
162 | let layer = json["layers"][kind.rawValue]["data"].array
163 | if layer == nil {
164 | throw ParseError.swiftyJsonError([json["layers"][kind.rawValue]["data"].error])
165 | }
166 |
167 | for y in 1 ..< layerTileCols+1 {
168 | for x in 1 ..< layerTileRows+1 {
169 | let index = (layerTileCols - y) * layerTileRows + x - 1
170 | if layer![index].int == nil {
171 | throw ParseError.swiftyJsonError([layer![index].error])
172 | }
173 | info[TileCoordinate(x: x, y: y)] = layer![index].int!
174 | }
175 | }
176 |
177 | return info
178 | }
179 |
180 | /// レイヤーのサイズを取得する
181 | ///
182 | /// - throws: SwiftyJsonError
183 | ///
184 | /// - returns: [ 行数, 列数 ]
185 | func getLayerSize() throws -> (cols: Int, rows: Int) {
186 | let layerTileCols = json["height"].int
187 | let layerTileRows = json["width"].int
188 | if layerTileCols == nil || layerTileRows == nil {
189 | throw ParseError.swiftyJsonError([json["height"].error, json["width"].error])
190 | }
191 |
192 | return (layerTileCols!, layerTileRows!)
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/MenuSceneModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MenuSceneModel.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/07/29.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import SwiftyJSON
12 | import RealmSwift
13 |
14 | protocol MenuSceneModelDelegate {
15 | func updateItemSelect()
16 | func reloadTable()
17 | }
18 |
19 | class MenuSceneModel: NSObject, UICollectionViewDataSource {
20 | var delegate: MenuSceneModelDelegate!
21 | fileprivate(set) var selectedContents: JSON? = nil
22 | let defaultMessage = "...。"
23 | fileprivate(set) var deselectedIndexPath: IndexPath? = nil
24 | fileprivate(set) var selectedIndexPath: IndexPath? = nil
25 | fileprivate(set) var contents: [Item] = []
26 |
27 | func updateItems() {
28 | let realm = try! Realm()
29 | let items = realm.objects(StoredItems.self)
30 | for item in items {
31 | contents.append(Item(key: item.key, name: item.name, description: item.text, image_name: item.image_name))
32 | }
33 | self.delegate.reloadTable()
34 | }
35 |
36 | func selectItem(_ indexPath: IndexPath) {
37 | self.deselectedIndexPath = self.selectedIndexPath
38 | self.selectedIndexPath = indexPath
39 | self.delegate.updateItemSelect()
40 | }
41 |
42 | // MARK: UICollectionViewDataSource
43 |
44 | func numberOfSections(in collectionView: UICollectionView) -> Int {
45 | return 1
46 | }
47 |
48 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
49 | return self.contents.count
50 | }
51 |
52 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
53 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ItemCell
54 | cell.imageView.image = UIImage(named: contents[indexPath.row].image_name)
55 | return cell
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Setting/ItemTable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ItemTable.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/08/04.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyJSON
11 |
12 | struct Item {
13 | var key: String
14 | var name: String
15 | var description: String
16 | var image_name: String
17 |
18 | func getJSON() -> JSON {
19 | return JSON([
20 | "key": self.key,
21 | "name": self.name,
22 | "description": self.description,
23 | "image_name": self.image_name
24 | ])
25 | }
26 | }
27 |
28 | struct ItemTable {
29 | static func get(_ key: String) -> Item? {
30 | for item in ItemTable.items {
31 | if item.key == key {
32 | return item
33 | }
34 | }
35 | return nil
36 | }
37 |
38 | static let items: [Item] = [
39 | Item(key: "test", name: "test object", description: "テスト用アイテムです", image_name: "kanamono_tile.png")
40 | ]
41 | }
42 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Setting/MapTable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MapTable.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2017/01/29.
6 | // Copyright © 2017年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct MapTable {
12 | static let fromJsonFileName: Dictionary = [
13 | "sample_map02.json": FirstGameScene.self,
14 | "sample_map01.json": SecondGameScene.self
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Setting/objectNameTable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // objectNameTable.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2016/02/23.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct objectNameTable {
12 | static let PLAYER_NAME = "tasuwo"
13 |
14 | static let PLAYER_IMAGE_UP = "plr_up.png"
15 | static let PLAYER_IMAGE_DOWN = "plr_down.png"
16 | static let PLAYER_IMAGE_RIGHT = "plr_right.png"
17 | static let PLAYER_IMAGE_LEFT = "plr_left.png"
18 | static let PLAYER_IMAGE_SET = IMAGE_SET(
19 | UP:
20 | [
21 | ["plr_up_01.png", "plr_up.png"],
22 | ["plr_up_02.png", "plr_up.png"]
23 | ],
24 | DOWN:
25 | [
26 | ["plr_down_01.png", "plr_down.png"],
27 | ["plr_down_02.png", "plr_down.png"]
28 | ],
29 | RIGHT:
30 | [
31 | ["plr_right_01.png", "plr_right.png"],
32 | ["plr_right_02.png", "plr_right.png"]
33 | ],
34 | LEFT:
35 | [
36 | ["plr_left_01.png", "plr_left.png"],
37 | ["plr_left_02.png", "plr_left.png"]
38 | ]
39 | )
40 |
41 | static func getImageBy(direction: DIRECTION) -> String {
42 | switch direction {
43 | case .up:
44 | return objectNameTable.PLAYER_IMAGE_UP
45 | case .down:
46 | return objectNameTable.PLAYER_IMAGE_DOWN
47 | case .right:
48 | return objectNameTable.PLAYER_IMAGE_RIGHT
49 | case .left:
50 | return objectNameTable.PLAYER_IMAGE_LEFT
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Setting/talkerImage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // talkerList.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2016/02/26.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public let TALKER_IMAGE = [
12 | "player": "player.png"
13 | ]
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/Setting/zPositionTable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // zPosition.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2016/02/24.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import SpriteKit
12 |
13 | class zPositionTable {
14 | static let TILE: CGFloat = 0.0
15 | static let BASE_OBJECT_POSITION: CGFloat = 2.0
16 | static let FLAME: CGFloat = 1001.0
17 | static let DIALOG: CGFloat = 1002.0
18 | static let DIALOG_ICON: CGFloat = 1003.0
19 | }
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/common/DIRECTION.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DIRECTION.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/08/04.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum DIRECTION {
12 | case up, down, left, right
13 |
14 | var toString : String! {
15 | switch self {
16 | case .up:
17 | return "UP"
18 | case .down:
19 | return "DOWN"
20 | case .left:
21 | return "LEFT"
22 | case .right:
23 | return "RIGHT"
24 | }
25 | }
26 |
27 | var reverse: DIRECTION {
28 | switch self {
29 | case .up:
30 | return .down
31 | case .down:
32 | return .up
33 | case .left:
34 | return .right
35 | case .right:
36 | return .left
37 | }
38 | }
39 |
40 | static func fromString(_ direction: String) -> DIRECTION? {
41 | switch direction {
42 | case "UP":
43 | return DIRECTION.up
44 | case "DOWN":
45 | return DIRECTION.down
46 | case "RIGHT":
47 | return DIRECTION.right
48 | case "LEFT":
49 | return DIRECTION.left
50 | default:
51 | return nil
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/common/DialogLabel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DialogLabel.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2016/03/18.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class DialogLabel: UILabel {
13 |
14 | let padding = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
15 |
16 | override func drawText(in rect: CGRect) {
17 | let newRect = UIEdgeInsetsInsetRect(rect, padding)
18 | super.drawText(in: newRect)
19 | }
20 |
21 | override var intrinsicContentSize : CGSize {
22 | var intrinsicContentSize = super.intrinsicContentSize
23 | intrinsicContentSize.height += padding.top + padding.bottom
24 | intrinsicContentSize.width += padding.left + padding.right
25 | return intrinsicContentSize
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/common/IMAGE_SET.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IMAGE_SET.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/08/04.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct IMAGE_SET {
12 | let UP: [[String]]
13 | let DOWN: [[String]]
14 | let RIGHT: [[String]]
15 | let LEFT: [[String]]
16 |
17 | func get(_ direction: DIRECTION) -> [[String]] {
18 | switch direction {
19 | case .up:
20 | return self.UP
21 | case .down:
22 | return self.DOWN
23 | case .left:
24 | return self.LEFT
25 | case .right:
26 | return self.RIGHT
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/common/String+subscript.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+subscript.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/08/04.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension String {
12 |
13 | subscript (i: Int) -> Character {
14 | return self[self.characters.index(self.startIndex, offsetBy: i)]
15 | }
16 |
17 | subscript (i: Int) -> String {
18 | return String(self[i] as Character)
19 | }
20 |
21 | subscript (r: Range) -> String {
22 | let start = characters.index(startIndex, offsetBy: r.lowerBound)
23 | let end = characters.index(start, offsetBy: r.upperBound - r.lowerBound)
24 | //let start = startIndex.advancedBy(r.startIndex)
25 | //let end = start.advancedBy(r.endIndex - r.startIndex)
26 | return self[Range(start ..< end)]
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/common/UIButton+title.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIButton+title.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/12/24.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | extension UIButton {
13 | var title: String? {
14 | get {
15 | return self.title(for: .normal)
16 | }
17 | set(v) {
18 | UIView.performWithoutAnimation {
19 | self.setTitle(v, for: .normal)
20 | self.layoutIfNeeded()
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/common/UIButtonAnimated.swift:
--------------------------------------------------------------------------------
1 | //
2 | // common.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2015/07/16.
6 | // Copyright (c) 2015年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SpriteKit
11 | import Foundation
12 |
13 | class UIButtonAnimated: UIButton {
14 |
15 | override init(frame: CGRect) {
16 | super.init(frame: frame)
17 | }
18 |
19 | convenience init() {
20 | self.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
21 | }
22 |
23 | required init(coder aDecoder: NSCoder) {
24 | fatalError("init(coder:) has not been implemented")
25 | }
26 |
27 | override func touchesBegan(_ touches: Set, with event: UIEvent?) {
28 | super.touchesBegan(touches, with: event)
29 | self.touchStartAnimation()
30 | }
31 |
32 | override func touchesCancelled(_ touches: Set, with event: UIEvent?) {
33 | super.touchesCancelled(touches, with: event)
34 | self.touchEndAnimation()
35 | }
36 |
37 | override func touchesEnded(_ touches: Set, with event: UIEvent?) {
38 | super.touchesEnded(touches, with: event)
39 | self.touchEndAnimation()
40 | }
41 |
42 | fileprivate func touchStartAnimation() {
43 | UIView.animate(withDuration: 0.1,
44 | delay: 0.0,
45 | options: UIViewAnimationOptions.curveEaseIn,
46 | animations: {
47 | () -> Void in
48 | self.transform = CGAffineTransform(scaleX: 0.95, y: 0.95);
49 | self.alpha = 0.7
50 | },
51 | completion: nil)
52 | }
53 |
54 | fileprivate func touchEndAnimation() {
55 | UIView.animate(withDuration: 0.1,
56 | delay: 0.0,
57 | options: UIViewAnimationOptions.curveEaseIn,
58 | animations: {
59 | () -> Void in
60 | self.transform = CGAffineTransform(scaleX: 1.0, y: 1.0);
61 | self.alpha = 1
62 | },
63 | completion: nil)
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/SwiftRPG/src/Model/common/myButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // myButton.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/07/29.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SpriteKit
11 | import Foundation
12 |
13 | class myButton: UIButton, NSCopying {
14 |
15 | func setTitle(_ title: String?) {
16 | setTitle(title, for: UIControlState())
17 | setTitle(title, for: UIControlState.highlighted)
18 | }
19 |
20 | func copy(with zone: NSZone?) -> Any {
21 | let newInstance = myButton()
22 |
23 | newInstance.frame = self.frame
24 | newInstance.backgroundColor = self.backgroundColor
25 | newInstance.layer.masksToBounds = self.layer.masksToBounds
26 | newInstance.layer.cornerRadius = self.layer.cornerRadius
27 | // 文字色
28 | newInstance.setTitleColor(
29 | self.titleColor(for: UIControlState()),
30 | for: UIControlState())
31 | newInstance.setTitleColor(
32 | self.titleColor(for: UIControlState.highlighted),
33 | for: UIControlState.highlighted)
34 |
35 | return newInstance
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/SwiftRPG/src/View/FirstGameScene.swift:
--------------------------------------------------------------------------------
1 | //
2 | // myGameScene.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/12/22.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SpriteKit
11 | import UIKit
12 |
13 | class FirstGameScene: GameScene {
14 | required init(size: CGSize, playerCoordiante: TileCoordinate, playerDirection: DIRECTION) {
15 | super.init(size: size, playerCoordiante: playerCoordiante, playerDirection: playerDirection)
16 | }
17 |
18 | required init?(coder aDecoder: NSCoder) {
19 | fatalError("init(coder:) has not been implemented")
20 | }
21 |
22 | override func didMove(to view: SKView) {
23 | super.didMove(to: view)
24 |
25 | /* 地形の読み込み */
26 | if let map = Map(mapName: "sample_map02", frameWidth: self.frame.width, frameHeight: self.frame.height) {
27 | self.map = map
28 | self.map!.addSheetTo(self)
29 | } else {
30 | // TODO: Alert to user and quit game
31 | print("Failed to generate map!!")
32 | return
33 | }
34 |
35 | // 主人公の作成
36 | let playerInitialPosition = TileCoordinate.getSheetCoordinateFromTileCoordinate(self.playerInitialCoordinate!)
37 | let player = Object(name: objectNameTable.PLAYER_NAME,
38 | imageName: objectNameTable.getImageBy(direction: self.playerInitialDirection!),
39 | position: playerInitialPosition,
40 | images: objectNameTable.PLAYER_IMAGE_SET)
41 | player.setCollision()
42 | self.map!.setObject(player)
43 | self.startBehaviors()
44 |
45 | // Config sheet's position
46 | self.map?.sheet?.centerOn(point: player.position, frameWidth: self.frame.width, frameHeight: self.frame.height)
47 |
48 | self.gameSceneDelegate?.startWalking()
49 |
50 | actionButton.isHidden = true
51 |
52 | textBox = Dialog(frame_width: self.frame.width, frame_height: self.frame.height)
53 | textBox.hide()
54 | textBox.setPositionY(Dialog.POSITION.top)
55 | textBox.addTo(self)
56 |
57 | eventDialog.isHidden = true
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/SwiftRPG/src/View/MenuScene.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MenuScene.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2016/03/18.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import SwiftyJSON
12 | import SpriteKit
13 |
14 | protocol MenuSceneDelegate {
15 | func didPressBackButton()
16 | func didSelectedItem(_ indexPath: IndexPath)
17 | }
18 |
19 | class MenuScene: SKScene {
20 | var menuSceneDelegate: MenuSceneDelegate?
21 | var model: MenuSceneModel! {
22 | didSet {
23 | self.contentsView.dataSource = self.model
24 | self.dialog.text = self.model!.defaultMessage
25 | }
26 | }
27 | fileprivate static let SELECTED_ALPHA: CGFloat = 1.0
28 | fileprivate static let DESELECTED_ALPHA: CGFloat = 0.5
29 |
30 | @IBOutlet var sceneView: SKView!
31 | @IBOutlet weak var baseView: UIView!
32 | @IBOutlet weak var backButton: UIButton!
33 | @IBOutlet weak var dialog: UILabel!
34 | @IBOutlet weak var contentsView: UICollectionView!
35 | @IBAction func didPressBackButton(_ sender: AnyObject) {
36 | self.menuSceneDelegate?.didPressBackButton()
37 | }
38 |
39 | override init(size: CGSize) {
40 | super.init(size: size)
41 |
42 | Bundle.main.loadNibNamed("MenuScene", owner: self, options: nil)
43 | self.view?.addSubview(baseView)
44 |
45 | contentsView.delegate = self
46 | contentsView.register(ItemCell.self, forCellWithReuseIdentifier: "cell")
47 |
48 | dialog.layer.borderColor = UIColor.white.cgColor
49 | }
50 |
51 | required init?(coder aDecoder: NSCoder) {
52 | fatalError("init(coder:) has not been implemented")
53 | }
54 | }
55 |
56 | extension MenuScene: MenuSceneModelDelegate {
57 | func updateItemSelect() {
58 | let selectedCell = self.contentsView.cellForItem(at: self.model.selectedIndexPath!) as! ItemCell
59 |
60 | // 選択されたセル以外の全てのセルを非選択にする
61 | for cell in self.contentsView.visibleCells as! [ItemCell] {
62 | if cell == selectedCell { continue }
63 | cell.imageView.alpha = MenuScene.DESELECTED_ALPHA
64 | }
65 |
66 | // 選択されたセルとテキストボックスの描画更新
67 | if selectedCell.imageView.alpha == MenuScene.SELECTED_ALPHA {
68 | selectedCell.imageView.alpha = MenuScene.DESELECTED_ALPHA
69 | self.dialog.text = self.model.defaultMessage
70 | } else {
71 | selectedCell.imageView.alpha = MenuScene.SELECTED_ALPHA
72 | self.dialog.text = self.model.contents[self.model.selectedIndexPath!.row].description
73 | }
74 | }
75 |
76 | func reloadTable() {
77 | self.contentsView.reloadData()
78 | }
79 | }
80 |
81 | extension MenuScene: UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
82 |
83 | // MARK: UICollectionViewDelegate
84 |
85 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
86 | self.menuSceneDelegate?.didSelectedItem(indexPath)
87 | }
88 |
89 | // MARK: UICollectionViewDelegateFlowLayout
90 |
91 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
92 | return CGSize(width: 40, height: 40)
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/SwiftRPG/src/View/MenuScene.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
47 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/SwiftRPG/src/View/TitleScene.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TitleScene.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2015/07/12.
6 | // Copyright (c) 2015年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SpriteKit
11 | import Foundation
12 |
13 | protocol TitleSceneDelegate: class {
14 | func newGameTouched()
15 | }
16 |
17 | class TitleScene: UIView {
18 | var titleSceneDelegate: TitleSceneDelegate?
19 |
20 | @IBOutlet var titleScene: UIView!
21 | @IBOutlet weak var startBtn: UIButton!
22 |
23 | @IBAction func startBtnPressed(_ sender: AnyObject) {
24 | self.titleSceneDelegate?.newGameTouched()
25 | }
26 |
27 | override init(frame: CGRect) {
28 | super.init(frame: frame)
29 |
30 | Bundle.main.loadNibNamed("TitleScene", owner: self, options: nil)
31 | titleScene.frame = frame
32 | addSubview(titleScene)
33 |
34 | titleScene.backgroundColor = UIColor.black
35 |
36 | UIApplication.shared.isNetworkActivityIndicatorVisible = true
37 | }
38 |
39 | required init?(coder aDecoder: NSCoder) {
40 | fatalError("init(coder:) has not been implemented")
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/SwiftRPG/src/View/TitleScene.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
29 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/SwiftRPG/src/View/animation/TransitionToMenuAnimator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TransitionToMenuAnimator.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2016/08/16.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class TransitionBetweenGameAndMenuSceneAnimator: NSObject, UIViewControllerAnimatedTransitioning {
12 | let duration = 0.6
13 | var presenting = true
14 | var originFrame = CGRect.zero
15 |
16 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?)-> TimeInterval {
17 | return duration
18 | }
19 |
20 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
21 | let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
22 | let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
23 |
24 | let toView = toVC?.view!
25 | let fromView = fromVC?.view!
26 | toView!.frame = originFrame
27 | toView!.alpha = 0.0
28 |
29 | let backgroundView = UIView(frame: originFrame)
30 | backgroundView.backgroundColor = UIColor.black
31 |
32 | transitionContext.containerView.addSubview(backgroundView)
33 | transitionContext.containerView.addSubview(toView!)
34 |
35 | UIView.animate(
36 | withDuration: duration/2.0,
37 | delay: 0.0,
38 | options: [],
39 | animations: {
40 | fromView?.alpha = 0.0
41 | },
42 | completion: {
43 | _ in
44 | UIView.animate(
45 | withDuration: self.duration/2.0,
46 | delay: 0.0,
47 | options: [],
48 | animations: {
49 | toView!.alpha = 1.0
50 | },
51 | completion: {
52 | _ in
53 | transitionContext.completeTransition(true)
54 | }
55 | )
56 | }
57 | )
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/SwiftRPG/src/View/components/ItemCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ItemCell.swift
3 | // SwiftRPG
4 | //
5 | // Created by 兎澤佑 on 2016/03/18.
6 | // Copyright © 2016年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class ItemCell: UICollectionViewCell {
13 | @IBOutlet var cellView: UICollectionViewCell!
14 | @IBOutlet weak var imageView: UIImageView!
15 |
16 | override init(frame: CGRect) {
17 | super.init(frame: frame)
18 | Bundle.main.loadNibNamed("ItemCell", owner: self, options: nil)
19 | imageView.backgroundColor = UIColor.black
20 | imageView.alpha = 0.5
21 | addSubview(imageView)
22 | }
23 |
24 | required init?(coder aDecoder: NSCoder) {
25 | fatalError("init(coder:) has not been implemented")
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/SwiftRPG/src/View/components/ItemCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/SwiftRPG/src/View/components/SecondGameScene.swift:
--------------------------------------------------------------------------------
1 | //
2 | // nextGameScene.swift
3 | // SwiftRPG
4 | //
5 | // Created by tasuku tozawa on 2017/01/29.
6 | // Copyright © 2017年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SpriteKit
11 | import UIKit
12 |
13 | class SecondGameScene: GameScene {
14 | required init(size: CGSize, playerCoordiante: TileCoordinate, playerDirection: DIRECTION) {
15 | super.init(size: size, playerCoordiante: playerCoordiante, playerDirection: playerDirection)
16 | }
17 |
18 | required init?(coder aDecoder: NSCoder) {
19 | fatalError("init(coder:) has not been implemented")
20 | }
21 |
22 | override func didMove(to view: SKView) {
23 | super.didMove(to: view)
24 |
25 | if let map = Map(mapName: "sample_map01", frameWidth: self.frame.width, frameHeight: self.frame.height) {
26 | self.map = map
27 | self.map!.addSheetTo(self)
28 | } else {
29 | return
30 | }
31 |
32 | let playerInitialPosition = TileCoordinate.getSheetCoordinateFromTileCoordinate(self.playerInitialCoordinate!)
33 | let player = Object(name: objectNameTable.PLAYER_NAME,
34 | imageName: objectNameTable.getImageBy(direction: self.playerInitialDirection!),
35 | position: playerInitialPosition,
36 | images: objectNameTable.PLAYER_IMAGE_SET)
37 | player.setCollision()
38 | self.map!.setObject(player)
39 | self.startBehaviors()
40 |
41 | self.map?.sheet?.centerOn(point: player.position, frameWidth: self.frame.width, frameHeight: self.frame.height)
42 |
43 | self.gameSceneDelegate?.startWalking()
44 |
45 | actionButton.isHidden = true
46 |
47 | textBox = Dialog(frame_width: self.frame.width, frame_height: self.frame.height)
48 | textBox.hide()
49 | textBox.setPositionY(Dialog.POSITION.top)
50 | textBox.addTo(self)
51 |
52 | eventDialog.isHidden = true
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/SwiftRPGTests/Supporting Files/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/SwiftRPGTests/SwiftRPGTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftRPGTests.swift
3 | // SwiftRPGTests
4 | //
5 | // Created by 兎澤佑 on 2015/06/27.
6 | // Copyright (c) 2015年 兎澤佑. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import XCTest
11 |
12 | class SwiftRPGTests: XCTestCase {
13 |
14 | override func setUp() {
15 | super.setUp()
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | super.tearDown()
22 | }
23 |
24 | func testExample() {
25 | // This is an example of a functional test case.
26 | XCTAssert(true, "Pass")
27 | }
28 |
29 | func testPerformanceExample() {
30 | // This is an example of a performance test case.
31 | self.measure() {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/readme_resources/movie.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tasuwo/SwiftRPG/65a2582b60e80083d14baf721a3ddd7e38aa8093/readme_resources/movie.gif
--------------------------------------------------------------------------------