/* Plugin generated by eternal cannabis wormgirl */ /* Healer zombie (RU:: Todo section): - Лечение зомбей при нажатии кнопки вторичной атаки за счёт MP. - Ускорение лечения зомбя, здоровье которого ниже нормального. - Ускорение лечения любого зомбя (в том числе из примера выше), если здоровье лечащего ниже нормального. Ускорение тем больше, чем ниже его HP. - Увеличение максимального количества MP при уменьшении здоровья лечащего. Чем меньше HP, тем больше максимальное MP (геометрическая прогрессия). - Ускорение регенарации MP при увеличении максимального MP. Прямо пропорционально увеличению максимума MP относительно нормы. - Возможность саморазуплотнения лечащего зомбя с летальным для оного исходом при низких значениях HP. Саморазуплотнение заряжает целительными лучами поноса всех зомбей, находящихся поблизости. Чем ближе зомбя к взорвавшемуся лекарю, тем большее дополнительное количество HP он получит в этот момент. - Свистелки и перделки вроде всяких спецэффектов при лечении и взрыве. - Звуковое сопровождение при лечении и/или на протяжении всей жизни хиляра, разоблачающее его для людей как присутствие луча поноса в царстве тьмы. - || Добавить: - Проверку LoS (Line of Sight) при лечении. - Дополнительную информацию (Имя цели, HP и прочее) на HUD. - Lesbian cosplayers. */ #include #include #include #include #include #include #include #include #define PLUGIN "Healer class" #define VERSION "0x0000YP4K" #define AUTHOR "Lord Canistra" #define H_HEALTASKOFFSET 1500 // Offset for healing tasks #define H_RANGE 290 // Range for healing #define H_HPSTEP 50 // HP gained at once #define H_HPGAINDELAY 0.5 // HP gaining delay, in seconds #define H_LOWHPMUL 2.0 // HP gaining multiplier when target HP is lower than default #define H_HEALBARRIER 1225.0 #define H_HEALHPFORPACK 2500.0 #define H_HEALREWARD 1 #define H_DEFAULTMAXMP 1666.0 // Default max. MP, increased when healer's HP goes lower than default #define H_MPCPHS 10 // MP Consumed per HP Step #define H_REGENERATEHPSTEP 6 #define H_REGENERATEMPSTEP 3 #define H_REGENERATEDELAY 0.5 #define H_EXPLOSIONBORDER 66.0 #define H_EXPLOSIONRADIUS 600 #define H_EXPLOSIONCOREHP 3000 #define H_EXPLOSIONHPFORBONUS 1666 //#define H_EXPLOSIONFRAGBONUS 1 #define H_EXPLOSIONPACKBONUS 1 #define H_ASSISTPACKREWARD 1 #define H_ASSISTFRAGREWARD 1 #define H_EFFECTDIST 60.0 #define MAXPLAYERS 33 new const zclass_name[] = { "[Healer]" } new const zclass_info[] = { "[Лечит зомби]" } new const zclass_model[] = { "zombie_healer5" } new const zclass_clawmodel[] = { "v_knife_zombie_healer5.mdl" } const zclass_health = 5000 const zclass_speed = 230 const Float:zclass_gravity = 0.80 const Float:zclass_knockback = 1.35 new h_hclass // h_hclass for being able to check if we belong to Healer class. new h_phid[33] new h_player_healed[33] // flags for checking if any of players is being healed... new h_healer_working[33] // ...and if any player is healing someone right now. new h_players_healer[33] = 0 // who's my healer? new Float:h_curmp[33] // current MP amount new Float:h_curmaxmp[33] // current maximum MP new Float:h_curhealmul[33] // healing multiplier on healer's low HP new h_prevhealth[33] // stored healer's health new Float:h_healed[33] new h_cancelled[33] = 0 // has our healing process been cancelled? new h_playername[33][32] new h_expspr, h_healspr public plugin_init() { register_plugin(PLUGIN, VERSION, AUTHOR) // Kawaiiiiiiiiii! } public plugin_precache() { h_hclass = zp_register_zombie_class(zclass_name, zclass_info, zclass_model, zclass_clawmodel, zclass_health, zclass_speed, zclass_gravity, zclass_knockback) register_clcmd("zp_h_exp", "h_explode") register_forward(FM_PlayerPreThink, "h_playerprethink") h_expspr = engfunc(EngFunc_PrecacheModel, "sprites/shockwave.spr") //h_healbeamspr = engfunc(EngFunc_PrecacheModel, "sprites/laserbeam.spr") h_healspr = precache_model("sprites/3dmflaora.spr") precache_sound("zombie_plague/h_healstart.wav") } public zp_user_infected_post(id, infector) { new a_id[1] a_id[0] = id h_assistcheck(id, infector) if(zp_get_user_zombie_class(id) == h_hclass) { h_hudtask(a_id) h_regenerate(a_id) h_curmp[id] = H_DEFAULTMAXMP h_curmaxmp[id] = H_DEFAULTMAXMP client_print(id, print_chat, "[ZE] Нажмите ATTACK2 для лечения зомби.") client_print(id, print_chat, "[ZE] Use 'zp_h_exp' command to explode when HP is lower than %i", floatround(H_EXPLOSIONBORDER)) } } public client_putinserver(id) { get_user_name(id, h_playername[id], charsmax(h_playername[])) } public h_playerprethink(id) // FM_PlayerPreThink { new ubut = get_user_button(id) new oubut = get_user_oldbutton(id) if((ubut & IN_ATTACK2) && !(oubut & IN_ATTACK2) && is_user_alive(id)) // secondary attack button pressed? { if(h_healer_working[id] == 1) // if we're healing... { h_cancelled[id] = 1 h_healer_working[id] = 0 // ...we're doing so no more. h_player_healed[h_phid[id]] = 0 h_players_healer[h_phid[id]] = 0 //console_print(id, "Cancelled healing") // PURGE } else if(h_healer_working[id] == 0) // And if we do not heal anyone... { h_healcmd(id) // ...proceed. } } if(pev(id, pev_health) != h_prevhealth[id]) { h_calcmaxmp(id) } h_prevhealth[id] = pev(id, pev_health) return FMRES_IGNORED } public h_healcmd(id) { if(!zp_get_user_zombie(id) || (zp_get_user_zombie_class(id) != h_hclass)) { return PLUGIN_CONTINUE } /*new vec[3] new aimvec[3] get_user_origin(id, vec) get_user_origin(id, aimvec, 3) new dist = get_distance(vec, aimvec) // Getting distance to our target console_print(id, "Distance: %d", dist) // PURGE*/ h_checktarget(id) // check if we pointed at someone return PLUGIN_CONTINUE } public h_checktarget(id) { new aimed_id, aimed_body get_user_aiming(id, aimed_id, aimed_body, H_RANGE - 1) if(aimed_id > 0 && aimed_id < MAXPLAYERS) // Is any player targeted? { //console_print(id, "Target: %s", h_playername[aimed_id]) // PURGE if(!zp_get_user_zombie(aimed_id)) { //console_print(id, "Not a zombie target, unable to proceed") // PURGE return PLUGIN_CONTINUE // Pity human, gtfo } if((h_healer_working[id] == 0) && (h_player_healed[aimed_id] == 0) && (pev(aimed_id, pev_health) <= H_HEALBARRIER)) // if we're not busy and so is our target... { h_cancelled[id] = 0 //console_print(id, "Healing starts...") // PURGE new args[2] args[0] = id args[1] = aimed_id set_task(H_HPGAINDELAY, "h_healtask", id+H_HEALTASKOFFSET, args, sizeof(args)) // ...start heal task client_print(id, print_chat, "[ZE] Healing: %s", h_playername[aimed_id]) h_healer_working[id] = 1 h_player_healed[aimed_id] = 1 // we're busy now h_players_healer[aimed_id] = id // id of my healer h_phid[id] = aimed_id emit_sound(id, CHAN_AUTO, "zombie_plague/h_healstart.wav", 1.0, ATTN_NORM, 0, PITCH_NORM) } } return PLUGIN_CONTINUE } public h_healtask(args[]) // Adds HP portion, consumes MP, calls healing visual effect function { new fwargs[2] fwargs[0] = args[0] fwargs[1] = args[1] new vec[3], tvec[3], dist get_user_origin(args[0], vec) get_user_origin(args[1], tvec) dist = get_distance(vec, tvec) // Distance to target, needs to be verified every time our target gains HP. if(h_cancelled[args[0]] == 0 && zp_get_user_zombie(args[0]) && (zp_get_user_zombie_class(args[0]) == h_hclass) && is_user_alive(args[0]) && is_user_alive(args[1]) && zp_get_user_zombie(args[1]) && (pev(args[1], pev_health) <= H_HEALBARRIER)) // if we're not healing fellow zombie now { if(h_curmp[args[0]] >= H_MPCPHS && dist < H_RANGE) { h_curhealmul[args[0]] = floatdiv(h_curmaxmp[args[0]], H_DEFAULTMAXMP) h_curmp[args[0]] = h_curmp[args[0]] - H_MPCPHS if(float(pev(args[1], pev_health)) < zp_get_zombie_maxhealth(args[1])) { new Float:addhp = floatmul(floatmul(float(H_HPSTEP), H_LOWHPMUL), h_curhealmul[args[0]]) set_pev(args[1], pev_health, float(pev(args[1], pev_health)) + floatmul(floatmul(float(H_HPSTEP), H_LOWHPMUL), h_curhealmul[args[0]])) // low target HP // Add HP portion * low HP multiplier h_healed[args[0]] = h_healed[args[0]] + addhp } else { new Float:addhp = floatmul(float(H_HPSTEP), h_curhealmul[args[0]]) set_pev(args[1], pev_health, float(pev(args[1], pev_health)) + floatmul(float(H_HPSTEP), h_curhealmul[args[0]])) // add HP portion //console_print(args[0], "Low healer's HP healing multiplier: %i * (%f / 1666.0) = %i * %.2f", H_HPSTEP, h_curmaxmp[args[0]], H_HPSTEP, h_curhealmul[args[0]]) h_healed[args[0]] = h_healed[args[0]] + addhp } h_healeffect(vec, tvec) h_showhealeronhud(args[0], args[1]) //console_print(args[0], "Distance to target: %i", dist) set_task(H_HPGAINDELAY, "h_healtask", args[0] + H_HEALTASKOFFSET, fwargs, sizeof(fwargs)) // set this task again } else if(h_curmp[args[0]] < H_MPCPHS) { client_print(args[0], print_chat, "[ZE] Not enough MP to heal") h_cancelled[args[0]] = 1 h_healer_working[args[0]] = 0 h_player_healed[args[1]] = 0 h_players_healer[args[1]] = 0 //console_print(args[0], "Cancelled healing due to lack of MP") // PURGE } else if(dist >= H_RANGE) { client_print(args[0], print_chat, "[ZE] Target out of range"), h_cancelled[args[0]] = 1 h_healer_working[args[0]] = 0 h_player_healed[args[1]] = 0 h_players_healer[args[1]] = 0 } return PLUGIN_CONTINUE } else { client_print(args[0], print_chat, "[ZE] Cancelled healing") h_cancelled[args[0]] = 1 h_healer_working[args[0]] = 0 h_player_healed[args[1]] = 0 h_players_healer[args[1]] = 0 } return PLUGIN_CONTINUE } public h_hudtask(hid[1]) // Shows current/max MP. In further versions will also inform of target's name HP. For now, I'm too lazy to do it. { if(((zp_get_user_zombie(hid[0])) || zp_get_user_nemesis(hid[0])) && (zp_get_user_zombie_class(hid[0]) == h_hclass) && (is_user_alive(hid[0]))) { set_hudmessage(25, 255, 255, 0.475, 0.8, 2, 0.5, 1.5, 0.0, 0.0, 2) show_hudmessage(hid[0], "MP: %i/%i", floatround(h_curmp[hid[0]]), floatround(h_curmaxmp[hid[0]])) set_task(1.0, "h_hudtask", 900, hid, sizeof(hid)) } return PLUGIN_CONTINUE } public h_showhealeronhud(healer, healed) // Shows healer's name to his target. Not tested. { /*set_hudmessage(255, 150, 30, 0.475, 0.77, 2, 0.5, 1.5, 0.0, 0.0, 2) show_hudmessage(healed, "Healer: %s", h_playername[healer])*/ client_print(healed, print_center, "You healer: %s", h_playername[healer]) } public h_regenerate(hid[1]) // Regenerate my HP and MP { if((is_user_alive(hid[0])) && (zp_get_user_zombie(hid[0]) || zp_get_user_nemesis(hid[0])) && (zp_get_user_zombie_class(hid[0]) == h_hclass)) { if(pev(hid[0], pev_health) < zclass_health) { set_pev(hid[0], pev_health, float(pev(hid[0], pev_health)) + H_REGENERATEHPSTEP) } if(float:h_curmp[hid[0]] < float:h_curmaxmp[hid[0]]) { new Float:h_curmpregmul h_curmpregmul = floatdiv(h_curmaxmp[hid[0]], H_DEFAULTMAXMP) h_curmp[hid[0]] = h_curmp[hid[0]] + floatmul(float(H_REGENERATEMPSTEP), h_curmpregmul) } h_healrewardcheck(hid[0]) set_task(H_REGENERATEDELAY, "h_regenerate", 800, hid, sizeof(hid)) } } public h_calcmaxmp(id) // Is called when HP changes, recalculates max. MP { new h_curhp = pev(id, pev_health) if(zclass_health > h_curhp) { new h_deltahp = zclass_health - h_curhp h_curmaxmp[id] = H_DEFAULTMAXMP + (power(h_deltahp, 2) / 2048) } } // Self-sacrificing explosion on healer's low HP (command 'zp_h_exp'), charges nearby zombies with HP dramatically. // The closer zombie is to healer, the more HP he will gain. public h_explode(id) { if(zp_get_user_zombie(id) && (zp_get_user_zombie_class(id) == h_hclass) && is_user_alive(id) && (pev(id, pev_health) <= H_EXPLOSIONBORDER)) { user_silentkill(id) new hvec[3] new tvec[3] new Float:evec[3] get_user_origin(id, hvec) evec[0] = float(hvec[0]) evec[1] = float(hvec[1]) evec[2] = float(hvec[2]) h_expeffect(evec) // calling explosion visual effect //console_print(id, "Your origin: %i %i %i", hvec[0], hvec[1], hvec[2]) static h_target h_target = -1 // Tried to use here FindEntityInSphere, but it's behaviour was kind of inadequate... while((h_target = engfunc(EngFunc_FindEntityByString, h_target, "classname", "player")) != 0) { if((h_target > 0) && (h_target != id) && (h_target < MAXPLAYERS)) { get_user_origin(h_target, tvec) new h_dist = get_distance(hvec, tvec) if(h_dist < 700 && zp_get_user_zombie(h_target)) { /*console_print(id, "Target: %i", h_target) console_print(id, "Target's origin: %i %i %i", tvec[0], tvec[1], tvec[2]) console_print(id, "Distance to target: %d", h_dist)*/ new h_prevhp, h_gainedhp, Float:h_distdelta h_prevhp = pev(h_target, pev_health) h_distdelta = floatdiv(float(h_dist), float(H_EXPLOSIONRADIUS)) console_print(id, "Dist. delta: %f", h_distdelta) h_gainedhp = H_EXPLOSIONCOREHP - floatround(float(H_EXPLOSIONCOREHP) * h_distdelta) set_pev(h_target, pev_health, float(h_prevhp + h_gainedhp)) if(h_gainedhp >= H_EXPLOSIONHPFORBONUS) { zp_set_user_ammo_packs(id, zp_get_user_ammo_packs(id) + H_EXPLOSIONPACKBONUS) } //console_print(id, "Gave %i HP to %i, prev. HP: %i", h_gainedhp, h_target, h_prevhp) } } } } } // Checks if infector has been assisted by any healer, and gives him reward. public h_assistcheck(victim, attacker) { new h_assistant_id = h_players_healer[attacker] //console_print(0, "Assistant id: %i", h_assistant_id) if(h_assistant_id > 0 && h_assistant_id < MAXPLAYERS) { new h_newfrags = get_user_frags(h_assistant_id) + H_ASSISTFRAGREWARD // console_print(0, "Setting %i frags to %i, prev. frags: %i, reward: %i", h_assistant_id, h_newfrags, get_user_frags(h_assistant_id), H_ASSISTFRAGREWARD) set_user_frags(h_assistant_id, h_newfrags) // Updating scoreboard message_begin(MSG_BROADCAST, get_user_msgid("ScoreInfo")) write_byte(h_assistant_id) write_short(h_newfrags) write_short(cs_get_user_deaths(attacker)) write_short(0) write_short(get_pdata_int(attacker, 114, 5)) message_end() zp_set_user_ammo_packs(h_assistant_id, zp_get_user_ammo_packs(h_assistant_id) + H_ASSISTPACKREWARD) // Informing everyone of healer's assist, yellow-colored message right below red-colored infection one. set_hudmessage(255, 125, 25, 0.05, 0.48, 2, 0.5, 6.0, 0.0, 1.0, 3) show_hudmessage(0, "%s killed %s with help of %s", h_playername[attacker], h_playername[victim], h_playername[h_assistant_id]) } } public h_healrewardcheck(healer) // Called from h_regenerate { if(h_healed[healer] >= H_HEALHPFORPACK) { h_healed[healer] = h_healed[healer] - H_HEALHPFORPACK zp_set_user_ammo_packs(healer, zp_get_user_ammo_packs(healer) + H_HEALREWARD) } } // Visual effect of healer's explosion. Three beamcylinders, just like ZP nade explosion has. Yellow-colored. // Radius of the largest one = radius of healing explosion (H_EXPLOSIONRADIUS). public h_expeffect(const Float:origin[3]) { engfunc(EngFunc_MessageBegin, MSG_PVS, SVC_TEMPENTITY, origin, 0) write_byte(TE_BEAMCYLINDER) // TE id engfunc(EngFunc_WriteCoord, origin[0]) // x engfunc(EngFunc_WriteCoord, origin[1]) // y engfunc(EngFunc_WriteCoord, origin[2]) // z engfunc(EngFunc_WriteCoord, origin[0]) // x axis engfunc(EngFunc_WriteCoord, origin[1]) // y axis engfunc(EngFunc_WriteCoord, origin[2] + float(H_EXPLOSIONRADIUS)) // z axis write_short(h_expspr) // sprite write_byte(0) // startframe write_byte(0) // framerate write_byte(10) // life write_byte(80) // width write_byte(0) // noise write_byte(255) // red write_byte(75) // green write_byte(45) // blue write_byte(165) // brightness write_byte(0) // speed message_end() engfunc(EngFunc_MessageBegin, MSG_PVS, SVC_TEMPENTITY, origin, 0) write_byte(TE_BEAMCYLINDER) engfunc(EngFunc_WriteCoord, origin[0]) engfunc(EngFunc_WriteCoord, origin[1]) engfunc(EngFunc_WriteCoord, origin[2]) engfunc(EngFunc_WriteCoord, origin[0]) engfunc(EngFunc_WriteCoord, origin[1]) engfunc(EngFunc_WriteCoord, origin[2] + (float(H_EXPLOSIONRADIUS) * 0.75)) write_short(h_expspr) write_byte(0) write_byte(0) write_byte(10) write_byte(96) write_byte(0) write_byte(255) write_byte(125) write_byte(35) write_byte(210) write_byte(0) message_end() engfunc(EngFunc_MessageBegin, MSG_PVS, SVC_TEMPENTITY, origin, 0) write_byte(TE_BEAMCYLINDER) engfunc(EngFunc_WriteCoord, origin[0]) engfunc(EngFunc_WriteCoord, origin[1]) engfunc(EngFunc_WriteCoord, origin[2]) engfunc(EngFunc_WriteCoord, origin[0]) engfunc(EngFunc_WriteCoord, origin[1]) engfunc(EngFunc_WriteCoord, origin[2] + (float(H_EXPLOSIONRADIUS) * 0.5)) write_short(h_expspr) write_byte(0) write_byte(0) write_byte(10) write_byte(112) write_byte(0) write_byte(255) write_byte(175) write_byte(25) write_byte(255) write_byte(0) message_end() /* Beamdisk - looks too phailed in game, lol engfunc(EngFunc_MessageBegin, MSG_PVS, SVC_TEMPENTITY, origin, 0) write_byte(TE_BEAMDISK) engfunc(EngFunc_WriteCoord, origin[0]) engfunc(EngFunc_WriteCoord, origin[1]) engfunc(EngFunc_WriteCoord, origin[2]) engfunc(EngFunc_WriteCoord, origin[0]) engfunc(EngFunc_WriteCoord, origin[1]) engfunc(EngFunc_WriteCoord, origin[2] + float(H_EXPLOSIONRADIUS)) write_short(h_expspr) write_byte(0) write_byte(0) write_byte(10) write_byte(112) write_byte(0) write_byte(255) write_byte(175) write_byte(25) write_byte(125) write_byte(0) message_end()*/ } // Visual effect, called every time healer gives portion of HP to his target. // Current: TE_SPRITETRAIL (Most likely the one that Shadow strike in WC3 mod uses) public h_healeffect(h_hpos[3], h_tpos[3]) { //console_print(0, "Showing effect, hpos %i %i %i tpos %i %i %i", h_hpos[0], h_hpos[1], h_hpos[2], h_tpos[0], h_tpos[1], h_tpos[2]) message_begin(MSG_ALL, SVC_TEMPENTITY) write_byte(TE_SPRITETRAIL) write_coord(h_hpos[0]) write_coord(h_hpos[1]) write_coord(h_hpos[2]) write_coord(h_tpos[0]) write_coord(h_tpos[1]) write_coord(h_tpos[2]) write_short(h_healspr) write_byte(floatround(float(get_distance(h_hpos, h_tpos)) / H_EFFECTDIST)) write_byte(1) write_byte(1) write_byte(32) write_byte(4) message_end() }