Aller au contenu


Contrôler Un Double De Son Pj


  • Veuillez vous connecter pour répondre
9 réponses à ce sujet

#1 Snaïpe

Snaïpe

    ...itset tnus otroh nI


Posté 30 janvier 2010 - 13:30

En cherchant de nouveaux moyens de contourner un AI assez capricieux, j'ai fait une découverte assez impressionnante :

On peut contrôler via des scripts son propre personnage à la 3ème personne !

Quel est l'interêt ?

je ne vous citerai que deux applications qui me viennent à l'idée :

- Un combat contre soi-même ( assez intéressant pour des cas de background, c'est en partie pour cette raison que j'ai essayé de trouver une manière de contrôler le PJ, même si les 4 autres essais se sont soldés par des échecs :mrgreen: )

- Des cinématiques incluant le joueur.


deux applications qui risquent de faire grandement évoluer le modding si correctement exploitées.


Voici la théorie pure :

prenons notre joueur - il s'agit d'un NPC, possédant l'ID "player", dans le TESCS.

Je pense que vous avez déjà tous une fois essayé de faire un "PlaceAtPC, player, 1, 100, 1" pour voir ce que cela donnerait - il s'agit du même principe : générer une copie du joueur, que l'on contrôlera.


Mais, vous le savez peut être, Les Fonctions de Morrowind, lorsqu'on les cible sur un objet à plusieurs références, ne s'appliquent que sur la première référence de cet objet.

Par exemple, si vous faites un Alit->ForceGreeting, vous entamerez la discussion avec le premier Alit placé dans le jeu.


Or, nous voulons contrôler le second joueur généré, soit la deuxième référence de player !

Pour ce faire, nous allons retourner la contrainte citée plus haut à notre avantage :

Nous allons ajouter un objet unique à la première des deux références du joueur, soit nous.
Que va-t-il se passer ? le 1er joueur ( nous ) possèdera un objet que la seconde référence n'aura pas ! La chose devient très facile, ensuite, puisqu'il faudra juste tester la quantité de cet objet sur le joueur.

En pratique, ça donne quoi ?

On aura :

1 variable globale,
2 scripts.

la variable globale est importante en ce sens qu'elle constitue le seul moyen de communication entre le script déclancheur/le script executeur.

Appelons cette variable PlayerControlState.

voici notre premier script, ou script déclencheur :

PlaceAtPC, player, 1, 100, 1
Player->Additem "objet_declencheur" 1
...
Set PlayerControlState to 1

Ces instructions peuvent être lancées depuis n'importe où. n'hésitez pas à les utiliser dans les Result des Dialogues.

voici le second script, qui sera à attacher au joueur

Begin PlayerControl_Sc

Short State

If ( GetItemCount "objet_declencheur" == 0 )

; Ceci est la partie destinée aux instructions appliquées à la copie du joueur.

	If ( PlayerControlState == 1 )

		Set State to 1
		Set PlayerControlState to 2

	 endif

	 If ( state == 1 )

	elseif...

	endif

Else; Cette partie correspond aux instructions appliquées au joueur ( ex : déplacement de la "camera" )

	If ( PlayerControlState == 2 )

		Set State to 1
		Set PlayerControlState to 0

	 endif

	If ...

	...

	endif

endif

End

que ce passera-t-il ? le même script tournera en deux exemplaires indépendants sur les deux joueurs, et remplira des conditions différentes, ce qui permettra d'exécuter sur l'un et l'autre des joueurs les actions désirées ( Fonctions sans ID-> préalable, sinon ça ne marchera pas ;) )

Enfin, quand vous aurez fini, n'oubliez pas un SetDelete 1 final sur la seconde référence.


j'espère que cela vous aidera :)

Snaïpe

#2 Kiitiara

Kiitiara

Posté 30 janvier 2010 - 14:28

Waa !

Effectivement il s'aggit d'une bonne découverte...

Je suis sur qu'en le travaillant un peu on peux arriver à un résultat assez bien...
Je vais essayer de modifié un peu tout cela...

Il s'emblerait juste que le second joueur s'habille avec des vêtements que le vrai joueur possède mais de façon aléatoire (il s'uffit juste de faire la commande "equip" et voilà !)  Les objets à "effets constants" n'affecte pas le second joueur.


Sinon, excellente découverte ! je ne connaissais pas ça !

Modifié par Kiitiara, 30 janvier 2010 - 14:35.


#3 Snaïpe

Snaïpe

    ...itset tnus otroh nI


Posté 30 janvier 2010 - 15:03

Les NPC s'équpent avec le meilleur équipement qui leur est proposé en fonction de leurs capacités :mrgreen:

à moins de trimbaler continuellement 6 armures sur soi ou d'avoir 100 en combat sans armures, le NPC généré aura le même équipement. ;)

#4 elendell

elendell

    Mécano Dell'Arte


Posté 30 janvier 2010 - 17:23

Bonjour Snaïpe,

Voila le type de recherches que j'aime et je m'étais aussi un peu penché sur ça il y a quelques temps. C'est bien que tu en parles car faire un double du PJ est sans doute utilisable et inexploité ou peu.

Il y a néanmoins des particularités supplémentaires dont il faut tenir compte :

- D'abord, comme titre de sujet, je dirais plutôt "Controler un double de son PJ".

- Quand on fait le "PlaceAtPC", ce n'est pas un double du PJ actuel que l'on crée mais un double du PJ tel qu'il était lors du chargement de la sauvegarde. Si par exemple le PJ se débarrasse de son armure avant d'appeler un double, ce dernier aura toujours l'armure. Le double aura donc l'inventaire de la dernière sauvegarde chargée mais également les effets de sorts, maladies, etc. du dernier chargement, et non de l'actuel PJ.

- Les caractéristiques du double par contre ne seront ni celles du PJ actuel, ni celles du PJ sauvegardé mais celles du PJ à sa création. Ce qui limite la possibilité de combat contre soi-même car la PJ en cours de jeu sera forcément nettement supérieur au double. Le combat serait donc fortement inégal, sauf si on en tient compte. Il suffirait par exemple que le script relève les caractéristiques du PJ pour les donner au double.

- Comme tu l'as remarqué, il faut interdire l'action de sauvegarder tant que le double est dans le jeu. Si on sauvegarde, cela provoque une erreur car le moteur n'accepte pas au chargement qu'il y ait 2 "playerSavedGame". Suivant le cas, il provoquera au chargement suivant un simple message d'erreur avec option de continuer ou non. Si on continue, il supprime le double. Mais dans d'autres cas, il ne sera plus possible de charger la sauvegarde et on obtiendra un blocage du jeu avec message d'erreur "d'adresse mémoire".

La solution est donc d'interdire la sauvegarde et quand on n'a plus besoin du double dans le jeu, de faire un "SetDelete 1" dans son script, avant de permettre à nouveau l'action de sauvegarder. Comme "SetDelete" s'utilise dans les scripts locaux, ça tombe bien. Note : Il faut obligatoirement Tribunal pour utiliser "SetDelete".

- Pour une cinématique, cela peut permettre de faire des effets de caméra. Le double est un PNJ et peut donc avoir toutes les instructions d'un PNJ. En lui donnant par exemple un "AIFollow Player", on peut en marchant à reculons faire un travelling arrière filmant le double qui marche, court, saute, etc., face à la caméra.

Voir le messageSnaïpe, le 30.01.2010 à 13:44, dit :

Mais, vous le savez peut être, Les Fonctions de Morrowind, lorsqu'on les cible sur un objet à plusieurs références, ne s'appliquent que sur la première référence de cet objet.

Par exemple, si vous faites un Alit->ForceGreeting, vous entamerez la discussion avec le premier Alit placé dans le jeu.
J'utilise dans un "mod" en cours, le ciblage d'un objet qui n'est pas unique dans un script global. Cela fonctionne très bien et c'est toujours la référence que je souhaite qui est concernée. J'ai fait pour ça de nombreux essais mais mes conclusions diffèrent des tiennes. A moins qu'elle ne les complètent car à partir du moment où ce que j'avais trouvé suffisait pour mon utilisation, je n'ai pas vérifié le reste.

J'ai placé un objet identique dans diverses cellules (un par cellule, dans le Tescs). Quand le PJ fait une certaine action dans une cellule qui a un de ces objets, cela met à jour des variables de son script local et l'envoie vers une autre cellule. Le script qui fait ça est global et les instructions ciblent l'ID partagée par toutes les références. C'est toujours la référence qui est dans la cellule du PJ qui est concernée. Cela me permet de n'avoir dans le script global qu'une seule instruction ciblant un seul objet, qui sera toujours valable dans toutes les cellules. Dans le cas contraire, j'aurai dû créer de nombreux objets dans le Tescs et faire de très gros blocs d'instructions pour que le global sache quel ID il doit cibler.

Cela permet également de mettre dans un global une instruction ciblant un objet mais qui est destinée à un objet qui n'est pas encore placé car l'endroit où il sera dépendra de l'action du PJ. On place une référence de l'objet dans une cellule poubelle pour pouvoir compiler le script global et dans le jeu, ce ne sera jamais la référence poubelle qui subira l'instruction mai celle qui est dans la cellule du PJ, crée en cours de jeu.

J'en déduit de mes essais que la référence traitée est la plus proche du PJ au moment de l'instruction mais je n'ai pas vérifié en plaçant plusieurs références dans la même cellule car je n'en n'avais pas besoin. A approfondir, donc.

Voir le messageKiitiara, le 30.01.2010 à 14:42, dit :

Les objets à "effets constants" n'affecte pas le second joueur.
Si, les effets constants des objets affectent le double mais à condition qu'ils furent actifs lors de la sauvegarde de la partie. J'avais lors de mes essais un anneau d'invisibilité constante qui me sert pour les tests. Si je sauvegardais en étant invisible puis rechargeais la partie, le double que je créais ensuite était automatiquement invisible et ce, même si j'enlevais l'anneau au PJ avant de faire le "PlaceAtPC".

#5 Orann

Orann

    Nérévarine de Pertevue


Posté 30 janvier 2010 - 18:31

J'ai toujours été intrigué par cette possibilité de créer un double du joueur et par les possibilités qui s'offraient ainsi. Mais la tâche étant ardu, je ne m'y suis jamais attelé. Je vous encourage donc dans toutes vos recherches ! J'ai hâte de voir des vidéos "cinématiques" de ce genre !
Palme d'honneur 2010 pour le mod Archipel de Pertevue

Entamez votre voyage vers l'Archipel de Pertevue ! Test communautaire en cours.

#6 Snaïpe

Snaïpe

    ...itset tnus otroh nI


Posté 30 janvier 2010 - 20:55

Citation

J'utilise dans un "mod" en cours, le ciblage d'un objet qui n'est pas unique dans un script global. Cela fonctionne très bien et c'est toujours la référence que je souhaite qui est concernée. J'ai fait pour ça de nombreux essais mais mes conclusions diffèrent des tiennes. A moins qu'elle ne les complètent car à partir du moment où ce que j'avais trouvé suffisait pour mon utilisation, je n'ai pas vérifié le reste.

J'ai placé un objet identique dans diverses cellules (un par cellule, dans le Tescs). Quand le PJ fait une certaine action dans une cellule qui a un de ces objets, cela met à jour des variables de son script local et l'envoie vers une autre cellule. Le script qui fait ça est global et les instructions ciblent l'ID partagée par toutes les références. C'est toujours la référence qui est dans la cellule du PJ qui est concernée. Cela me permet de n'avoir dans le script global qu'une seule instruction ciblant un seul objet, qui sera toujours valable dans toutes les cellules. Dans le cas contraire, j'aurai dû créer de nombreux objets dans le Tescs et faire de très gros blocs d'instructions pour que le global sache quel ID il doit cibler.

Cela permet également de mettre dans un global une instruction ciblant un objet mais qui est destinée à un objet qui n'est pas encore placé car l'endroit où il sera dépendra de l'action du PJ. On place une référence de l'objet dans une cellule poubelle pour pouvoir compiler le script global et dans le jeu, ce ne sera jamais la référence poubelle qui subira l'instruction mai celle qui est dans la cellule du PJ, crée en cours de jeu.

J'en déduit de mes essais que la référence traitée est la plus proche du PJ au moment de l'instruction mais je n'ai pas vérifié en plaçant plusieurs références dans la même cellule car je n'en n'avais pas besoin. A approfondir, donc.

D'après mes souvenirs, quand j'avais placé dans ma cellule de test cinq références d'objets, le premier était bien touché - mais comme le test avait été fait dans une seule et unique cellule, je n'ai pas pu m'apercevoir de ça.

C'est intéressant car dans ce cas, le ciblage s'effectuerait sur la première référence ( en terme de valeur ) chargée, et donc dans la/les cellules actuelles.

Il me faudra faire de nouveaux tests, car lors de mon 1er test sur le ciblage, j'avais mis les créatures en file indienne - il se peut que le ciblage se fasse, dans ce cas, à la référence la plus proche en terme de distance.

Citation

- Pour une cinématique, cela peut permettre de faire des effets de caméra. Le double est un PNJ et peut donc avoir toutes les instructions d'un PNJ. En lui donnant par exemple un "AIFollow Player", on peut en marchant à reculons faire un travelling arrière filmant le double qui marche, court, saute, etc., face à la caméra.

je m'était amusé à faire tout un package d'AI à ce niveau, et les résultats ont été convaincants :orthoseveres: j'ai même réussi à faire danser mon PJ, en lui ajoutant l'animation des "Dancing Girls" :mrgreen:

mais cela met en valeur un petit défaut à ce système : l'incompatibilité...

en effet, le script est à attacher sur le joueur même, et donc modification d'un élément de base veut dire... conflits. A user à bon escient, je dirais donc.


Autre chose : concernant le ciblage, il devient possible de cibler une Xème référence grâce à ce script, si on l'attache sur une créature/NPC :

Begin Script

Short State
Float Timer

If ( State == -1 )
	return
endif

If ( GetItemCount "Mon_Objet" == 0 )

	If ( Globale < X ); où X est la référence voulue.

		Additem, "Mon_Objet" 1
		Set Globale to Globale + 1
		Return

	elseif ( Globale == X )

	  ; Les actions sur une Xème ref.

		Set Globale to 0
		Set State to -1

		return

	Endif

endif

End

ce ciblage risque d'être assez aléatoire en revanche, mais peut être utile pour générer un inventaire d'objets et/ou de magies pour un groupe d'ennemis, par ex.

Citation

- Les caractéristiques du double par contre ne seront ni celles du PJ actuel, ni celles du PJ sauvegardé mais celles du PJ à sa création. Ce qui limite la possibilité de combat contre soi-même car la PJ en cours de jeu sera forcément nettement supérieur au double. Le combat serait donc fortement inégal, sauf si on en tient compte. Il suffirait par exemple que le script relève les caractéristiques du PJ pour les donner au double.

c'est intéressant dans l'idée que l'équipement du double sera différent s'il a les caractéristiques de départ.

Donc, la meilleure chose à faire sera d'initialiser le second joueur en récupérant les stats.

#7 Kiitiara

Kiitiara

Posté 01 février 2010 - 17:12

Bonjour :mdr:

Je demande juste comment faire pour contrer un script du type :  
player->startcombat "player"
ou bien faut-il passer par un script qui modifie l' IA pour que le double du joueur attaque le joueur et qu'une fois mort l' IA du double soit rétablie...

Car ce genre de script sont un peu déliquat à cause du double du joueur...

Ou bien faut-il faire un script du style :

If (getitemcount "objet_unique" == 1 )
	player-> Startcombat "player"
et donc dans ce cas là , assigner ce script au double et mettre l'objet unique dans l'inventaire du double du joueur  qu'une fois mort ou autre...cet objet disparaîsse ?

Merci de vos réponses...

#8 Snaïpe

Snaïpe

    ...itset tnus otroh nI


Posté 01 février 2010 - 18:18

Si j'ai bien compris, tu veux que le double attaques ton PJ ?

Et bien, dans ton script attaché au joueur, tu auras une partie contenant un :

If (getitemcount "objet_unique" == 0 )

	...
	Startcombat "player"
	...

endif

attention cependant : il ne faut pas faire de confusions :

- l'objet unique est ajouté au joueur original. il convient donc, après la destruction du double par SetDelete 1, d'enlever cet objet au 1er joueur.

- player->StartCombat player revient à demander au joueur original de s'attaquer lui-même. il ne faut pas mettre d'ID initial dans ce cas ( voir balise code ci-dessus )

Si j'ai compris de travers et que tu souhaites que les deux joueurs ne s'attaquent pas, tu peux détecter le combat avec des fonctions comme GetWeaponDrawn, et autres, puis le stopper avec StopCombat.

#9 Kiitiara

Kiitiara

Posté 01 février 2010 - 18:38

Voir le messageSnaïpe, le 01.02.2010 à 18:32, dit :

Spoiler

Bonjour,

Oui, oui...cela je le savais déjà mais...c'est le
"starstcombat "player"" qui me dérange car ce n'est que dans le TESCS que l'on peux faire un script comme ça (sans un nom devant) car il est déjà assigné à un NPC alors que dans d'autres script : par exemple un activateur qu'une fois activé forcerais un NPC à attaquer alors là on est obligé de mettre un truc du style "NPC->StartCombat "player""

Alors là je crois qu'il faudrait faire :
If Player ( getitemcount "objet_unique" == 0 )

	...
	Startcombat "player"
	...
Et donc par d'autre scripts ajouter cet "objet unique" dans l'inventaire de joueur originel...Mais cela peux aussi l'ajouter dans celui du double ,non ?

:peur: WAHHH ! :peur:


Bon ben je vais devoir encore y réfléchir... :)

#10 Snaïpe

Snaïpe

    ...itset tnus otroh nI


Posté 02 février 2010 - 21:30

et bien, le problème est qu'il est théoriquement impossible de cibler le 2nd joueur ( on ne fait que passer par un ciblage implicite ), toute tentative externe par un Player-> aboutissant au ciblage sur le PJ original.

Citation

Et donc par d'autre scripts ajouter cet "objet unique" dans l'inventaire de joueur originel...Mais cela peux aussi l'ajouter dans celui du double ,non ?
toute fonction utilisant un "player->" initial ciblera toujours le vrai PJ.

ce que tu proposes est de passer par un script global ou local externe au joueur, mais dans ce cas, ton Startcombat Player ne marchera pas, puisque son effet sera appliqué soit à l'objet/activateur en question, soit à rien du tout ( cas du script global )

pour travailler avec ton second joueur, tu est obligé de passer par un script local à "player".

ou sinon, tu peux essayer de scripter avec le MWSE ( MorroWind Scripting Extender ), car tu peux alors utiliser la fonction xSetRef pour stocker la référence dans une variable ( de type String il me semble ); néanmoins, cela requiert de passer par MWEdit et d'utiliser MWSE ou MGE, et on ne sort toujours pas totalement du script local sur le joueur...

P.S. attention à la syntaxe :

c'est "If ( Player->GetItemCount "ID" == X )" et pas " If Player ( GetItemCount "ID" == X )" :green:




0 utilisateur(s) li(sen)t ce sujet

0 membre(s), 0 invité(s), 0 utilisateur(s) anonyme(s)