Advertisement
bullebeur

Untitled

Dec 8th, 2018
425
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 22.77 KB | None | 0 0
  1. # Starter code for an adventure type game.
  2. # University of Utah, David Johnson, 2017.
  3. # This code, or code derived from this code, may not be shared without permission.
  4.  
  5. # Current implementations:
  6. # Right now this game has most of the elements that the fully fleshed out version will have.
  7. # We have 5 items in the game at the moment that the character can add to their inventory.
  8. # When an item is added to the inventory the player can press the tab key to bring the inventory up.
  9. # When the player receives the scanner item they can press caps lock to use the scanner and receive a hint.
  10. # If the player touches the flames from the fire then the player gets sent to a different location on the map.
  11. # After receiving the scanner the player can receive a message to avoid the fire character.
  12.  
  13.  
  14. import sys, pygame, math, random
  15.  
  16.  
  17. # This function loads a series of sprite images stored in a folder with a
  18. # consistent naming pattern: sprite_# or sprite_##. It returns a list of the images.
  19. def load_piskell_sprite(sprite_folder_name, number_of_frames):
  20. frame_counts = []
  21. padding = math.ceil(math.log(number_of_frames, 10))
  22. for frame in range(number_of_frames):
  23. folder_and_file_name = sprite_folder_name + "/sprite_" + str(frame).rjust(padding, '0') + ".png"
  24. frame_counts.append(pygame.image.load(folder_and_file_name).convert_alpha())
  25.  
  26. return frame_counts
  27.  
  28.  
  29.  
  30.  
  31.  
  32.  
  33.  
  34.  
  35. #This version of the game has no tiles and works directly off a png->pixel background. There
  36. # are two 'coordinate systems' in use: a map coordinate system that is static, such that the player moves one map
  37. # map unit per frame and all items have fixed map coordinates, and a pixel coordinate system that remains oriented
  38. #on the player. The ration between these two coordinate systems is the 'pixelscale', defined below.
  39.  
  40.  
  41.  
  42. # loc_init takes in coordinates on a fixed map scale and outputs the pixel coordinates.
  43. # The '350' and '250' are because the player is at the center of the screen, at (350,250).
  44. def loc_init(x,y,pixelscale):
  45.  
  46. return (x*pixelscale[0] +350,y*pixelscale[1]+250)
  47.  
  48.  
  49.  
  50. # The main loop handles most of the game
  51. def main():
  52. # Initialize pygame
  53. pygame.init()
  54.  
  55. screen_size = width, height = (700, 500)
  56. screen = pygame.display.set_mode(screen_size)
  57.  
  58. #this determines the number of pixels in one 'map unit', effectively setting
  59. # a scale size for the game. Previously this would have corresponded to tile_rect.width and tile_rect.height
  60. pixelscale = (16,16)
  61.  
  62. # create the hero character
  63. hero = load_piskell_sprite("hero", 11)
  64. hero_rect = hero[0].get_rect()
  65. hero_rect.center = (350, 250)
  66.  
  67. # create the aliem character
  68. aliem = load_piskell_sprite("aliem", 15)
  69. aliem_rect = hero[0].get_rect()
  70. aliem_rect.center = (750, 150)
  71.  
  72. # create the fire "character"
  73. fire = load_piskell_sprite("flame", 13)
  74. fire_rect = fire[0].get_rect()
  75. fire_rect.center = loc_init(191,102,pixelscale)
  76.  
  77. fire2 = load_piskell_sprite("flame", 13)
  78. fire2_rect = fire[0].get_rect()
  79. fire2_rect.center = loc_init(90,120,pixelscale)
  80.  
  81. # this defines the start and end points of the fire bouncing loop.
  82. # by making these rectangles, they transform as the player moves just like
  83. # all other background sprites, allowing the bouncing to occur in the map
  84. # reference frame, not the player reference frame.
  85. firestart_rect = fire_rect.copy()
  86. firestart_rect.center = loc_init(191,102,pixelscale)
  87.  
  88. fireend_rect = fire_rect.copy()
  89. fireend_rect.center = loc_init(106,101,pixelscale)
  90.  
  91. fire2start_rect = fire2_rect.copy()
  92. fire2start_rect.center = loc_init(90,120,pixelscale)
  93.  
  94. fire2end_rect = fire2_rect.copy()
  95. fire2end_rect.center = loc_init(133,120,pixelscale)
  96.  
  97.  
  98. frame_count = 0;
  99.  
  100.  
  101. # add a maintenance card
  102. maintenance = pygame.image.load("maintenance.png").convert_alpha()
  103. maintenance_rect = maintenance.get_rect()
  104. maintenance_rect.center = loc_init(66,60,pixelscale)
  105.  
  106. # add some fuel
  107. fuel = pygame.image.load("fuel.png").convert_alpha()
  108. fuel_rect = fuel.get_rect()
  109. fuel_rect.center = loc_init(40,133,pixelscale)
  110.  
  111. # add a gun
  112. gun = pygame.image.load("gun.png").convert_alpha()
  113. gun_rect = gun.get_rect()
  114. # gun_rect.center = 750, 400
  115. gun_rect.center = loc_init(85,93,pixelscale)
  116.  
  117. # add a blow torch
  118. torch = pygame.image.load("blow_torch.png").convert_alpha()
  119. torch_rect = torch.get_rect()
  120. torch_rect.center = loc_init(107,169,pixelscale)
  121.  
  122. # add a scanner
  123. scanner = pygame.image.load("scanner.png").convert_alpha()
  124. scanner_rect = scanner.get_rect()
  125. scanner_rect.center = loc_init(125,46,pixelscale)
  126.  
  127. #The starting position of the human character
  128. HumanMaploc = 100,100
  129. (mapx, mapy) = HumanMaploc
  130.  
  131.  
  132.  
  133.  
  134. map = pygame.image.load("map/Map.png").convert()
  135. map_rect = map.get_rect()
  136. map_rect.center = 3540,1930
  137.  
  138.  
  139. # Load a minimap
  140. world = pygame.image.load("map/Map.png").convert()
  141. world_rect = world.get_rect()
  142. # Set the offset into the map
  143.  
  144. minimap = world.copy()
  145. minimap_rect = minimap.get_rect()
  146.  
  147. # The clock helps us manage the frames per second of the animation
  148. clock = pygame.time.Clock()
  149.  
  150. # Mostly used to cycle the animation of sprites
  151. frame_count = 0;
  152. Hero_anim_count=0
  153.  
  154. # variable to show if we are still playing the game
  155. playing = True
  156.  
  157. # variable for hero direction
  158. is_facing_left = True
  159.  
  160. # Variable to track text on the screen. If you set the dialog string to something and set the position and the
  161. # counter, the text will show on the screen for dialog_counter number of frames.
  162. dialog_counter = 0
  163. dialog = ''
  164. dialog_position = (0, 0)
  165.  
  166. dialog2_counter = 0
  167. dialog2 = ''
  168. dialog2_position = (0, 0)
  169.  
  170. # Load font
  171. pygame.font.init() # you have to call this at the start,
  172. # if you want to use this module.
  173. myfont = pygame.font.SysFont('Comic Sans MS', 13)
  174. invfont = pygame.font.SysFont('Helvetica', 20)
  175.  
  176.  
  177. # create the inventory and make it empty
  178. inventory = {}
  179.  
  180. # This list should hold all the sprite rectangles that get shifted with a key press.
  181. rect_list = [aliem_rect, fire_rect, gun_rect, fuel_rect, maintenance_rect, torch_rect, scanner_rect, map_rect, firestart_rect,
  182. fireend_rect,fire2_rect,fire2start_rect,fire2end_rect]
  183.  
  184. for rect in rect_list:
  185. rect.move_ip(-HumanMaploc[0]*pixelscale[0], -HumanMaploc[1]*pixelscale[1])
  186.  
  187. just_used_scanner = False
  188. RoadSlow = False
  189.  
  190. # This function checks to see if a pixel color corresponds to a hard boundary that the player
  191. # should not be able to pass through.
  192. def hitting_obstacle_query(PixelColor):
  193. hitting_obstacle = False
  194. # blue walls of the facility
  195. if (153 < PixelColor[0] < 160) and (195 < PixelColor[1] < 200) and (215 < PixelColor[2] < 220):
  196. hitting_obstacle = True
  197. # green doors of the facility
  198. if (16 <= PixelColor[0] <= 16) and (45 <= PixelColor[1] <= 45) and (
  199. 14 <= PixelColor[2] <= 14) and "torch" and "fuel" not in inventory:
  200. hitting_obstacle = True
  201. # red doors of the facility
  202. if (62 <= PixelColor[0] <= 62) and (15 <= PixelColor[1] <= 15) and (
  203. 13 <= PixelColor[2] <= 13) and "maintenance" not in inventory:
  204. hitting_obstacle = True
  205.  
  206. return (hitting_obstacle)
  207.  
  208. # Loop while the player is still active
  209. while playing:
  210. # start the next frame
  211. screen.fill((170, 190, 190))
  212.  
  213. # Check events by looping over the list of events
  214. for event in pygame.event.get():
  215. if event.type == pygame.QUIT:
  216. playing = False
  217.  
  218. # diagnostics - outputs location on the map grid
  219. print(mapx)
  220. print(mapy)
  221.  
  222.  
  223.  
  224.  
  225.  
  226.  
  227.  
  228.  
  229. movement_x = 0
  230. movement_y = 0
  231.  
  232. keys = pygame.key.get_pressed()
  233. # RoadSlow is a boolean that says whether you're being slowed by being on the road or not.
  234. # Being slowed slows the animation time for the walking animation and reduces character speed
  235. # by allowing motion/animation only every other frame.
  236.  
  237. # if you're being slowed, spend one frame doing nothing
  238. if (RoadSlow):
  239. RoadSlow = False
  240. Hero_anim_count -=1
  241.  
  242. else:
  243. # if you're not being slowed, or if this is in the half of the slowed frames that do allow motion, proceed here
  244.  
  245. # deltamapx/y determine movement in the map coordinate system and move by one unit per frame of motion.
  246. #deltamovement_x/y determine movement in the pixel coordinate system and move by one pixelscale unit per
  247. # frame of motion.
  248.  
  249. deltamapx=0
  250. deltamapy=0
  251. deltamovement_x=0
  252. deltamovement_y=0
  253.  
  254.  
  255. # 'is_facing_left' is a boolean that determines which direction the player sprite should be facing.
  256. if keys[pygame.K_LEFT]:
  257. is_facing_left = False
  258. deltamovement_x += -pixelscale[0]
  259. deltamapx += -1
  260. if keys[pygame.K_RIGHT]:
  261. is_facing_left = True
  262. deltamovement_x += pixelscale[0]
  263. deltamapx += 1
  264. if keys[pygame.K_UP]:
  265. deltamovement_y += -pixelscale[1]
  266. deltamapy += -1
  267. if keys[pygame.K_DOWN]:
  268. deltamovement_y += +pixelscale[1]
  269. deltamapy += 1
  270.  
  271.  
  272. # apply the appropriate changes to the coordinates
  273. mapx += deltamapx
  274. mapy += deltamapy
  275. movement_x +=deltamovement_x
  276. movement_y +=deltamovement_y
  277.  
  278.  
  279. # 'footpos' is the position of the player's feet in map coordinates. When dealing with detection, or walking on
  280. # different terrain, it makes more sense to ask 'where are the player's feet?' than 'where is the player's head?'
  281. # footpos is positioned 3 pixels below the player's head.
  282.  
  283. footposx = int(min(max(mapx,0),world.get_width()/pixelscale[0]))
  284. footposy = int(min(mapy+3,int(world.get_height()/pixelscale[1] - 1)))
  285.  
  286. # PixelCol finds the color of the pixel at the player's feet
  287. PixelCol = world.get_at((footposx*pixelscale[0],footposy*pixelscale[1]))
  288.  
  289. #if the player's feet are on the slippery road, distinguished by its brown-purple color, set RoadSlow to true.
  290. if (100<PixelCol[0]<155) and (70<PixelCol[1]<115) and (PixelCol[2]==84):
  291. RoadSlow = True
  292.  
  293.  
  294.  
  295. # If the player is walking into an obstacle, he should not move, regardless of the circumstances.
  296. # The code below calls 'hitting_obstacle_query' and checks if the player is walking into a wall. If
  297. # so, all motion changes are reversed immediately.
  298. if hitting_obstacle_query(PixelCol):
  299. mapx -= deltamapx
  300. mapy -= deltamapy
  301. movement_x -=deltamovement_x
  302. movement_y -=deltamovement_y
  303.  
  304. # PixelCol is useful for diagnostics to see what color/terraint the player is on.
  305. print(PixelCol)
  306.  
  307. # Boundaries for fire "character"
  308.  
  309. def bounce_rect_between_two_positions(rect, start_pos, end_pos, num_frame, frame_count):
  310. if frame_count % num_frame < num_frame / 2:
  311. new_pos_x = start_pos[0] + (end_pos[0] - start_pos[0]) * (
  312. frame_count % (num_frame / 2)) / (
  313. num_frame / 2)
  314. new_pos_y = start_pos[1] + (end_pos[1] - start_pos[1]) * (
  315. frame_count % (num_frame / 2)) / (
  316. num_frame / 2)
  317. else:
  318. new_pos_x = end_pos[0] + (start_pos[0] - end_pos[0]) * (frame_count % (num_frame / 2)) / (
  319. num_frame / 2)
  320. new_pos_y = end_pos[1] + (start_pos[1] - end_pos[1]) * (frame_count % (num_frame / 2)) / (
  321. num_frame / 2)
  322. rect.center = (new_pos_x, new_pos_y)
  323.  
  324. bounce_rect_between_two_positions(fire_rect, firestart_rect.center, fireend_rect.center, 100, frame_count)
  325. bounce_rect_between_two_positions(fire2_rect, fire2start_rect.center, fire2end_rect.center, 100, frame_count)
  326. bounce_rect_between_two_positions(aliem_rect, fire2start_rect.center, fire2end_rect.center, 100, frame_count)
  327.  
  328.  
  329.  
  330.  
  331.  
  332.  
  333. # if the player is not moving at all, 'is_standing_still' is true. This replaces the walking animation with a simple
  334. #standing animation.
  335. is_standing_still = False
  336.  
  337. if not keys[pygame.K_LEFT] and not keys[pygame.K_RIGHT] and not keys[pygame.K_UP] and not keys[pygame.K_DOWN]:
  338. is_standing_still = True
  339.  
  340.  
  341.  
  342. # boudaries of the map, with a little extra buffer. Prevents movement off the map.
  343. if mapx < 0:
  344. mapx = 0
  345. movement_x = 0
  346. if mapx > int(world.get_width()/pixelscale[0] - 1) :
  347. mapx = int(world.get_width()/pixelscale[0] - 1)
  348. movement_x = 0
  349. if mapy < 0:
  350. mapy = 0
  351. movement_y = 0
  352. if mapy > int(world.get_height()/pixelscale[1] - 2) :
  353. mapy = int(world.get_height()/pixelscale[1] -2)
  354. movement_y = 0
  355.  
  356.  
  357.  
  358. # Move all the sprites in the scene by movement amount.
  359. # You can still move the rect of an individual sprite to make
  360. # it move around the landscape.
  361. for rect in rect_list:
  362. rect.move_ip(-movement_x, -movement_y)
  363.  
  364.  
  365.  
  366.  
  367.  
  368.  
  369.  
  370. # Check for touching the gun.
  371. if hero_rect.colliderect(gun_rect) and "gun" not in inventory:
  372. inventory["gun"] = "Gun"
  373. dialog = "Gun added to inventory"
  374. dialog_counter = 30
  375. dialog_position = (300, 200)
  376.  
  377. # Check for touching the blow torch.
  378. if hero_rect.colliderect(torch_rect) and "blow torch" not in inventory:
  379. inventory["torch"] = "Blow Torch"
  380. dialog = "Blow torch added to inventory"
  381. dialog_counter = 30
  382. dialog_position = (300, 200)
  383.  
  384. # Check for touching the fuel.
  385. if hero_rect.colliderect(fuel_rect) and "fuel" not in inventory:
  386. inventory["fuel"] = "Fuel"
  387. dialog = "Fuel added to the inventory"
  388. dialog_counter = 30
  389. dialog_position = (300, 200)
  390.  
  391. # Check for touching the maintenance card.
  392. if hero_rect.colliderect(maintenance_rect) and "maintenance" not in inventory:
  393. inventory["maintenance"] = "Maintenance Card"
  394. dialog = "Maintenance Card added to inventory"
  395. dialog_counter = 30
  396. dialog_position = (300, 200)
  397.  
  398. # Check for touching the scanner.
  399. if hero_rect.colliderect(scanner_rect) and "scanner" not in inventory:
  400. inventory["scanner"] = "Scanner"
  401. dialog = "Scanner added to inventory"
  402. dialog_counter = 30
  403. dialog_position = (300, 200)
  404.  
  405.  
  406.  
  407.  
  408.  
  409.  
  410.  
  411.  
  412. # here is a simple transport mechanism if the player touches the fire and aliem.
  413. # transportloc gives the location in map coordinates where the player is transported
  414. transportloc = (100, 100)
  415.  
  416.  
  417. if hero_rect.colliderect(aliem_rect) and "gun" not in inventory:
  418. delta_x = -mapx + transportloc[0]
  419. delta_y = -mapy + transportloc[1]
  420.  
  421. # delta_x and delta_y give the distance, in map coordinates, from the player's location to the transport location
  422. if hero_rect.colliderect(fire_rect) or hero_rect.colliderect(fire2_rect):
  423. delta_x = -mapx + transportloc[0]
  424. delta_y = -mapy + transportloc[1]
  425.  
  426.  
  427. # the entire map is displaced to the transport location
  428. mapx = transportloc[0]
  429. mapy = transportloc[1]
  430.  
  431. # all rectangles must be translated in a manner consistent witht he map transformation. The different scaling between the map
  432. # coordinate system and the pixel scale is the reason for the factor of tile_rect.width/height.
  433. for rect in rect_list:
  434. rect.move_ip(-delta_x * pixelscale[0], -delta_y * pixelscale[1])
  435.  
  436. if "scanner" in inventory:
  437. dialog = 'Whoa! Watch out for flames, their positronic field might teleport you to an entangled location!'
  438. dialog_counter = 30
  439. dialog_position = (200, 200)
  440.  
  441.  
  442.  
  443.  
  444.  
  445.  
  446.  
  447.  
  448.  
  449. # Draw the map
  450. screen.blit(map, map_rect)
  451.  
  452. #draw the bouncing fire
  453. screen.blit(fire[frame_count % len(fire)], fire_rect)
  454. screen.blit(fire2[frame_count % len(fire)], fire2_rect)
  455. screen.blit(aliem[frame_count % len(aliem)], aliem_rect)
  456.  
  457. # Only draw the gun if it hasn't been picked up
  458. if "gun" not in inventory:
  459. screen.blit(gun, gun_rect)
  460. if "fuel" not in inventory:
  461. screen.blit(fuel, fuel_rect)
  462. if "maintenance" not in inventory:
  463. screen.blit(maintenance, maintenance_rect)
  464. if "torch" not in inventory:
  465. screen.blit(torch, torch_rect)
  466. if "scanner" not in inventory:
  467. screen.blit(scanner, scanner_rect)
  468.  
  469. # Pick the sprite frame to draw
  470. hero_sprite = hero[Hero_anim_count % len(hero)]
  471. # Flip the sprite depending on direction
  472. if not is_facing_left:
  473. hero_sprite = pygame.transform.flip(hero_sprite, True, False)
  474. if is_standing_still:
  475. hero_sprite = hero[0]
  476. screen.blit(hero_sprite, hero_rect)
  477.  
  478. #Draw a rectangle around something
  479. # pygame.draw.rect(screen, (0, 0, 0), hero_rect, 1)
  480.  
  481. # Show items in inventory.
  482. # Respond differently depending on inventory status
  483.  
  484. # If the 'tab' button is pressed, the public names of the items in the inventory
  485. # (the values in the 'inventory' dictionary) fill up the list 'dialog2vec'
  486. # and are presented on screen for as long as the player holds down tab.
  487. dialog2vec = []
  488. if keys[pygame.K_TAB]:
  489.  
  490. dialog2vec = ["Inventory:"]
  491. for x in inventory:
  492. dialog2vec.append(inventory[x]) # We change this line to call the value instead of the key
  493. dialog2_counter = 5000000
  494. dialog2_position = (0, 0)
  495.  
  496. if dialog2vec:
  497. counter = 0
  498. pygame.draw.rect(screen, (200, 220, 220), [0, 0, 200, 200])
  499. for invitem in dialog2vec:
  500. textsurface2 = invfont.render(invitem, True, (0, 0, 0))
  501. screen.blit(textsurface2, (0, 0 + 35 * counter))
  502. counter += 1
  503. # Track how long the dialog is on screen
  504. dialog2_counter -= 1
  505. if dialog2_counter == 0:
  506. screen.blit(textsurface2, (0, 0 + 35 * counter))
  507.  
  508. #the "35*counter" is used to space out inventory items verticallye
  509.  
  510. # draw any dialog
  511. if dialog:
  512. pygame.draw.rect(screen, (200, 220, 220), [50, 425, 600, 50])
  513. textsurface = myfont.render(dialog, True, (0, 0, 0))
  514. screen.blit(textsurface, (75, 430))
  515. # Track how long the dialog is on screen
  516. dialog_counter -= 1
  517. if dialog_counter == 0:
  518. dialog = ''
  519.  
  520.  
  521.  
  522. #draw the minimap. First we make a smaller version of the game map and the hero sprite.
  523. # These are drawn into the upper right corner. The position of the human in the map is converted to its
  524. #appropriate position on the minimap
  525.  
  526. # the minimap is 200 pixels across, 100 down
  527. minimap_size = (200,100)
  528.  
  529. # the minimap is positioned to be at the upper right of the map
  530. minimap_pos = (500,0)
  531.  
  532. # scale the game map to the appropriate size
  533. minimap2 = pygame.transform.scale(minimap,minimap_size)
  534.  
  535. # only show the minimap if the 'm' key is hit
  536. if keys[pygame.K_m]:
  537.  
  538. # make the minimap sprite (here he's 5x10 pixels)
  539. hero_sprite_mini = pygame.transform.scale(hero_sprite,(5,10))
  540. # take the hero's location and scale it by the ratio of the minimaps size to the real map size
  541. hero_mmposx = mapx*minimap_size[0]/400
  542. hero_mmposy = mapy*minimap_size[1]/212
  543.  
  544. #draw the minimap
  545. screen.blit(minimap2, (500, 0))
  546.  
  547. # draw the hero (note that the position is translated because of the minimap position)
  548. screen.blit(hero_sprite_mini,(hero_mmposx+minimap_pos[0],hero_mmposy+minimap_pos[1]))
  549.  
  550.  
  551.  
  552.  
  553.  
  554. # if the player does not have the scanner and hits caps lock, a random hint message will appear.
  555. # if the player has the scanner and hits caps lock, a random hint message will appear.
  556. # a new message won't appear until the player turns on caps lock again.
  557. no_scanner_dialog = ['The scanner could tell me more information','I need a maintenance card to access the red doors.']
  558. scanner_dialog = ['Scanning for life signs...', 'Ship damaged beyond repair', 'Doors are currently inoperable',
  559. 'Recommendation: Utilize the blow torch to open doors.', 'These doors looks strange', 'Why is there an oil spill outside?', 'I better be careful' , 'Why is the TV facing a wall?',
  560. "The mother ship can't save you so your ass is goin' get it", "This games needs more fleshing out..." ]
  561. while keys[pygame.K_CAPSLOCK] and "scanner" in inventory and (just_used_scanner == False):
  562. dialog = random.choice(scanner_dialog)
  563. dialog_counter =60
  564. dialog_position = (300, 200)
  565. just_used_scanner = True
  566.  
  567. if (not keys[pygame.K_CAPSLOCK]) and "scanner" in inventory and (just_used_scanner == True):
  568. just_used_scanner = False
  569.  
  570.  
  571. while keys[pygame.K_CAPSLOCK] and "scanner" not in inventory and (just_used_scanner == False):
  572. dialog = random.choice(no_scanner_dialog)
  573. dialog_counter = 30
  574. dialog_position = (300, 200)
  575. just_used_scanner = True
  576.  
  577. if (not keys[pygame.K_CAPSLOCK]) and "scanner" not in inventory and (just_used_scanner == True):
  578. just_used_scanner = False
  579. # Bring drawn changes to the front
  580. pygame.display.update()
  581.  
  582. frame_count += 1
  583. Hero_anim_count +=1
  584.  
  585. # 30 fps
  586. clock.tick(10)
  587.  
  588. # loop is over
  589. pygame.quit()
  590. sys.exit()
  591.  
  592.  
  593. # Start the program
  594. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement