Advertisement
Guest User

Untitled

a guest
Sep 23rd, 2017
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.95 KB | None | 0 0
  1. #include <amxmodx>
  2. #include <fakemeta>
  3. #include <engine>
  4. #include <zombie_plague_advance>
  5.  
  6. #define PLUGIN "[ZP] Class : Leap Zombie"
  7. #define VERSION "1.4.2"
  8. #define AUTHOR "Fry!"
  9.  
  10. #define TASK_ID_1 param[0]+3344
  11. #define TASK_ID_2 ent+4455
  12. #define ENTITY_ID_2 taskid-4455
  13.  
  14. #define SUPPORT_BOT_TO_USE
  15.  
  16. new const zclass_name[] = { "Leap" }
  17. new const zclass_info[] = { "Can use leap" }
  18. new const zclass_model[] = { "zp_cso_jumper" }
  19. new const zclass_clawmodel[] = { "v_cso_jumper.mdl" }
  20. new const g_vgrenade[] = "models/zombie_plague/v_zombibomb.mdl"
  21. new const g_pgrenade[] = "models/zombie_plague/p_zombie_bomb.mdl"
  22. const zclass_health = 1200
  23. const zclass_speed = 180
  24. const Float:zclass_gravity = 1.3
  25. const Float:zclass_knockback = 1.2
  26.  
  27. // Settings
  28. new const Float:Effect_Human_Delay = 1.5
  29. new const Float:Play_Cough_Sound_Delay = 2.5
  30.  
  31. // Sounds
  32. new const Smoke_Effect_Sound[] = { "items/airtank1.wav" }
  33. new const Humans_Cough_Sound[][] = { "zombie_plague/cough1.wav", "zombie_plague/cough2.wav", "zombie_plague/cough3.wav",
  34. "zombie_plague/cough4.wav", "zombie_plague/cough5.wav", "zombie_plague/cough6.wav" }
  35.  
  36. new bool:g_hasLeap[33]
  37. new Float:g_last_LongJump_time[33]
  38. new g_zclass_Leap, g_Leap_force, g_Leap_height , g_LongJump_cooldown
  39.  
  40. // Cvars
  41. new g_UseSmokeTimes, g_SmokeTimelimit, g_SmokeSkillCooldown
  42. new g_smokeSpr
  43. new maxplayers
  44. new bool:round_end
  45. new g_msgScreenFade, g_msgScreenShake
  46.  
  47. new UseSmokeTimes[33]
  48. new bool:UseSmokeStarted[33]
  49. new bool:CooldownStarted[33]
  50. new bool:EffectStarted[33], Float:EffectOverTime[33], Float:NextEffectTime[33]
  51. new EffectSoundIndex[33], Float:NextPlaySoundTime[33]
  52.  
  53. public plugin_init()
  54. {
  55. register_plugin(PLUGIN, VERSION, AUTHOR)
  56.  
  57. register_cvar("zp_zclass_leap_zombie",VERSION,FCVAR_SERVER|FCVAR_EXTDLL|FCVAR_UNLOGGED|FCVAR_SPONLY)
  58.  
  59. g_Leap_force = register_cvar("zp_zclass_leap_force", "790")
  60. g_Leap_height = register_cvar("zp_zclass_leap_height", "325")
  61. g_LongJump_cooldown = register_cvar("zp_longjump_cooldown", "0.0")
  62.  
  63. g_UseSmokeTimes = register_cvar("zp_zclass_frank_times", "10") //???????????(??? 0 ???????)
  64. g_SmokeTimelimit = register_cvar("zp_zclass_frank_timelimit", "15.0") //??????,??????(??:?)
  65. g_SmokeSkillCooldown = register_cvar("zp_zclass_frank_cooldown", "30.0") //??????????????(??:?)
  66.  
  67. register_forward(FM_PlayerPreThink, "fw_PlayerPreThink")
  68. register_forward(FM_Touch, "fw_Touch")
  69. register_forward(FM_CmdStart, "fw_CmdStart")
  70. register_forward(FM_PlayerPostThink, "fw_PlayerPostThink", 1)
  71. register_event("CurWeapon","handle_gun","be","1=1")
  72. register_event("ResetHUD","event_NewRound","be")
  73. register_event("DeathMsg", "event_Death", "a")
  74. register_event("HLTV", "event_RoundStart", "a", "1=0", "2=0")
  75.  
  76. register_clcmd("hability", "hability")
  77.  
  78. maxplayers = get_maxplayers()
  79. g_msgScreenFade = get_user_msgid("ScreenFade")
  80. g_msgScreenShake = get_user_msgid("ScreenShake")
  81. }
  82.  
  83. public plugin_precache()
  84. {
  85. g_zclass_Leap = zp_register_zombie_class(zclass_name, zclass_info, zclass_model, zclass_clawmodel, g_vgrenade, zclass_health, zclass_speed, zclass_gravity, zclass_knockback)
  86. precache_model(g_vgrenade)
  87. precache_model(g_pgrenade)
  88. precache_sound(Smoke_Effect_Sound)
  89.  
  90. for (new i = 0; i < sizeof Humans_Cough_Sound; i++)
  91. precache_sound(Humans_Cough_Sound[i])
  92.  
  93. g_smokeSpr = precache_model("sprites/black_smoke1.spr")
  94.  
  95. }
  96.  
  97. public client_connect(id)
  98. {
  99. g_hasLeap[id] = false
  100. UseSmokeStarted[id] = false
  101. CooldownStarted[id] = false
  102. }
  103.  
  104.  
  105. public zp_user_infected_post(id, player, infector)
  106. {
  107. if (zp_get_user_zombie_class(player) == g_zclass_Leap)
  108. {
  109. g_hasLeap[player] = true
  110. UseSmokeTimes[id] = get_pcvar_num(g_UseSmokeTimes)
  111. client_cmd(id, "bind v hability)
  112. }
  113.  
  114. return PLUGIN_CONTINUE
  115. }
  116.  
  117. public zp_user_humanized_post(id, player)
  118. {
  119. g_hasLeap[player] = false
  120. if (task_exists(id)) remove_task(id)
  121.  
  122. client_print(id, print_center, "")
  123.  
  124. UseSmokeStarted[id] = false
  125. CooldownStarted[id] = false
  126. }
  127.  
  128. public fw_PlayerPreThink(id)
  129. {
  130. if (!is_user_alive(id) || !zp_get_user_zombie(id))
  131. return FMRES_IGNORED
  132.  
  133. if (zp_get_user_zombie_class(id) != g_zclass_Leap)
  134. {
  135. g_hasLeap[id] = false
  136. }
  137.  
  138. if (allowed_Leap(id))
  139. {
  140. g_hasLeap[id] = true
  141.  
  142. static Float:velocity[3]
  143. velocity_by_aim(id, get_pcvar_num(g_Leap_force), velocity)
  144.  
  145. velocity[2] = get_pcvar_float(g_Leap_height)
  146.  
  147. set_pev(id, pev_velocity, velocity)
  148.  
  149. g_last_LongJump_time[id] = get_gametime()
  150.  
  151. }
  152.  
  153. return FMRES_IGNORED
  154. }
  155.  
  156. allowed_Leap(id)
  157. {
  158. if (!zp_get_user_zombie(id) || zp_get_user_zombie_class(id) != g_zclass_Leap)
  159. return false
  160.  
  161.  
  162. static buttons
  163. buttons = pev(id, pev_button)
  164.  
  165. if (!(pev(id, pev_flags) & FL_ONGROUND) || fm_get_speed(id) < 20 || !(buttons & IN_JUMP) || !(buttons & IN_DUCK))
  166. return false
  167.  
  168. if (get_gametime() - g_last_LongJump_time[id] < get_pcvar_float(g_LongJump_cooldown))
  169. return false
  170.  
  171. return true
  172. }
  173.  
  174. stock fm_get_speed(entity)
  175. {
  176. static Float:velocity[3]
  177. pev(entity, pev_velocity, velocity)
  178.  
  179. return floatround(vector_length(velocity))
  180. }
  181.  
  182. public handle_gun(id)
  183. {
  184. new weap = get_user_weapon(id)
  185.  
  186. if(weap == CSW_HEGRENADE && zp_get_user_zombie_class(id) == g_zclass_Leap && zp_get_user_zombie(id))
  187. {
  188. entity_set_string(id, EV_SZ_viewmodel, g_vgrenade)
  189. set_pev(id, pev_weaponmodel2, g_pgrenade);
  190. }
  191. else if(weap == CSW_SMOKEGRENADE && zp_get_user_zombie_class(id) == g_zclass_Leap && zp_get_user_zombie(id))
  192. {
  193. entity_set_string(id, EV_SZ_viewmodel, g_vgrenade)
  194. set_pev(id, pev_weaponmodel2, g_pgrenade);
  195. }
  196. else if(weap == CSW_FLASHBANG && zp_get_user_zombie_class(id) == g_zclass_Leap && zp_get_user_zombie(id))
  197. {
  198. entity_set_string(id, EV_SZ_viewmodel, g_vgrenade)
  199. set_pev(id, pev_weaponmodel2, g_pgrenade);
  200. }
  201.  
  202. if(weap == CSW_HEGRENADE && zp_get_user_zombie(id))
  203. {
  204. set_pev(id, pev_weaponmodel2, g_pgrenade);
  205. }
  206. else if(weap == CSW_SMOKEGRENADE && zp_get_user_zombie(id))
  207. {
  208. set_pev(id, pev_weaponmodel2, g_pgrenade);
  209. }
  210. else if(weap == CSW_FLASHBANG && zp_get_user_zombie(id))
  211. {
  212. set_pev(id, pev_weaponmodel2, g_pgrenade);
  213. }
  214.  
  215.  
  216. return PLUGIN_HANDLED
  217. }
  218.  
  219. public fw_Touch(ptr, ptd)
  220. {
  221. if (!pev_valid(ptr))
  222. return FMRES_IGNORED;
  223.  
  224. static classname[32]
  225. pev(ptr, pev_classname, classname, 31)
  226.  
  227. if (!equal(classname, "FAKE_SMOKE_ENT"))
  228. return FMRES_IGNORED;
  229.  
  230. if ((1 <= ptd <= 32) && is_user_alive(ptd) && !zp_get_user_zombie(ptd))
  231. {
  232. if (!EffectStarted[ptd])
  233. {
  234. EffectSoundIndex[ptd] = random_num(0, sizeof Humans_Cough_Sound -1)
  235. EffectStarted[ptd] = true
  236. }
  237.  
  238. //???????????????,?????????(?????????3.0?).
  239. //???????????,?????????????,?????????????????????.
  240. EffectOverTime[ptd] = get_gametime() + 3.0
  241. }
  242.  
  243. return FMRES_IGNORED;
  244. }
  245.  
  246. public hability(id)
  247. {
  248. if(zp_get_user_zombie(id))
  249. return PLUGIN_CONTINUE
  250.  
  251. if ((zp_get_user_zombie_class(id) != g_zclass_Leap) || zp_get_user_nemesis(id) || zp_get_user_assassin(id) || zp_get_user_boss(id))
  252. return FMRES_IGNORED;
  253.  
  254. if (!UseSmokeStarted[id])
  255. Use_SmokeSkill(id)
  256.  
  257. return FMRES_HANDLED;
  258. }
  259.  
  260. public fw_CmdStart(id, uc_handle, seed)
  261. {
  262. if (!is_user_alive(id) || !zp_get_user_zombie(id))
  263. return FMRES_IGNORED;
  264.  
  265. if ((zp_get_user_zombie_class(id) != g_zclass_Leap) || zp_get_user_nemesis(id) || zp_get_user_assassin(id) || zp_get_user_boss(id))
  266. return FMRES_IGNORED;
  267.  
  268. #if defined SUPPORT_BOT_TO_USE
  269. if (is_user_bot(id))
  270. {
  271. if (CooldownStarted[id])
  272. return FMRES_IGNORED;
  273.  
  274. new enemy, body
  275. get_user_aiming(id, enemy, body) //????????????
  276.  
  277. if ((1 <= enemy <= 32) && is_user_alive(enemy) &&!zp_get_user_zombie(enemy)) //??????????
  278. {
  279. if (fm_entity_range(id, enemy) <= 100.0)
  280. {
  281. if (!UseSmokeStarted[id])
  282. Use_SmokeSkill(id)
  283. }
  284. }
  285.  
  286. return FMRES_IGNORED;
  287. }
  288. #endif
  289.  
  290. static button, oldbutton
  291. button = get_uc(uc_handle, UC_Buttons)
  292. oldbutton = pev(id, pev_oldbuttons)
  293.  
  294. if ((button & IN_RELOAD) && !(oldbutton & IN_RELOAD))
  295. {
  296. if (!UseSmokeStarted[id])
  297. Use_SmokeSkill(id)
  298. }
  299.  
  300. return FMRES_HANDLED;
  301. }
  302.  
  303. public fw_PlayerPostThink(id)
  304. {
  305. if (!is_user_alive(id) || zp_get_user_zombie(id))
  306. return FMRES_IGNORED;
  307.  
  308. if (!EffectStarted[id])
  309. return FMRES_IGNORED;
  310.  
  311. new Float:gametime = get_gametime()
  312. if (gametime >= NextEffectTime[id])
  313. {
  314. screen_fade(id, 1.0)
  315. NextEffectTime[id] = gametime + Effect_Human_Delay
  316. }
  317.  
  318. if (gametime >= NextPlaySoundTime[id])
  319. {
  320. screen_shake(id, 5, 1, 5)
  321. engfunc(EngFunc_EmitSound, id, CHAN_VOICE, Humans_Cough_Sound[EffectSoundIndex[id]], VOL_NORM, ATTN_NORM, 0, PITCH_NORM)
  322. NextPlaySoundTime[id] = gametime + Play_Cough_Sound_Delay
  323. }
  324.  
  325. if (gametime >= EffectOverTime[id])
  326. EffectStarted[id] = false
  327.  
  328. return FMRES_IGNORED;
  329. }
  330.  
  331. public Use_SmokeSkill(id)
  332. {
  333. if (!is_user_alive(id))
  334. return;
  335.  
  336. if (!zp_get_user_zombie(id) || (zp_get_user_zombie_class(id) != g_zclass_Leap))
  337. return;
  338.  
  339. if (zp_get_user_nemesis(id))
  340. return;
  341.  
  342. if (get_pcvar_num(g_UseSmokeTimes) && UseSmokeTimes[id] <= 0)
  343. return;
  344.  
  345. if (UseSmokeStarted[id])
  346. return;
  347.  
  348. if (CooldownStarted[id])
  349. {
  350. client_print(id, print_center, "????????! ????????????.")
  351. return;
  352. }
  353.  
  354. UseSmokeStarted[id] = true
  355.  
  356. client_print(id, print_center, "")
  357.  
  358. // Create smoke entity
  359. new ent = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_target"))
  360. if (pev_valid(ent))
  361. {
  362. new Float:origin[3]
  363. pev(id, pev_origin, origin)
  364.  
  365. new flags = pev(id, pev_flags)
  366. if (!((flags & FL_DUCKING) && (flags & FL_ONGROUND)))
  367. origin[2] -= 36.0
  368.  
  369. Create_Smoke_Group(origin)
  370.  
  371. new iOrigin[3]
  372. FVecIVec(origin, iOrigin)
  373.  
  374. new param[6]
  375. param[0] = ent //???????ID
  376. param[1] = iOrigin[0] //?????????? X??
  377. param[2] = iOrigin[1] //?????????? Y??
  378. param[3] = iOrigin[2] //?????????? Z??
  379. param[4] = floatround(get_pcvar_float(g_SmokeTimelimit) / 0.1) //???????????(??:0.1?)
  380. param[5] = 10 //??????????????(??:0.1?)
  381.  
  382. set_task(0.1, "Do_Smoke_Effect", TASK_ID_1, param, 6)
  383.  
  384. // Set smoke entity status
  385. set_pev(ent, pev_classname, "FAKE_SMOKE_ENT")
  386. set_pev(ent, pev_solid, SOLID_TRIGGER)
  387. set_pev(ent, pev_movetype, MOVETYPE_NOCLIP)
  388. set_pev(ent, pev_sequence, 1)
  389.  
  390. // Set smoke entity size
  391. new Float:mins[3], Float:maxs[3]
  392. if (!((flags & FL_DUCKING) && (flags & FL_ONGROUND)))
  393. {
  394. mins = Float:{ -130.0, -130.0, -20.0 }
  395. maxs = Float:{ 130.0, 130.0, 130.0 }
  396. }
  397. else
  398. {
  399. mins = Float:{ -130.0, -130.0, -56.0 }
  400. maxs = Float:{ 130.0, 130.0, 130.0 }
  401. }
  402.  
  403. engfunc(EngFunc_SetSize, ent, mins, maxs)
  404.  
  405. // Set smoke entity origin
  406. set_pev(ent, pev_origin, origin)
  407.  
  408. engfunc(EngFunc_EmitSound, ent, CHAN_VOICE, Smoke_Effect_Sound, VOL_NORM, ATTN_NORM, 0, PITCH_NORM)
  409.  
  410. CooldownStarted[id] = true
  411. set_task(get_pcvar_float(g_SmokeSkillCooldown), "SmokeSkill_Cooldown_Over", id)
  412.  
  413. if (get_pcvar_num(g_UseSmokeTimes))
  414. {
  415. UseSmokeTimes[id]--
  416.  
  417. if (UseSmokeTimes[id] > 0)
  418. client_print(id, print_chat, "", UseSmokeTimes[id])
  419. else
  420. client_print(id, print_chat, "")
  421. }
  422. }
  423.  
  424. UseSmokeStarted[id] = false
  425. }
  426.  
  427. public Do_Smoke_Effect(param[6])
  428. {
  429. new ent = param[0]
  430. new iOrigin[3]
  431. iOrigin[0] = param[1]
  432. iOrigin[1] = param[2]
  433. iOrigin[2] = param[3]
  434.  
  435. if (!pev_valid(ent))
  436. return;
  437.  
  438. if (param[4] <= 0 || round_end)
  439. {
  440. if (pev_valid(ent))
  441. set_task(0.8, "Remove_Smoke_Entity", TASK_ID_2)
  442.  
  443. return;
  444. }
  445.  
  446. if (param[5] <= 0)
  447. {
  448. new Float:fOrigin[3]
  449. IVecFVec(iOrigin, fOrigin)
  450. Create_Smoke_Group(fOrigin)
  451.  
  452. param[5] = 10
  453. }
  454.  
  455. param[5]--
  456. param[4]--
  457.  
  458. set_task(0.1, "Do_Smoke_Effect", TASK_ID_1, param, 6)
  459. }
  460.  
  461. public Create_Smoke_Group(Float:position[3])
  462. {
  463. new Float:origin[12][3]
  464. get_spherical_coord(position, 40.0, 0.0, 0.0, origin[0])
  465. get_spherical_coord(position, 40.0, 90.0, 0.0, origin[1])
  466. get_spherical_coord(position, 40.0, 180.0, 0.0, origin[2])
  467. get_spherical_coord(position, 40.0, 270.0, 0.0, origin[3])
  468. get_spherical_coord(position, 100.0, 0.0, 0.0, origin[4])
  469. get_spherical_coord(position, 100.0, 45.0, 0.0, origin[5])
  470. get_spherical_coord(position, 100.0, 90.0, 0.0, origin[6])
  471. get_spherical_coord(position, 100.0, 135.0, 0.0, origin[7])
  472. get_spherical_coord(position, 100.0, 180.0, 0.0, origin[8])
  473. get_spherical_coord(position, 100.0, 225.0, 0.0, origin[9])
  474. get_spherical_coord(position, 100.0, 270.0, 0.0, origin[10])
  475. get_spherical_coord(position, 100.0, 315.0, 0.0, origin[11])
  476.  
  477. for (new i = 0; i < 12; i++)
  478. create_Smoke(origin[i], g_smokeSpr, 100, 0)
  479. }
  480.  
  481. public Remove_Smoke_Entity(taskid)
  482. {
  483. new ent = ENTITY_ID_2
  484.  
  485. if (pev_valid(ent))
  486. engfunc(EngFunc_RemoveEntity, ent)
  487. }
  488.  
  489. public SmokeSkill_Cooldown_Over(id)
  490. {
  491. if (CooldownStarted[id] && zp_get_user_zombie(id) && (zp_get_user_zombie_class(id) == g_zclass_Leap))
  492. {
  493. CooldownStarted[id] = false
  494.  
  495. if (!get_pcvar_num(g_UseSmokeTimes) || UseSmokeTimes[id] > 0)
  496. client_print(id, print_center, "", floatround(get_pcvar_float(g_SmokeSkillCooldown)))
  497. }
  498. }
  499.  
  500. public Remove_All_FakeSmokeEnt()
  501. {
  502. new ent = fm_find_ent_by_class(-1, "FAKE_SMOKE_ENT")
  503. while(ent)
  504. {
  505. engfunc(EngFunc_RemoveEntity, ent)
  506. ent = fm_find_ent_by_class(ent, "FAKE_SMOKE_ENT")
  507. }
  508. }
  509.  
  510. public client_disconnect(id)
  511. {
  512. UseSmokeStarted[id] = false
  513. CooldownStarted[id] = false
  514. }
  515.  
  516. public event_NewRound(id)
  517. {
  518. client_print(id, print_center, "")
  519.  
  520. UseSmokeStarted[id] = false
  521. CooldownStarted[id] = false
  522. }
  523.  
  524. public event_Death()
  525. {
  526. new player = read_data(2)
  527. if (!(1 <= player <= maxplayers))
  528. return;
  529.  
  530. if (task_exists(player)) remove_task(player)
  531.  
  532. client_print(player, print_center, "")
  533.  
  534. UseSmokeStarted[player] = false
  535. CooldownStarted[player] = false
  536. }
  537.  
  538. public event_RoundStart()
  539. {
  540. round_end = false
  541. Remove_All_FakeSmokeEnt()
  542. }
  543.  
  544. public zp_round_ended(winteam)
  545. {
  546. round_end = true
  547. }
  548.  
  549. stock get_spherical_coord(const Float:ent_origin[3], Float:redius, Float:level_angle, Float:vertical_angle, Float:origin[3])
  550. {
  551. new Float:length
  552. length = redius * floatcos(vertical_angle, degrees)
  553. origin[0] = ent_origin[0] + length * floatcos(level_angle, degrees)
  554. origin[1] = ent_origin[1] + length * floatsin(level_angle, degrees)
  555. origin[2] = ent_origin[2] + redius * floatsin(vertical_angle, degrees)
  556. }
  557.  
  558. stock create_Smoke(const Float:position[3], sprite_index, life, framerate)
  559. {
  560. // Alphablend sprite, move vertically 30 pps
  561. message_begin(MSG_BROADCAST, SVC_TEMPENTITY)
  562. write_byte(TE_SMOKE) // TE_SMOKE (5)
  563. engfunc(EngFunc_WriteCoord, position[0]) // position.x
  564. engfunc(EngFunc_WriteCoord, position[1]) // position.y
  565. engfunc(EngFunc_WriteCoord, position[2]) // position.z
  566. write_short(sprite_index) // sprite index
  567. write_byte(life) // scale in 0.1's
  568. write_byte(framerate) // framerate
  569. message_end()
  570. }
  571.  
  572. stock screen_fade(id, Float:time)
  573. {
  574. // Add a blue tint to their screen
  575. message_begin(MSG_ONE_UNRELIABLE, g_msgScreenFade, _, id)
  576. write_short((1<<12)*1) // duration
  577. write_short(floatround((1<<12)*time)) // hold time
  578. write_short(0x0000) // fade type
  579. write_byte(0) // red
  580. write_byte(0) // green
  581. write_byte(0) // blue
  582. write_byte(255) // alpha
  583. message_end()
  584. }
  585.  
  586. stock screen_shake(id, amplitude = 4, duration = 2, frequency = 10)
  587. {
  588. message_begin(MSG_ONE_UNRELIABLE, g_msgScreenShake, _, id)
  589. write_short((1<<12)*amplitude) // ??
  590. write_short((1<<12)*duration) // ??
  591. write_short((1<<12)*frequency) // ??
  592. message_end()
  593. }
  594.  
  595. stock fm_find_ent_by_class(index, const classname[])
  596. {
  597. return engfunc(EngFunc_FindEntityByString, index, "classname", classname)
  598. }
  599.  
  600. #if defined SUPPORT_BOT_TO_USE
  601. stock Float:fm_entity_range(ent1, ent2)
  602. {
  603. new Float:origin1[3], Float:origin2[3];
  604. pev(ent1, pev_origin, origin1);
  605. pev(ent2, pev_origin, origin2);
  606.  
  607. return get_distance_f(origin1, origin2);
  608. }
  609. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement