[CSS] Setting weapon view models.

Please post any questions about developing your plugin here. Please use the search function before posting!
User avatar
Doldol
Senior Member
Posts: 200
Joined: Sat Jul 07, 2012 7:09 pm
Location: Belgium

[CSS] Setting weapon view models.

Postby Doldol » Mon Dec 01, 2014 7:20 pm

I've been trying to set weapon view models using this topic https://forums.alliedmods.net/showthread.php?t=244926 as a guideline.

The way the code in that topic achieves custom weapon models nearly perfectly is by binding the custom model to a second possible view model index, which appears to stay unused by default and copying m_nSequence, m_flPlaybackRate and m_nSequence over from the default model, then hiding the default model and drawing the secondary model, I tested this with the provided SM plugin and it works almost flawlessly.

Now, I've basically tried to convert the plugin from SM to SourcePython. I implemented it as good as I could in SourcePython (while staying relatively close and not doing Pythonic optimisations so I could more easily compare and not lose track), but basically no dice. I end up with my knife model being invisible.

But I was able to semi-reliably glitch the model to stick to my glock when spawning and weapon switching (glock-knife-glock), and upon toggling between burst fire and semi I was able to get the custom model's animations fully working on the glock when I each time toggled between firemodes.

So I'm getting the feeling that I'm close. At this point some help would really be appreciated. Here is my best working code:

Syntax: Select all

import memory


from memory.manager import manager
from memory.manager import CustomType
from memory.manager import Argument
from memory.manager import Return


from filters.players import PlayerIter
from filters.entities import EntityIter


from players.helpers import index_from_pointer, index_from_userid
from entities.helpers import pointer_from_inthandle, edict_from_index, index_from_inthandle
from players.entity import PlayerEntity
from entities.entity import BaseEntity


from events import Event
from tools import server_tools
from core import PLATFORM


from engines.precache import Model
from stringtables.downloads import Downloadables


model = Model('models/weapons/franug_cwi/knuckles/v_knuckles.mdl')
dl = Downloadables()
dl.add("models/weapons/franug_cwi/knuckles")
dl.add("materials/models/weapons/v_models/brass_knuckles")


EF_NODRAW = (1 << 5)


just_spawned = set()
ClientVM = (dict(), dict())


model_overrides = dict()
is_custom = dict()


old_weapon_index = dict()
old_sequence = dict()
old_cycle = dict()


Weapon_CanUse = None
PreThink = None
PostThink = None
Spawn = None


class CBaseEntity(CustomType, metaclass=manager):
Spawn = manager.virtual_function(
22 if PLATFORM == 'windows' else 23,
(),
Return.VOID
)


@property
def base_entity(self):
return BaseEntity(index_from_pointer(self))


class CCSPlayer(CustomType, metaclass=manager):
PreThink = manager.virtual_function(
331 if PLATFORM == 'windows' else 332,
(),
Return.VOID
)
PostThink = manager.virtual_function(
332 if PLATFORM == 'windows' else 333,
(),
Return.VOID
)


@property
def player_entity(self):
return PlayerEntity(index_from_pointer(self))


def set_weapon_model(weapon_index, model_index):
weapon = BaseEntity(weapon_index)
player = PlayerEntity(index_from_inthandle(weapon.m_hOwner))
classname = weapon.classname
# player.drop_weapon(weapon.pointer, True, True)
# weapon.Kill()
server_tools.remove_entity_immediate(weapon_index)
player.give_named_item(classname, 0)
for weapon_index in player.weapon_indexes(is_filters='knife'): break
model_overrides[weapon_index] = model_index




def load():
for_players_already_on()
hook_all()
for_players_already_on()


def unload():
unhook_all()


@Event
def player_activate(event):
hook_all()


@Event
def player_death(event):
ClientVM[1][PlayerEntity(index_from_userid(event.get_int("userid"))).inthandle].m_fEffects |= EF_NODRAW # += EF_NODRAW


@Event
def player_spawn(event):
player = PlayerEntity(index_from_userid(event.get_int("userid")))
just_spawned.add(player.inthandle)
for weapon_index in player.weapon_indexes(is_filters='knife'):
print("Setting Knife", weapon_index, "to", model.index, model)
set_weapon_model(weapon_index, model.index)


def for_players_already_on():
for index in PlayerIter(return_types='index'):
player_entity = PlayerEntity(index)
ClientVM[0][player_entity.inthandle] = BaseEntity(index_from_inthandle(player_entity.m_hViewModel))
for index2 in EntityIter("predicted_viewmodel", True, "index"):
be = BaseEntity(index2)
if be.m_hOwner == player_entity.inthandle:
if be.m_nViewModelIndex == 1:
ClientVM[1][player_entity.inthandle] = be
break


def hook_all():
global Weapon_CanUse, PreThink, PostThink, Spawn
if PostThink is not None:
return
#CBaseEntity
for pointer in EntityIter("predicted_viewmodel", True, "pointer"): break
try:
entity = memory.make_object(CBaseEntity, pointer)
except UnboundLocalError:
print("Could not hook on entity")
else:
entity.Spawn.add_post_hook(post_spawn)
Spawn = entity.Spawn
#CCSPlayer
players = tuple(PlayerIter(return_types='pointer'))
if not players:
return
player = memory.make_object(CCSPlayer, players[0])
# player.PreThink.add_post_hook(post_prethink)
player.PostThink.add_post_hook(post_postthink)
# PreThink = player.PreThink
PostThink = player.PostThink


def unhook_all():
if PostThink is not None:
# PreThink.remove_post_hook(post_prethink)
PostThink.remove_post_hook(post_postthink)
if Spawn is not None:
Spawn.remove_post_hook(post_spawn)


def post_spawn(args, return_value):
entity = memory.make_object(CBaseEntity, args[0])
owner = entity.base_entity.m_hOwner
if owner:
index = entity.base_entity.index
viewmodel = entity.base_entity.m_nViewModelIndex
ClientVM[viewmodel][owner] = BaseEntity(index)


def post_prethink(args, return_value):
player = memory.make_object(CCSPlayer, args[0])


def post_postthink(args, return_value):
player = memory.make_object(CCSPlayer, args[0])
if not player.player_entity.isdead:
player_handle = player.player_entity.inthandle
try:
weapon_index = index_from_inthandle(player.player_entity.active_weapon)
old_weapon_index[player_handle]
except (ValueError, KeyError):
weapon_index = -1
sequence = ClientVM[0][player_handle].m_nSequence
cycle = ClientVM[0][player_handle].m_flCycle
if weapon_index <= 0:
ClientVM[1][player_handle].m_fEffects |= EF_NODRAW # += EF_NODRAW


is_custom[player_handle] = False
else:
if weapon_index != old_weapon_index[player_handle]:
try:
custom_model_index = model_overrides[weapon_index]
except KeyError:
ClientVM[1][player_handle].m_fEffects |= EF_NODRAW # += EF_NODRAW


is_custom[player_handle] = False
else:
ClientVM[0][player_handle].m_fEffects |= EF_NODRAW # += EF_NODRAW
ClientVM[1][player_handle].m_fEffects &= ~EF_NODRAW # -= EF_NODRAW


ClientVM[1][player_handle].m_nModelIndex = custom_model_index
ClientVM[1][player_handle].m_hWeapon = ClientVM[0][player_handle].m_hWeapon


ClientVM[1][player_handle].m_nSequence = ClientVM[0][player_handle].m_nSequence
ClientVM[1][player_handle].m_flPlaybackRate = ClientVM[0][player_handle].m_flPlaybackRate


is_custom[player_handle] = True
else:
try:
custom = is_custom[player_handle]
except KeyError:
pass
else:
if custom:
ClientVM[1][player_handle].m_nSequence = ClientVM[0][player_handle].m_nSequence
ClientVM[1][player_handle].m_flPlaybackRate = ClientVM[0][player_handle].m_flPlaybackRate
if cycle < old_cycle[player_handle] and sequence == old_sequence[player_handle]:
ClientVM[1][player_handle].m_nSequence = 0
try:
just_spawned.remove(player_handle)
except KeyError:
pass
else:
try:
custom = is_custom[player_handle]
except KeyError:
pass
else:
if custom:
ClientVM[0][player_handle].m_fEffects |= EF_NODRAW # += EF_NODRAW
old_weapon_index[player_handle] = weapon_index
old_sequence[player_handle] = sequence
old_cycle[player_handle] = cycle
# print("postthink", args[0], weapon_index, sequence, cycle, player.player_entity.name)


Side note: All in all I was at the very least very happy that the server wasn't laggy at all. Which IMO was quite a testament to how solid SourcePython is over EventScripts (I should do a performance comparison one day).
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Mon Dec 01, 2014 7:40 pm

Not that this is the cause of any issues you are having, as you likely have the models/materials already on your client, but Downloadables().add only accepts files, not directories. However, that is not a bad idea. I think we should consider adding that functionality.
Image
User avatar
Doldol
Senior Member
Posts: 200
Joined: Sat Jul 07, 2012 7:09 pm
Location: Belgium

Postby Doldol » Mon Dec 01, 2014 8:16 pm

satoon101 wrote:Not that this is the cause of any issues you are having, as you likely have the models/materials already on your client, but Downloadables().add only accepts files, not directories. However, that is not a bad idea. I think we should consider adding that functionality.


It's mainly there for me to later not forget to put that in correctly. This is above all test code.
However I agree adding it would not be a bad idea, but I would suggest putting in a file extension filter, I don't know if Source checks for file extensions in this case, but I think some filetypes should be selected out to be scanned for, especially when only specifying a directory. Maybe with an optional argument to specify an iterable of extensions as override?

Return to “Plugin Development Support”

Who is online

Users browsing this forum: No registered users and 24 guests