Advertisement
Guest User

Isometric Engine in Symta

a guest
Mar 1st, 2013
111
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.93 KB | None | 0 0
  1.  
  2. /*
  3. Octree Neighbor Discovery algorithm:
  4. 1. on a given cube's side (square): push random sub-square onto stack
  5. 2. pop top square, then add corresponding to it cube to Neighbors list and for each segment of each side of this square, push all corresponding neigbor square onto stack, if they are part of original cube's side
  6. 3. if stack isnt empty, goto 2.
  7. 4. return Neighbors list
  8.  
  9. Note: hash poped quads by their XYZ
  10.  
  11. Octree Neighbor Discovery algorithm:
  12. 1. на данной стороне (квадрате) данного куба: добавить любой саб-квадрат наверх стека
  13. 2. взять со стека топовый квадрат, внести соответстующий ему куб/воксель в список Neighbors и для каждого отрезка каждой стороны этого квадрата, добавить все лежащие на отрезках квадраты на стэк, при условие что квадраты так же являются частью стороны оригинального куба
  14. 3. если стэк не пуст, перейти к пункту 2
  15. 4. вернуть список Neighbors
  16.  
  17.  
  18. */
  19.  
  20. m:genOctNeibFn N V = \<R E = $V:(`,` E $N) = Q: Get E = Ns: o Q,1,phash:Q
  21. = en:<Qs L F // edge neibs
  22. = 0,<N = {N≥≥L = w r ø}
  23. = Q: F N | Get = [S O:$\[$@(q X Y Z | cng N \_)] L]:Q = H:O,phash
  24. = {hget H Ns; rectsIsect [$@(q X Y Z | del N) L L] R = hset H Q Ns = [Q@!Qs]}
  25. = r N+Q,2>
  26. = Qs>
  27. = l Q | <[[S O:$\[$@(q X Y Z | cng N \_)] L]:Q @Qs]
  28. $@(= [X Y]: q X Y Z | del N = q (`+`N `+`L) (`+`N `-`1) (`+`L `+`N) (`-`1 `+`N)
  29. | m:[A B C D] \(= en !Qs L <N=[$@(ins N V [[A X B] [C Y D]])]>) | j)
  30. = r Qs>
  31. = Ns | m ?,1>
  32.  
  33. octree D = T:0
  34. = Basis:[[1 0 0] [0 1 0] [0 0 1]]
  35. = phash:<[X Y Z]=Z*#10000+Y*#100+X>
  36. = p2i:<[X Y Z] = Z*4+Y*2+X> = merge:<[X:int?@(?|all:Y ptrEq X Y)]=X;R=R>
  37. = nh:<L F = xy 0 0 L L | m F | f ø <M X = hset X,1,phash X M> | m ?,1>
  38. = Get:<P = mod !P D
  39. = with [0 0 0] D P T <:descend O L P T
  40. = T,<S:int? = [S O L] //[Value Origin Dimension]
  41. ;T = !L%2 = PD:P%L = I:PD,p2i = PM: mod P L = descend O+L*PD L PM T,I>
  42. >>
  43. = xNs: genOctNeibFn 0 X = yNs: genOctNeibFn 1 Y = zNs: genOctNeibFn 2 Z
  44. = o get:Get
  45. set:<P V = mod !P D
  46. = !T: merge :: with D P T <:descend L P T = !L%2
  47. = T,<S:int? = {ptrEq L 0 |v ptrEq V S = V; vec 8 I:S | r}
  48. ;T = I: p2i P%L = P: mod P L
  49. = R: descend L P T,I = aset I R,merge T = T>
  50. >
  51. = ø>
  52. neibs:<P = C:P,get = [S O L]:C = [X Y Z]:O
  53. = West: xNs [Y Z L L] [X-1 Y Z] = East: xNs [Y Z L L] [X+L Y Z]
  54. = North: yNs [X Z L L] [X Y-1 Z] = South: yNs [Y Z L L] [X Y+L Z]
  55. = Down: zNs [X Y L L] [X Y Z-1] = Up: zNs [X Y L L] [X Y Z+L]
  56. = l @West @East @North @South @Down @Up>
  57. neibs_old:<P = C:P,get = [S O L]:C = [X Y Z]:O
  58. = West: nh L <[A B] = get [X-1 Y+A Z+B]> = East: nh L <[A B] = get [X+L Y+A Z+B]>
  59. = North: nh L <[A B] = get [X+A Y-1 Z+B]> = South: nh L <[A B] = get [X+A Y+L Z+B]>
  60. = Down: nh L <[A B] = get [X+A Y+B Z-1]> = Up: nh L <[A B] = get [X+A Y+B Z+L]>
  61. = l @West @East @North @South @Down @Up>
  62. pilar:<P = with ø 0 <R (D) = R
  63. ;R Z = [S O L]:[@P Z],get = r [@R [L S]] Z+L>
  64. | <[@H [A V] [B V] @T] = [@H @[[A+B V] @T],r]
  65. ;X = X>>
  66. data:<=T>
  67.  
  68. //octree 16 | <T = T.set [2 0 0] 7 = T.set [2 0 0] 0 = T.get [2 0 0] | p = T'data | p>
  69. //octree 16 | <T = T.set [7 7 7] 123 = T.neibs [7 7 8] | p = T.neibs_old [7 7 8] | p>
  70. //octree 16 | <T = T.set [7 7 7] 123 = T.pilar [7 7] | p>
  71. //abort,c
  72.  
  73.  
  74. Data = “symwars/data”
  75. Sounds = “$Data/sounds”
  76. DataFull = “$(pwd,c)/$Data”
  77.  
  78. Timers =
  79. setSkin “$Data/ui”
  80.  
  81.  
  82. DummyGfx = gfx 1 1
  83. Cursor = gfxLoad “$Data/misc/cursor.png”
  84. RectBack = gfxLoad “$Data/misc/rect_back.png”
  85. RectFront = gfxLoad “$Data/misc/rect_front.png”
  86.  
  87. Dirs = rng 8 | m (?-2)*PI/4 | m:A round [A,cos A,sin]
  88. Dirs4 = l [0 ~1] [1 0] [0 1] [~1 0]
  89.  
  90. Cycle = 0
  91.  
  92. EditorTypes = ø
  93.  
  94. loadTypes What Dir = S:ø = DefAni: o still:[[4 0 0]]
  95. = R: k y :: ls Dir | m ?,pne,1 | k y | uniq | m:N with N “$Dir/$N” ø <:loop N P O
  96. = {O; !O:cfg “$P.txt” | m:[[_ K X]@Xs] [K {Xs=X,<\q=Xs;_=[X@Xs]>;X}] | sort by:lhd}
  97. = {O.editor; !O.editor:N}
  98. = {dir? P = K: ls P | sort | m pne | m:[P N E] loop “$(P,rhd)$N” [P N ø],unpne O = l@!S @K = w loop ø}
  99. = !O.gfxes: v [DummyGfx] {F:“$P.png”,file? = G:F,gfxLoad = O.gfxes |v [[0 0 G'w G'h]] | m:[X Y W H] G.cut X Y W H}
  100. = Ani:ø = !O | k <[[@“ani_” @A] V] = !Ani.A:V = ø; X=√> = {Ani = !O.ani:Ani}
  101. = What,<\obj = l@!EditorTypes.(O.editor) N>
  102. = Default: o height:2 type:N ani:DefAni disp:[0 0]
  103. = l N :: o Default O
  104. >
  105. = o R S
  106.  
  107.  
  108. Acts = loadTypes \act “$Data/act/”
  109. ObjTypes = loadTypes \obj “$Data/obj/”
  110. UnitTypes = loadTypes \unit “$Data/units/”
  111. Types = o ObjTypes UnitTypes
  112.  
  113.  
  114. transparentize Base A = R:Base'copy = D:R.put
  115. = xy 0 0 64 64 | e:[X Y] {ptrEq (and X 1) (and Y 1) = D X Y 255}
  116. = R
  117.  
  118. yoba X Y = [[32 0] [64 16] [32 32]] | m:P abs P-[X Y] | avg | flt
  119.  
  120. genTransition M F T = R:T'copy = P:R.put = G:F.get = MG:M.get
  121. = xy 0 0 64 32 | e:[X Y] {ptrEq (MG X Y) 255|n = P X Y (G X Y)}
  122. = R
  123.  
  124.  
  125. AuxTiles =
  126.  
  127. Tiles
  128. = G: “$Data/til/gfx.png”,gfxLoad = R:ø
  129. = cutTile:<N = G.cut (mod N 20)*64 N%20*64 64 64>
  130. = cfg “$Data/til/map.txt” | e <[N C @Is] = [Is As]: div <[“:” _ _]> Is
  131. = !C | digits 10 | pad 4 0
  132. = {Is; error “one of $N tiles misses gfxes”}
  133. = Gs: Is,<[\stack @Is]=!R.N.stack:Is=ø; _=m cutTile Is>
  134. = As | m ltl | e
  135. <[\a V] = !Gs | m:G transparentize G V
  136. ;[\aux V] = !AuxTiles.N:V
  137. ;[K V] = !R.N.K:V>
  138. = {Gs = !R.N.tiles.C:Gs}>
  139. = Trns:R.trns.tiles = Base:R.base.tiles.[1 1 1 1],0
  140. = !R | m <[K V] = Role:{V.role;K} = Trn:V.trn = NoTrn:Trn,n = Empty:V.empty
  141. = NEs:ø // neighbouring elevations
  142. = Tid:V.tid
  143. = Elev:{V.elev;1} = Lineup:V.no_lineup,n
  144. = Tiler:V.tiling,<\side=<=getSideElev>;_=<=getCornerElev>>
  145. = [Ds Ms Us]:{T:V.tiles=[T T T]; m R.?.tiles V.stack}
  146. = V: o _prn:<=“tile \\$K”> type:<=K> role:<=Role> elev:<=Elev> trn:<=Trn> empty:<=Empty> heavy:<=Empty,n>
  147. order:<A=ø> tid:<=Tid>
  148. slope:<=NEs,<ø=0; [1 1 1 1]=0; [0 0 0 0]=0; _=16>>
  149. render:V.gen,<ø = <:render P Z D U S
  150. = DE:D'empty = DR:D'role
  151. = UH:U'heavy = UR:U'role = UPad:UR≥≤\pad
  152. = Gs:{DR≤≥Role = Ds
  153. ;UR≤≥Role |a UPad,n = Us
  154. ;Ms}
  155. = G:{Lineup |a {UH; UPad; UR≥≤Role} = !NEs:[1 1 1 1] = Gs.NEs
  156. ;√ = !NEs: Tiler,c P Z | m:E {E≤Elev = 0; 1}
  157. = {Gs.NEs; √ = !NEs:[1 1 1 1] = Gs.NEs}}
  158. = G:G,(mod S G,len)
  159. = {NoTrn |v NEs≤≥[1 1 1 1] = w render G}
  160. = Rs: getCornerTrns P Z Role
  161. = {all 1 Rs = w render G}
  162. = !G: genTransition Trns.Rs,0 G Base
  163. = G>
  164. ;\none = <P Z D U S = DummyGfx>
  165. ;T = error “can't generate $T”>
  166. = l K V>
  167. = R | o
  168.  
  169. Tids = R: vec 1024 = Tiles | e:[K T] {I:T'tid = aset I T R} = R
  170.  
  171.  
  172. TileW = 64
  173. TileH = 32
  174.  
  175. world D = Filler:Tiles.base'tid //= OT:D,octree = octSet:OT.set = octGet:OT.get
  176. = !p2i:<[X Y] = mod !X D = mod !Y D = Y*D+X>
  177. = Map:D,octree = Gfxes:xy 0 0 D D | m:_ ø
  178. = !mapGet:Map.get = mapSet:Map.set = !mapNeibs:Map.neibs
  179. = xy 0 0 D D | m:P mapSet [@P 0] Filler
  180. = DD:D*D = Seed: xy 0 0 D D | m:P rand DD | vectorize
  181. = getElev:<P Z = Tids,([@P Z],mapGet,0)'elev>
  182. = getTrn:<P Z = C:Tids,([@P Z],mapGet,0) = C'trn |a C'role>
  183. = !getCornerElev:<P Z
  184. = l (minBy y ::l (getElev P+[~1 ~1] Z) (getElev P+[0 ~1] Z) (getElev P+[~1 0] Z))
  185. (minBy y ::l (getElev P+[ 1 ~1] Z) (getElev P+[0 ~1] Z) (getElev P+[ 1 0] Z))
  186. (minBy y ::l (getElev P+[ 1 1] Z) (getElev P+[0 1] Z) (getElev P+[ 1 0] Z))
  187. (minBy y ::l (getElev P+[~1 1] Z) (getElev P+[0 1] Z) (getElev P+[~1 0] Z))>
  188. = !getSideElev:<P Z = l (getElev P+[0 ~1] Z) (getElev P+[1 0] Z) (getElev P+[0 1] Z) (getElev P+[~1 0] Z)>
  189. = !getCornerTrns:<P Z R
  190. = l (all <(R);ø> ::l (getTrn P+[~1 ~1] Z) (getTrn P+[0 ~1] Z) (getTrn P+[~1 0] Z))
  191. (all <(R);ø> ::l (getTrn P+[ 1 ~1] Z) (getTrn P+[0 ~1] Z) (getTrn P+[ 1 0] Z))
  192. (all <(R);ø> ::l (getTrn P+[ 1 1] Z) (getTrn P+[0 1] Z) (getTrn P+[ 1 0] Z))
  193. (all <(R);ø> ::l (getTrn P+[~1 1] Z) (getTrn P+[0 1] Z) (getTrn P+[~1 0] Z))
  194. | m <ø=0;_=1>>
  195. = getPilar:Map.pilar
  196. = updPilarGfxes:<P = I:P,p2i = S:aget I Seed = Cs:P,getPilar = Gs:ø = Z:0 = B:Tids,0
  197. = Cs|e<[N V] = {V≥≤0 = !B:Tids,0 = !Z+N = l@!Gs N = w r ø}
  198. = C:Tids,V
  199. = times I:N (
  200. = A:{I+1≤N = B; Tids,([@P Z+1],mapGet,0)}
  201. // B=Below, A=Above, S=Seed
  202. = l@!Gs (C.render P Z B A S)
  203. = !B:C
  204. = !Z+1
  205. )
  206. >
  207. = aset I Gs Gfxes>
  208. = !drawPilar:<P BX BY B CI = mod !P Dim = I:P,p2i = Gs:aget I Gfxes = C:ptrEq I CI = Z:0 = Os:P,<[X Y]=Objs.X.Y>
  209. = Gs|e <G:int? = !Z+G
  210. ;G = {C = B BX BY-RectBack'h+32-Z*32 RectBack}
  211. = B BX BY-G'h+32-Z*32 G
  212. = Os.(Z+1) | e:O O.render B BX BY-Z*32
  213. = {C = B BX BY-RectFront'h+32-Z*32 RectFront}
  214. = !Z+1>
  215. //= {C = B BX BY-Cursor'h+32-E*32 Cursor}
  216. >
  217. = updElev:<P = Dirs | m:D updPilarGfxes P+D = updPilarGfxes P>
  218. = O: o dim:<=D>
  219. height:<XY = Cs:XY,getPilar = Z:D-Cs,rhd,0>
  220. push:<XY C = mapSet [@XY XY,height] C'tid = updElev XY>
  221. pop:<XY = {H:XY,height,pos? = mapSet [@XY H-1] 0 = updElev XY}>
  222. = xy 0 0 D D | m:P updPilarGfxes P
  223. = O
  224.  
  225. Dim = 16
  226. World = world Dim
  227. Objs = ø
  228. Sel = ø
  229.  
  230. DirToAnim = u [0 0]:[0 ø] [0 ~1]:[1 √] [1 ~1]:[1 √] [1 0]:[0 √] [1 1]:[0 √]
  231. [0 1]:[0 ø] [~1 1]:[0 ø] [~1 0]:[1 ø] [~1 ~1]:[1 √]
  232.  
  233.  
  234.  
  235. //L=limit, S=entry_point, F=search_target H=heuristic M=can_move_to? N=gets_neibs_points
  236. aStar L S F H M N = Vs:ø /*visited cells*/ = u 0:[S ø 0] |
  237. <:next [[_ O]@Os] = C:O,0 = G:O,2
  238. = {F C = O,<[N P:y @_]=[@P,r N]> // backtrace path
  239. ;√ = {G≤L = NG:G+1 = N C | k M | s Vs.? | e <N = hins NG+(H N) [N O NG] !Os = !Vs.N:√>}
  240. = next Os}>
  241.  
  242. findPath E T = [TS [TX TY TZ] TL]:T,mapGet
  243. = aStar 1024
  244. E
  245. <[X Y Z] = inRng TX TX+TL X |a inRng TY TY+TL Y |a inRng TZ TZ+TL Z>
  246. <P = T-P | m abs | sum>
  247. <P = P,mapGet,0 ≤ 100>
  248. <P = P,mapNeibs | m ?,1>
  249.  
  250. obj N SP Z:{ø} = T:{Types.N; error “obj: cant find $N”} = H:T.height = G:DummyGfx
  251. = [DX DY]: T.disp = FlipX:ø = DI:0 = Slope:DY = !DX+32 = Empty:T.empty
  252. = Anim:ø = Act:Acts.still = P:ø = Bs:ø = BG:ø = Hits:T.hp
  253. = O: o _prn:<=“obj \\$N [$P] Z:$Z”>
  254. selectable?:<=Hits,pos?>
  255. role:<=N> slope:<=0> trn:<=ø> empty:<=Empty> heavy:<=ø>
  256. lookAt:<D = DirToAnim.(sign D-P),<[I F] = !DI:I = !FlipX:F>>
  257. upd:<= Act.type,<\still =
  258. ;\move = [DXY DZ]:Act.dst
  259. = findPath [@P Z] [@DXY DZ] | p
  260. = order Acts.still
  261. /*= F:{Act.flood; √ = F:World'newFlood = o !Act flood:F = F}
  262. = {NP: F.next P
  263. //= p [P NP XY]
  264. = lookAt NP
  265. = move NP Z
  266. = {P,p2i≥≤XY,p2i = order Acts.still}
  267. }*/>
  268. = !Anim | <[[W@Ds]@As] = !G:T.gfxes,(Ds,DI) = sched W upd = As
  269. ;ø = r {T.ani.(Act.show); T.ani.still; error “no still anim for $O”}
  270. ;[\attack @As] = r As>
  271. = >
  272. render:<B X Y = //!Slope:BelowCell'slope+DY
  273. = B X+DX-G'w%2 Y-16-G'h+Slope G FX:FlipX>
  274. s_flipped:<V=!FlipX:V>
  275. flipped:<=FlipX>
  276. order:<O=!Act:O>
  277. height:<=Z>
  278. move:<:move NP NZ = mod !NP Dim = NZ:{NZ; NP = World.height NP}
  279. = P,<[X Y] = s (ptrEq ? Me) !Objs.X.Y.Z
  280. ;_ = sched 0 upd>
  281. = !P:NP = P,<ø = w move ø> //remove
  282. = !Z:NZ = P,<[X Y] = l@!Objs.X.Y.Z Me>
  283. >
  284. = O.move SP Z
  285. = {O'selectable? = l@!Sel O}
  286. = O
  287.  
  288.  
  289. Scheds =
  290. sched T F = l@!Scheds.(Cycle+T) F
  291.  
  292. updObjs
  293. = while S:Scheds.Cycle (= ~@!Scheds.Cycle = e c S)
  294. = !Cycle+1
  295.  
  296. //VO: View Origin
  297. //BO: Blit Origin
  298. //CXY: XY of cell under cursor
  299. //CI: index of cell under cursor
  300. //A: mice anchor
  301. //LA: lmb anchor
  302. //MZ: mice Z
  303. viewport W H = Paused:ø = A:ø = LA:ø = M:[0 0] = MZ:0 = CXY:[0 0] = CI:0 = BO:[360 ~170] = VO:[0 0] = G: gfx W H C:3
  304. = Brush:[\tile \base] = Visibles:ø = Notes:ø = Speed:1 = MX:0 = MY:0 = LKs:ø = o
  305. notify:<P Text Life:{6} = l @!Notes [time,c+Life Text]>
  306. init:<= !Paused:ø = !Notes:ø = !Visibles:ø>
  307. w:<=W> h:<=H>
  308. render:<= G.clear C:#929292/*#00A0C0*/ = B:G.blit = D:32 = [TX TY]:BO = Y:0 = YY:D
  309. = for (; Y≤D; !Y+1) times N:Y+1
  310. (= BX:TX-Y*TileH + N*TileW = BY:TY+Y*TileH%2
  311. = drawPilar [N Y-N]+VO BX BY B CI)
  312. = for (; YY≥0; !Y+1) times N:!YY-1
  313. (= BX:TX-(YY-1)*TileH + N*TileW = BY:TY+Y*TileH%2
  314. = drawPilar [D-YY+N D-N-1]+VO BX BY B CI)
  315. = G>
  316. mapToView:<P=[X Y]:P-VO=[X*TileW-Y*TileW X*TileH-Y*TileH]%2+BO>
  317. viewToMap:<P=[X Y]:P-BO=!X-32= mod [Y*TileW+X*TileH Y*TileW-X*TileH]%(TileW*TileH)+VO Dim>
  318. order:<A XY Z = A:o Acts.A dst:[XY Z] = Sel |e:U U.order A>
  319. update:<= Ks:GUI'keys = key:<X = Ks.X |a LKs.X,n> = Z:World.height CXY = Us:CXY,<[X Y]=Objs.X.Y.Z>
  320. //= q (left ~1 0) (right 1 0) (up 0 ~1) (down 0 1) | filter:[N@P] {Ks.N=P} | f !VO `+`
  321. = q (left ~1 1) (right 1 ~1) (up ~1 ~1) (down 1 1) | filter:[N@P] {Ks.N=P,sign} | f !VO `+`
  322. = {key\m = order\move CXY Z}
  323. = {key\l = Sel |e:U U.lookAt CXY}
  324. = !LKs:Ks
  325. //= !MX+Speed = !MY+Speed
  326. //= {GUI'keys.delete = rmXY M,screenToCell}
  327. //= {Music'playing?; !Music: sideMusic,c | snd Music:√}
  328. = {A,n =
  329. ;[\obj Type]: Brush = {Us,n = O:obj EditorTypes.Type,pick CXY = O.s_flipped GUI'keys.lctrl}
  330. ;[\unit Type]: Brush = {Us,n = O:obj Type CXY}
  331. ;Z≥≥MZ =
  332. ;[\tile Type]: Brush = World.push CXY Tiles.Type
  333. }
  334. = {LA,n; Z≤≤MZ
  335. ;Brush,0,<\unit;\obj> = Us|e:U U.move ø ø
  336. ;Brush,0,<\tile> = World.pop CXY}
  337. = dup Speed updObjs,c = k ?'selectable? !Sel
  338. = √>
  339. pause:<=!Paused:√> unpause:<=!Paused:ø> paused?:<=Paused>
  340. s_brush:<NB=!Brush:NB>
  341. input:
  342. <[\mice_move _ P] = !P+[0 32] = !M:P = !CXY:P,viewToMap = !CI:CXY,p2i
  343. ;[\mice_left y XY] = !A:XY = !MZ:(World.height CXY)+1
  344. ;[\mice_left ø XY] = !A:ø
  345. ;[\mice_right y XY] = !LA:XY = !MZ:(World.height CXY)-1
  346. ;[\mice_right ø XY] = !LA:ø
  347. >
  348.  
  349. VP = R:viewport 600 600-2 = timer 1/12 <={R'paused?,n=R'update}=√> = R
  350. Panel = ObjList: list L:31 W:160 F:<N=VP.s_brush [\obj N]> :: m ?,0 EditorTypes
  351. = TileList: list L:31 W:160 F:<N=VP.s_brush [\tile N]> :: Tiles | s AuxTiles.(?,1'type) | m ?,0
  352. = UnitList: list L:31 W:160 F:<N=VP.s_brush [\unit N]> :: m ?,0 UnitTypes
  353. = Tabs: tabs \tile :: u tile:TileList obj:ObjList unit:UnitList
  354. = Labels2: box\h :: l (button W:\medium H:\small “Unit” <=VP.s_brush [\unit UnitList'value]=Tabs.s_tab\unit>)
  355. (button W:\medium H:\small “ToDo” S:{\disabled} <>)
  356. = Labels: box\h :: l (button W:\medium H:\small “Tile” <=VP.s_brush [\tile TileList'value]=Tabs.s_tab\tile>)
  357. (button W:\medium H:\small “Obj” <=VP.s_brush [\obj ObjList'value]=Tabs.s_tab\obj>)
  358. = VP.s_brush [\tile \base]
  359. = box\v :: l Labels Labels2 Tabs
  360.  
  361.  
  362. main N = {N = game N} = gui (box\h :: l Panel VP)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement