FlashFun v1.0 re-release

Release your plugins here!
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

FlashFun v1.0 re-release

Postby BackRaw » Wed Sep 10, 2014 12:16 am

Maybe a few of you remember my old FlashFun script back in the EventScripts days.
Well, here it is for Source.Python!

Basically it allows you to kill enemies using Flashbang grenades by setting each player's health and armor to something low (default: 1 HP, 0 AP) on spawn.

GitHub repo: https://github.com/backraw/flashfun
Releases: https://github.com/backraw/flashfun/releases
Issues list: https://github.com/backraw/flashfun/issues

Features:
  • Free Flashbang grenades, of course
  • Spawn Points -manageable in game via the Admin menu (say command: !flashfun)
  • Damage Protection (timed on spawn, but indefinitely when using the Admin Menu)
  • Reward system for killing sprees

Game Support:
  • Counter-Strike: Global Offensive
  • Counter-Strike: Source (untested, but should work out of the box)

Configuration:
The config file is generated and placed under ../<game root>/cfg/source-python/flashfun.cfg after the plugin has been loaded up the first time.

Code: Select all

// ========================================================================= //
//                             PLAYER ATTRIBUTES                             //
// ========================================================================= //

// Default Value: 1
// The health value the player starts with.
   flashfun_health_spawn 1


// Default Value: 0
// The armor value the player starts with.
   flashfun_armor_spawn 0


// ========================================================================= //
//                                  GAMEPLAY                                 //
// ========================================================================= //

// Default Value: 1
// The respawn delay in seconds.
   flashfun_respawn_delay 1


// Default Value: 3
// The spawn protection time in seconds.
   flashfun_spawn_protection_time 3


// ========================================================================= //
//                               PLAYER REWARDS                              //
// ========================================================================= //

// Default Value: 1
// Enable player rewards (0 disable).
   flashfun_enable_player_rewards 1


// ========================================================================= //
//                                SAY COMMANDS                               //
// ========================================================================= //

// Default Value: "!flashfun"
// The admin say command.
   flashfun_admin_saycommand "!flashfun"


Reward System
In the plugin's data files (../addons/source-python/data/plugins/flashfun/rewards/<game-name>.ini), you can change the rewards a player can receive. Currently, only CS:GO is supported. The file contains something like this:

Syntax: Select all

[health]
value = 3
max_value = 50

[armor]
value = 1
max_value = 30

[hegrenade]
type = "kills"
multiplier = 5

[glock]
type = "health"
multiplier = 7
clip = 2
ammo = 0

[hkp2000]
type = "kills"
multiplier = 12
clip = 2
ammo = 0

[deagle]
type = "kills"
multiplier = 20
clip = 1
ammo = 0

[fiveseven]
type = "health"
multiplier = 20
clip = 3
ammo = 0


Property rewards
The player properties (health, armor) have a value and a maximum value. If the maximum value is 0, it will be converted to 999 internally. You can turn those rewards off by setting the respective value to 0.

Let's look at them more closely:

Syntax: Select all

[health]
value = 3
max_value = 50

[armor]
value = 1
max_value = 30
Each time a player kills an enemy, they will gain 3 HP if their current HP is below 50 and 1 AP if their current AP is below 30.

Note: value is required, max_value is optional and will be set to 0 internally if it is not provided.

Weapon rewards
The weapon rewards (hegrenade, glock, hkp2000, ...) have a type and a multiplier. type defines which player property to respect for the multiplier. type can be any player property the Source.Python Player API exposes. This can be kills, health, armor, etc. I don't restrict the type, but, be sure to only set types which make sense. The weapon properties clip and ammo can be set for weapons which have those properties (everything besides grenades and melee weapons).

Note: type and multiplier are required. Everything else is optional.

Let's take a closer look at the HE grenade reward:

Syntax: Select all

[hegrenade]
type = "kills"
multiplier = 5
As soon as the player reaches any number of kills that is in the range of 5 (5, 10, 15, ...), the player receives a HE grenade.

For glock and other weapons of the same type, clip and ammo can be set:

Syntax: Select all

[glock]
type = "health"
multiplier = 7
clip = 2
ammo = 0
Here, the player receives a glock with a clip of 2 and 0 as soon as their HP is in the range of 7 (7, 14, 21, ...). If the weapon has the clip and ammo properties, it will be removed as soon as the it runs out of ammo.

Enjoy!
Last edited by BackRaw on Tue Aug 14, 2018 1:39 pm, edited 6 times in total.
User avatar
L'In20Cible
Project Leader
Posts: 1533
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Postby L'In20Cible » Wed Sep 10, 2014 1:10 am

Good job!

I was looking at the code and noticed something. Into the following function:

Syntax: Select all

def strip(self):
# loop through all weapons the player currently carries
for weapon_index in self.weapon_indexes():

# drop it
self.drop_weapon(pointer_from_index(weapon_index), *DROP_WEAPON_ARGS)

# remove it
server_tools.remove_entity(weapon_index)

# store the handle
inthandle = self.inthandle

# loop through all the items on the server
for index, handle in entityiter_items:

# are we in possession of the item?
if handle != inthandle:

# if not, skip to the next one
continue

# if yes, remove it
server_tools.remove_entity(index)
The following condition will always returns False:

Syntax: Select all

# are we in possession of the item?
if handle != inthandle:
Cause you are comparing an entity's handle with the player's one. What you need to compare is the following:

Syntax: Select all

# are we in possession of the item?
if edict_from_index(index).get_prop_int(
'm_hOwnerEntity') != inthandle:
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Wed Sep 10, 2014 1:34 am

The m_hOwnerEntity send prop can also be retrieved using:

Syntax: Select all

# are we in possession of the item?
if BaseEntity(index).owner != inthandle:


Also, your __new__ classmethod is pointless:

Syntax: Select all

def __new__(cls, index):
"""
Since there's no other way (yet), we need to return a new instance like this.
"""

return super().__new__(cls, index)


If you aren't doing anything to change the creation of the instance, there is no need to even define the __new__ classmethod.

If, however, you wanted to get an instance of your class using player userids instead of indexes, you could use the following:

Syntax: Select all

def __new__(cls, userid):
"""
Since there's no other way (yet), we need to return a new instance like this.
"""

return super(_Player, cls).__new__(cls, index_from_userid(index))


Another tip: it is much better practice to not use super() without arguments.

Syntax: Select all

# Instead of
super().__init__()

# Use
super(_Player, self).__init__()


Overall, nice plugin! :) I'll have to try it out sometime soon.
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Wed Sep 10, 2014 5:15 am

L'In20Cible wrote:I was looking at the code and noticed something. Into the following function:

Syntax: Select all

def strip(self):
# loop through all weapons the player currently carries
for weapon_index in self.weapon_indexes():

# drop it
self.drop_weapon(pointer_from_index(weapon_index), *DROP_WEAPON_ARGS)

# remove it
server_tools.remove_entity(weapon_index)

# store the handle
inthandle = self.inthandle

# loop through all the items on the server
for index, handle in entityiter_items:

# are we in possession of the item?
if handle != inthandle:

# if not, skip to the next one
continue

# if yes, remove it
server_tools.remove_entity(index)
The following condition will always returns False:

Syntax: Select all

# are we in possession of the item?
if handle != inthandle:
Cause you are comparing an entity's handle with the player's one. What you need to compare is the following:

Syntax: Select all

# are we in possession of the item?
if edict_from_index(index).get_prop_int(
'm_hOwnerEntity') != inthandle:


satoon101 wrote:The m_hOwnerEntity send prop can also be retrieved using:

Syntax: Select all

# are we in possession of the item?
if BaseEntity(index).owner != inthandle:


Doh! Forgot about the owner entity :D Gonna fix it later today.

satoon101 wrote:Also, your __new__ classmethod is pointless:

Syntax: Select all

def __new__(cls, index):
"""
Since there's no other way (yet), we need to return a new instance like this.
"""

return super().__new__(cls, index)


If you aren't doing anything to change the creation of the instance, there is no need to even define the __new__ classmethod.

Good to know!!

satoon101 wrote:Another tip: it is much better practice to not use super() without arguments.

Syntax: Select all

# Instead of
super().__init__()

# Use
super(_Player, self).__init__()


Why is that so? I read about both, and I'm sure python devs know what they're doing, but I don't :D

satoon101 wrote:Overall, nice plugin! :) I'll have to try it out sometime soon.

L'In20Cible wrote:Good job!


Thank you both! :)


Edit: Updated the script to use the m_hOwnerEntity, and it uses super(<class>, self) now for subclassing instead of super().
My Github repositories:

Source.Python: https://github.com/backraw
User avatar
Doldol
Senior Member
Posts: 200
Joined: Sat Jul 07, 2012 7:09 pm
Location: Belgium

Postby Doldol » Wed Sep 10, 2014 11:50 am

BackRaw wrote:
satoon101 wrote:Another tip: it is much better practice to not use super() without arguments.
Python:
  1. # Instead of
  2. super().__init__()
  3. # Use
  4. super(_Player, self).__init__()





Why is that so? I read about both, and I'm sure python devs know what they're doing, but I don't :D


Hah, I would disagree. I like renaming stuff a lot, and I've never liked the idea of having to essentially name the class twice, it doesn't adhere to Don't Repeat Yourself.
About this: http://stackoverflow.com/questions/466611/why-do-i-have-to-specify-my-own-class-when-using-super-and-is-there-a-way-to

I also don't really like using super in the first place, which is why I do omit it when unnecessary.
And about this: http://stackoverflow.com/questions/222877/how-to-use-super-in-python

Links are there because I'm to lazy to explain myself. =]
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Wed Sep 10, 2014 12:38 pm

Doldol wrote:
BackRaw wrote:
satoon101 wrote:Another tip: it is much better practice to not use super() without arguments.
Python:
  1. # Instead of
  2. super().__init__()
  3. # Use
  4. super(_Player, self).__init__()





Why is that so? I read about both, and I'm sure python devs know what they're doing, but I don't :D


Hah, I would disagree. I like renaming stuff a lot, and I've never liked the idea of having to essentially name the class twice, it doesn't adhere to Don't Repeat Yourself.
About this: http://stackoverflow.com/questions/466611/why-do-i-have-to-specify-my-own-class-when-using-super-and-is-there-a-way-to

I also don't really like using super in the first place, which is why I do omit it when unnecessary.
And about this: http://stackoverflow.com/questions/222877/how-to-use-super-in-python

Links are there because I'm to lazy to explain myself. =]


yep, PEP 3135 says super() is equal to super(<class>, <argument>).
http://legacy.python.org/dev/peps/pep-3135/#specification

So, satoon101, can you explain your standpoint on this? :)
My Github repositories:

Source.Python: https://github.com/backraw
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Wed Sep 10, 2014 3:12 pm

I just feel it isn't explicit as to what you are doing. Using super() without arguments just seems illogical. I get that it automatically figures out which class it is called in, but nothing else in Python does that (afaik). It isn't about repeating yourself. I mean, should we also not have to use self inside an instance method? Or cls inside a classmethod? Or, if we call int() without arguments in a class that has a defined __int__ method, should it automatically know that is what we are wanting to do or simply return 0? super() in one class without arguments means something altogether different to super() without arguments in another class.


Back to the plugin. I just noticed one fatal issue. You do not have the plugin in its own folder. It needs to be ../addons/source-python/plugins/flashfun/flashfun.py. Also, what is with all the other files and the __MACOSX directory in the zip?
Image
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Wed Sep 10, 2014 9:27 pm

satoon101 wrote:I just feel it isn't explicit as to what you are doing. Using super() without arguments just seems illogical. I get that it automatically figures out which class it is called in, but nothing else in Python does that (afaik). It isn't about repeating yourself. I mean, should we also not have to use self inside an instance method? Or cls inside a classmethod? Or, if we call int() without arguments in a class that has a defined __int__ method, should it automatically know that is what we are wanting to do or simply return 0? super() in one class without arguments means something altogether different to super() without arguments in another class.


Back to the plugin. I just noticed one fatal issue. You do not have the plugin in its own folder. It needs to be ../addons/source-python/plugins/flashfun/flashfun.py. Also, what is with all the other files and the __MACOSX directory in the zip?


Yeah you're right, that sounds logical.
re-download it, I zipped it correctly using Linux, gonna do it this way for all the releases from now on.

Mac OS X creates zip files this way, don't know why though... I'm using an OS X machine as my host.
My Github repositories:

Source.Python: https://github.com/backraw
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Wed Sep 10, 2014 10:20 pm

You might try using zipfile.ZipFile to create the zip. Just an idea. If I remember, I'll post an example of this on the cookbook forum later.
Image
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Wed Sep 10, 2014 10:34 pm

satoon101 wrote:You might try using zipfile.ZipFile to create the zip. Just an idea. If I remember, I'll post an example of this on the cookbook forum later.


That would be nice. In the mean time: http://www.sourcepython.com/showthread.php?599-Addon-releaser-version-1-Linux :)

Edit: It uses ZipFile now! :)
My Github repositories:

Source.Python: https://github.com/backraw
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Thu Sep 11, 2014 7:20 am

Did anybody test the addon yet? :)
My Github repositories:

Source.Python: https://github.com/backraw
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Sun Sep 14, 2014 10:17 pm

Updated to version 2.0. Now everything works, no console spams, etc. :)
My Github repositories:

Source.Python: https://github.com/backraw
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Mon Sep 15, 2014 12:54 am

I still have not had a chance to test, sorry, been really busy. I would like to offer the same advice to you that I offered in another thread:
satoon101 wrote:One more thing. If a server updates your plugin and does not restart, the old value of your plugin's "version" variable will still show the old version number. Instead you should use something like:[python]info.cvar = ConVar("{0}_version".format(info.basename), info.version, FCVAR_NOTIFY, info.name)
info.cvar.set_string(info.version)[/python]

I personally prefer make_public() to passing FCVAR_NOTIFY, just used that in the example above because that is how the other plugin had used it. Either way works, though.


Also, I find it strange that you interact with the player dictionary as if it wasn't a dictionary.
Image
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Mon Sep 15, 2014 7:29 am

satoon101 wrote:I still have not had a chance to test, sorry, been really busy. I would like to offer the same advice to you that I offered in another thread:
satoon101 wrote:One more thing. If a server updates your plugin and does not restart, the old value of your plugin's "version" variable will still show the old version number. Instead you should use something like:[python]info.cvar = ConVar("{0}_version".format(info.basename), info.version, FCVAR_NOTIFY, info.name)
info.cvar.set_string(info.version)[/python]

I personally prefer make_public() to passing FCVAR_NOTIFY, just used that in the example above because that is how the other plugin had used it. Either way works, though.


Also, I find it strange that you interact with the player dictionary as if it wasn't a dictionary.


Great, I'm going to look into it :)

Well I need it to be a dictionary type, maybe I'll need another way of handling players. But this method is convenient enough :) I'm open for any suggestions.
My Github repositories:

Source.Python: https://github.com/backraw
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Mon Sep 15, 2014 12:59 pm

This is roughly what I typically use:

Syntax: Select all

from players.entity import PlayerEntity
from players.helpers import index_from_userid


class _Player(PlayerEntity):
'''extend the PlayerEntity functionality with new methods/attributes'''


class _PlayerManager(dict):
def __missing__(self, userid):
value = self[userid] = _Player(index_from_userid(userid))
return value

def __delitem__(self, userid):
if userid in self:
super(_PlayerDictionary, self).__delitem__(userid)

player_manager = _PlayerManager()


Syntax: Select all

from events import Event

from flashfunsp.players import player_manager

@Event
def player_disconnect(game_event):
del player_manager[game_event.get_int('userid')]


@Event
def player_spawn(game_event):
player = player_manager[game_event.get_int('userid')]

if not player.isdead:
player.on_spawn()
Image
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Tue Sep 16, 2014 6:08 am

satoon101 wrote:This is roughly what I typically use:

Syntax: Select all

from players.entity import PlayerEntity
from players.helpers import index_from_userid


class _Player(PlayerEntity):
'''extend the PlayerEntity functionality with new methods/attributes'''


class _PlayerManager(dict):
def __missing__(self, userid):
value = self[userid] = _PlayerInstance(index_from_userid(userid))
return value

def __delitem__(self, userid):
if userid in self:
super(_PlayerDictionary, self).__delitem__(userid)

player_manager = _PlayerManager()


Syntax: Select all

from events import Event

from flashfunsp.players import player_manager

@Event
def player_disconnect(game_event):
del player_manager[game_event.get_int('userid')]


@Event
def player_spawn(game_event):
player = player_manager[game_event.get_int('userid')]

if not player.isdead:
player.on_spawn()


awesome! thanks :) I knew mine was getting a bit complicated
My Github repositories:

Source.Python: https://github.com/backraw
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Sun Sep 21, 2014 9:13 pm

Update #3 -- version 2.1

Created a new file for each functionality, plus added shaking when hurt.
Also, I added a strings.ini file located at ../resource/source-python/translations/flashfunsp/strings.ini
My Github repositories:

Source.Python: https://github.com/backraw
8guawong
Senior Member
Posts: 148
Joined: Sat Sep 20, 2014 3:06 am

Postby 8guawong » Thu Sep 25, 2014 10:54 am

crashes on CSGO most likely due to weapon dropping~
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Sun Sep 28, 2014 7:55 pm

8guawong wrote:crashes on CSGO most likely due to weapon dropping~


Thanks for reporting. As the first post says, I just tested it for CS:S lol. But I have both games so I'll upload an update later.

EDIT: I'll upload an update without droppping the weapon, since that works fine for CS:S, too. You can test it for CS:GO, I will too when I get time.

EDIT 2: Update #4 -- version 2.2

Fixed an AttributeError considering '.faded' which didn't exist.
Changed SayText color to orange.
Doesn't drop a weapon before it's being removed now.
My Github repositories:

Source.Python: https://github.com/backraw
8guawong
Senior Member
Posts: 148
Joined: Sat Sep 20, 2014 3:06 am

Postby 8guawong » Mon Sep 29, 2014 12:13 am

BackRaw wrote:
8guawong wrote:crashes on CSGO most likely due to weapon dropping~


Thanks for reporting. As the first post says, I just tested it for CS:S lol. But I have both games so I'll upload an update later.

EDIT: I'll upload an update without droppping the weapon, since that works fine for CS:S, too. You can test it for CS:GO, I will too when I get time.

EDIT 2: Update #4 -- version 2.2

Fixed an AttributeError considering '.faded' which didn't exist.
Changed SayText color to orange.
Doesn't drop a weapon before it's being removed now.


actually what was making it crash was the removing portion not the dropping :p

fixed by using

http://forums.sourcepython.com/showthread.php?613-Colored-Smoke&p=3202&viewfull=1#post3202

maybe not need to add hammerid for next version of SP

since http://forums.sourcepython.com/showthread.php?613-Colored-Smoke&p=3231&viewfull=1#post3231

Return to “Plugin Releases”

Who is online

Users browsing this forum: No registered users and 12 guests