Massassi Forums Logo

This is the static archive of the Massassi Forums. The forums are closed indefinitely. Thanks for all the memories!

You can also download Super Old Archived Message Boards from when Massassi first started.

"View" counts are as of the day the forums were archived, and will no longer increase.

ForumsCog Forum → Could someone finish a weap_seqcharge.COG for me?
Could someone finish a weap_seqcharge.COG for me?
2005-06-01, 1:13 PM #1
Modified Sequencer Charge COG: currently, primary fire lays a sequencer mine w/proximity option while secondary fire detonates the last mine that's been layed. Would like secondary fire to detonate ALL mines that have been layed (or preferably one at a time, 1st layed mine to most recent).

Here's the COG:


Code:
# Jedi Knight Cog Script
#
# WEAP_SEQCHARGE.COG
#
# WEAPON 8 Script - Sequencer Charge
#
# Heartier than the Thermals and the DF IM Mines, primary fire 
#provides proximity mode while secondary fire has been
#modified to provide "manual detonation mode" similar to 
#the MotS Sequencers
# -- but only for the last mine that's been dropped :(
#
# - Not affected by MagSealed sectors/surfaces.
#
# [YB & CYW]
#
# ========================================================================================

symbols

model        povModel=seqv.3do                  local                         
model        weaponMesh=seqg.3do                local                         

keyframe     mountAnim=SeqVmnt.key              local                         
keyframe     dismountAnim=SeqVdis.key           local                         
keyframe     povfireAnim=SeqVpst1.key           local                         
keyframe     holsterAnim=kyhlstr.key            local                         

flex         mountWait                          local                         
flex         fireWait=0.8                       local                         
flex         holsterWait                        local                         

template     projectile_tpl=+seqchrg2           local
template     detnt=+sequencer_exp		 	local                 

material     flashing=seq0mtp3.mat              local                         
int          cel                                local                         
int          mode                               local
                                              
thing        projectile				 	local                 

thing        player                             local                         
int          trackID=-1                         local                         
int          holsterTrack                       local                         

int          selectMode=1                       local                         

template     detnt=+sequencer_exp		 	local

message      	startup                                                          
message      	activated                                                        
message      	deactivated                                                      
message      	selected                                                         
message      	deselected                                                       
#message     	newplayer                                                        
message      	autoselect                                                       
message      	fire
message     	timer

end                                                                           

# ========================================================================================

code

startup:
   // Setup delays and variables.
   mountWait    = GetKeyLen(mountAnim);

   // Start the material flashing.
   MaterialAnim( flashing, 4, 1 );

	GetInv(GetLocalPlayerThing(), 116); 

   Return;

# ........................................................................................

fire:
   player = GetSourceRef();
   mode = GetSenderRef();

   // Check that the player is still alive.
   if(GetThingHealth(player) <= 0)
      Return;

if(mode == 0)
{
if((GetInv(player, 8) == 0) && (GetInv(player, 116) == 0))
{
   // If out of ammo try to autoswitch to another weapon
   // if autoswitch is enabled else just switch to fists.

     if(GetAutoSwitch() & 1)
         SelectWeapon(player, AutoSelectWeapon(player, 1));
    else
      SelectWeapon(player, 1);
   Return;
}

if(GetInv(player, 8) > 0)
{
	projectile = FireProjectile(player, projectile_tpl, -1, 16, '0 0.05 0.00', '0 0 0', 1.0, 0, 0.0, 0.0);

	jkPlayPOVKey(player, povfireAnim, 1, 0x38);
	ChangeInv(player, 8, -1.0);
	ChangeInv(player, 116, 1.0);
	SetTimerEx(30, 3, 0, 0);
   }
      }
else
{
if(GetInv(player, 8) == 0)
{
if(GetAutoSwitch() & 1)
   SelectWeapon(player, AutoSelectWeapon(player, 1));
else
   SelectWeapon(player, 1);

	Return;
}
else
if(GetInv(player, 116) > 0)
	DestroyThing(projectile);
	dummy = CreateThingAtPos(detnt,GetThingSector(projectile),GetThingPos(projectile),'0 0 0');
	#ChangeInv(player, 116, -1.0);
	killTimerEx(3);

	Return;

}

	Return;

# ........................................................................................

activated:
   player = GetSourceRef();
   mode = GetSenderRef();

   if(mode > 1) Return;

   jkSetWaggle(player, '0.0 0.0 0.0', 0);
   ActivateWeapon( player, fireWait, mode );

   Return;

# ........................................................................................

deactivated:
   player = GetSourceRef();
   mode = GetSenderRef();

   jkSetWaggle(player, '10.0 7.0 0.0', 350);
   DeactivateWeapon( player, mode );

   Return;

# ........................................................................................

selected:
   player = GetSourceRef();
   PlayMode(player, 40);
{
   jkSetPOVModel(player, povModel);
    SetArmedMode(player, 0);
   jkSetWeaponMesh(player, weaponMesh);
}
  trackID = jkPlayPOVKey(player, mountAnim, 0, 20);
   jkSetWaggle(player, '10.0 7.0 0.0', 350);
   SetMountWait(player, GetKeyLen(mountAnim));

   jkClearFlags(player, 0x5);
   SetCurWeapon(player, 8);
  jkPrintUNIString(player, 8);

  Return;

# ........................................................................................

deselected:
   player = GetSourceRef();

   jkPlayPOVKey(player, dismountAnim, 0, 18);

   holsterWait = GetKeyLen(holsterAnim);
   SetMountWait(player, holsterWait);
   holsterTrack = PlayKey(player, holsterAnim, 1, 0x4);
   SetTimerEx(holsterWait, 2, 0.0, 0.0);
   if (trackID != -1)
   {
      jkStopPOVKey(player, trackID, 0);
      trackID = -1;
   }
   jkSetWaggle(player, '0.0 0.0 0.0', 0);
   Return;

# ........................................................................................

//newplayer:
//   player = GetSourceRef();
//
//   // Make sure that if the player is respawning, the old mount isn't playing anymore.
//   if (trackID != -1)
//      jkStopPOVKey(player, trackID, 0);
//
//   Return;

# ........................................................................................

autoselect:
   selectMode = GetSenderRef();
   player = GetSourceRef();

   // If the player has ammo
   if(GetInv(player, 8) >= 0)
{
      // query for ammo
      if(selectMode == -1)
      {
         ReturnEx(300.0);
         Return;
      }

      if((selectMode == 0) && !(GetAutoPickup() & 2))
      {
         ReturnEx(300.0);
         Return;
      }

      if((selectMode == 1) && !(GetAutoSwitch() & 2))
      {
         ReturnEx(300.0);
         Return;
      }

      if((selectMode == 2) && !(GetAutoPickup() & 2))
      {
         ReturnEx(300.0);
         Return;
      }

      	ReturnEx(-2.0);
      	Return;
   }
else
   {
      ReturnEx(-1.0);
   }
   Return;

# ........................................................................................

timer:
   StopKey(player, holsterTrack, 0.0);
   Return;

# ........................................................................................

end
[Use code tags, please]

Any help is GREATLY appreciated...

Thanx...!

_K_
2005-06-01, 1:33 PM #2
Not sure about the exact verbs anymore, but you need to assign a value to each mine that is placed (GetThingSignature, maybe?). This value has to contain the unique player id of the placer. It will be easy, then, to loop through all things in the level and only destroy the things that have this special value assigned. If you want to destroy them one at a time, you also have to encode the time each mine was placed in it's assigned value, so that in the loop, you can destroy only the one with the oldest time value.

Good luck!
"Häb Pfrässe, süsch chlepfts!" - The coolest language in the world (besides Cherokee)
2005-06-01, 3:37 PM #3
Hmmm.... a little stumped on this one....

_K_
2005-06-01, 9:42 PM #4
Only posting the stuff that should be changed. By the way, I changed your autoswitching a bit. If a person who had set down all of his mines (with the autoswitch on), tried to set them off, his weapon would then be switched instead of firing, the way you had it.

Code:
fire:
player = GetSourceRef();
mode = GetSenderRef();

// Check that the player is still alive.
if(GetThingHealth(player) <= 0)
   Return;

if(mode == 0){
   if((GetInv(player, 8) == 0) && (GetInv(player, 116) == 0)){
      // If out of ammo try to autoswitch to another weapon
      // if autoswitch is enabled else just switch to fists.

      if(GetAutoSwitch() & 1)
         SelectWeapon(player, AutoSelectWeapon(player, 1));
      else
         SelectWeapon(player, 1);
      Return;
   }

   if(GetInv(player, 8) > 0){
      projectile = FireProjectile(player, projectile_tpl, -1, 16, '0 0.05 0.00',
                                 '0 0 0', 1.0, 0, 0.0, 0.0);

      jkPlayPOVKey(player, povfireAnim, 1, 0x38);
      ChangeInv(player, 8, -1.0);
      ChangeInv(player, 116, 1.0);
      SetThingUserData(projectile, player);  //This is used to check that we 
                                             //only destroy those projectiles 
                                             //this player set down.  It'd be 
                                             //rather unfair to enable the 
                                             //player to destroy the sequencers 
                                             //of another player.  You could 
                                             //remove this and the check below 
                                             //to allow it, however.
      
      //SetTimerEx(30, 3, 0, 0);  What's the point of this?  All it does is
      //                          stop a track which should be long finished
      //                          by the time the timer's run out.
   }
}else{
   if((GetInv(player, 8) == 0) && !GetInv(player, 116)){
      if(GetAutoSwitch() & 1)
         SelectWeapon(player, AutoSelectWeapon(player, 1));
      else
         SelectWeapon(player, 1);
      Return;
   }else{
      for(i = 0; i < GetThingCount(); i = i + 1){
         if((GetThingTemplate(i) == projectile_tpl)
            && (GetThingUserData(i) == player)){
             DestroyThing(i);
             dummy = CreateThingAtPos(detnt, GetThingSector(i), GetThingPos(i), 
                                      '0 0 0');
         }
      }
      SetInv(player, 116, 0);  //A safety.  It's possible that a sequencer got
                               //destroyed for some other reason, so just to be 
                               //safe, clear them off our saved amount.
   }
   //killTimerEx(3); // See above.
   Return;
}
Return;


I fixed up that code a bit to be more readable, lol. This should do it.
_ _ _____________ _ _
Wolf Moon
Cast Your Spell On Me
Beware
The Woods At Night
The Wolf Has Come
2005-06-01, 11:12 PM #5
GetThingCount() returns the number defined in the level's JKL file, not the current number of things. Consider using a trigger (sent only to the local player so as to not waste bandwidth), and add a trigger: message section to the sequencer class COG that checks the userdata of the sequencer it's assigned to and then does SetLifeLeft() to detonate itself instead of creating an explosion at the location that won't give the player kill credit. Thing userdata isn't sync'd over a network, so mines set by someone else would have the default userdata value, while your mines you could designate a number (don't use the player's thing number because the host is usually 0 which is the default userdata of a thing; simply setting it to 1 upon creation and checking for 1 would work).

That all make sense? If you can't get that working I'll take a stab at it.

QM
2005-06-02, 9:11 AM #6
You guys are _AWESOME_...!!!

LKOH_SniperWolf: Thanx a million -- no, a BILLion -- no a TRILLION...!!!

Must peruse the DM more: this "GetThingCount" is gonna be _Super_ handy...!

Not quite sure what Quib M. is gettin' at, tho...

Thanx again...!
2005-06-02, 11:54 AM #7
/sigh, I was trying to explain the flaws in LKOH_SniperWolf's code politely and in a constructive way so that he or you (Lord Kaje) might learn from it. I'll break it up a little more step-by-step.

---

Problem 1: GetThingCount()
GetThingCount() returns the number defined in the level's JKL file, not the current number of things.

Solution 1: Local Triggers
Consider using a trigger (sent only to the local player so as to not waste bandwidth), and add a trigger: message section to the sequencer class COG that checks the UserData of the sequencer it's assigned to.

---

Problem 2: SetThingUserData(projectile, player)
The default UserData value for a thing is 0. Usually the hosts thing number is 0. This causes a problem, as UserData isn't sync'd in multiplayer, so on the hosts machine, all sequencers will have a UserData of 0.

Solution 2: SetThingUserData(projectile, 1)
Since UserData isn't sync'd, all sequencers created on your machine will have 1, and all those made by others will be 0.

---

Problem 3: CreateThingAtPos()
This will create an explosion where the mine is, but will not give the owner of the sequencer kill credit; it'll be a suicide for whoever dies to the explosion.

Solution 3: SetLifeLeft(projectile, 0.1)
This will cause the sequencer to time out in 0.1 seconds and explode.

---

I think that's everything. I also suggest you stop using bin 116 to keep track of what could simply be a local COG integer, instead of wasting a bin to do the same thing.

QM
2005-06-02, 12:30 PM #8
Quibmask, I had indeed considered that idea, however, a class cog receiving a trigger message has no reference to the object itself.
And since as far as I know, there is not a seperate running form of the cog for each object (someone feel free to correct me if wrong), it would most likely fail to destroy every sequencer, and instead destroy only the last dropped. (Unless you wanted to start messing with using the heap to store the thing number of every single sequencer)

GetThingCount() will work perfectly fine in this case. It's really not necessary to worry about whether there's an object in the slot or not. If there isn't, the template will be returned as either -1 or 0 (I'd assume -1 in the case of an invalid thing), and most certainly wouldn't match the projectile template.

As for the trigger, if I used it, I'd send it to everyone, give it a rather random ID, and use GetParam(0) as the true ID. Doing so would allow it to work in SP as well as MP.

As for SetThingUserData(), I haven't checked syncing myself. Even if that's the case, you could simply replace GetThingUserData(i) with GetThingParent(i).
_ _ _____________ _ _
Wolf Moon
Cast Your Spell On Me
Beware
The Woods At Night
The Wolf Has Come
2005-06-02, 3:03 PM #9
Class COGs load for each object so there is a seperate one running for each object. GetSenderRef() is the thing itself in the created: message of the class COG, so it can be saved as a variable for later use. Someone correct me if I'm wrong.

Triggers do not always broadcast to every computer and don't necessarily need to. They are much less reliable than SkillTarget() (the other verb that can send data to another player directly), so using them locally to SetLifeLeft() will be more likely to sync properly in MP than broadcasting a trigger. Using SendTrigger(GetLocalPlayerThing(), etc. should do the trick. It should work both in SP and MP as long as the COG flags for the sequencer class COG are set to 0x40.

GetThingCount() should more or less never be used. The proper alternative to doing it the way you did would be 2 for loops, one inside the other, the first using GetSectorCount() and the child loop using GetSectorThingCount(). The verbs GetFirstThingInSector() and GetNextThingInSector() would then cycle through all things in that sector. This would accurately go through all things in game.

Something like
for(i=0; i=i+1; i<GetSectorCount())
{
for(j=0, k=FirstThingInSector(i); j=j+1, k=NextThingInSector(k); j<GetSectorThingCount(i))
{
do stuff to k;
}
}

You don't have to check if UserData sync's, a lot of people already have and it doesn't.

Anyway, using a trigger was just my suggestion, you could also keep an array of dropped sequencers, or run through every thing in game checking if it's a sequencer. However, I'm telling you very directly that GetThingCount() is a bad verb and will only lead to faulty COGs and CreateThingAtPos() will cause people to get suicide counts and not give kills to the owner of the sequencers.

With what you posted, manually detonated sequencers will give people a suicide death, if the host detonates will detonate sequencers they didn't place, and if the World Thing Count in the level's JKL file is kinda low, some sequencers may not be within the counting loop and won't get detonated. I'm trying to help you make a better COG, not just outright criticize your work.

QM
2005-06-02, 6:31 PM #10
Only triggers with a destination of -1 work in SP, unless you first load into MP. And even then, the ID of the triggers return incorrectly. This information provided by ZeqMacaw after asking him to look into it a few days ago. (Datamaster suggests that triggers do not work at all in SP, unless you first go into MP, however, Zeq found that inaccurate, luckily).

Why I said I've never tested SetThingUserData(), is it isn't written in the few COG references that it does not sync. As I said, that could be fixed simply by changing GetThingUserData(i) to GetThingParent(i).

As for class cogs, you're incorrect. Only a single cog is run. It essentially 'captures' all items that have it as a class cog. Because of this, you'd have to keep a copy of each one's thing number, which essentially means using the heap. You could, use a static array, however, it'd mean limiting specificially the number of sequencers existing at any one time.

CreateThingAtPos() was Kaje's coding. I just copied it.

GetThingCount() works fine, honestly. It may grab some nonexistent thing slots, but that's honestly ok. And in most other cases as well.


"if the World Thing Count in the level's JKL file is kinda low, some sequencers may not be within the counting loop and won't get detonated."

That's just nonsense. There cannot be more things than the World Thing Count. Things are defined as a static array of structs, on level start. There will NEVER be more than the world thing count. (Which is why if you hit the thing count, you can no longer fire projectiles, no things will be created, etc...)
_ _ _____________ _ _
Wolf Moon
Cast Your Spell On Me
Beware
The Woods At Night
The Wolf Has Come
2005-06-02, 11:51 PM #11
Alright, I went and ran my own test to confirm/oppose what you said.

First, you're right on class COGs. Only 1 loads, so that ruins the trigger idea unless an array is put in the class COG. To test this, I used class_sequencer.cog and added PrintInt(GetSelfCOG()); to the created: message.

Second, I tested triggers in single player and multiplayer, just like you said, -1 works in both, sending a trigger to the player's thing only works in single player after loading MP once. I didn't have any trouble with the trigger returning incorrect IDs in SP (well, or MP), and all 4 variables in the verb also echo'd accurately. I didn't tst exceedingly large values here though.

Third, I tested GetThingCount() as follows:
In class_sequencer.cog's created: section, I added
Code:
   PrintInt(GetThingCount());
   l=0;
for(i=0; i=i+1; i<GetSectorCount())
{
for(j=0, k=FirstThingInSector(i); j=j+1, k=NextThingInSector(k); j<GetSectorThingCount(i))
{
l=l+1;
}
}
   PrintInt(l);
   SetLifeLeft(GetSenderRef(), 0);

The SetLifeLeft() verb is so the sequencers wouldn't have a time limit so I could drop a lot of them. Then I loaded up default Canyon Oasis, and the first time I dropped a sequencer it printed 266 followed by 200. The next said 266, 201. 266, 202. 266, 203. And so on. Eventually it got to 266, 266. Then 266, 267. It went to 266, 299 before sequencers (and any projectile) stopped being created. So I'm glad I was right about something.

I guess my suggestion changes to replacing the GetThingCount() loop with the loop I suggested, changing the UserData value and using SetLifeLeft(projectile, 0.1) instead of CreateThingAtPos(). Don't bother with triggers.

QM
2005-06-03, 1:18 AM #12
There is a way to get the class cog to work. I've done it before with a Natural Selection commander interface thingie.

I'll try to see if I have it around, but if I remember off the top of my head, checking the thingid or the signature would make it work.

[edit] Ok, I found my old class cog. It didn't use triggers, but it did store the variable for the object. AKA a seperate class cog for each object created.
Code:
# Natural Selection Cog Script
#
# armory.cog
#
# Plays the Key Frame of a armory from Natural Selection.
# When the Key ends, the keyframe armory is replaced with a solid armory for collide purposes.
#
# THIS COG IS NOT SUPPORTED BY LEC
#
# SG-fan
# ========================================================================================
//flags 0x80
symbols

message		activate
message		timer
message		created

keyframe        openanim=armory.key		local
thing           armory				local
template	armorytemplate=ns_armory		local

int	open=0		local
flex	wait		local
end

# ========================================================================================

code
activate:
if(open) return;
	open=1;
	wait=GetKeyLen(openanim);
	PlayKey(armory, openanim, 2, 0x4);
	SetTimer(wait);
   Return;
# ========================================================================================
Timer:
If(armory==-1) return;
	CreateThing(armorytemplate, armory);
	DestroyThing(armory);
Return;


        Created:
	armory=GetSenderRef();
     
	Printint(armory);
Return;
end

I believe the line in red is all that you would need to fix the class cog. (Also remember, I havn't tested this cog in about 2 years. I can't guarentee that I got the right one.) [/edit]
Sam: "Sir we can't call it 'The Enterprise'"
Jack: "Why not!"
2005-06-03, 11:14 AM #13
Sorry Quib, no. I tested myself, and at 266, nothing else is created. Only enough memory for the specific number of things in the JKL is allocated. It is literally impossible to have more than that. I would say there's a mistake in your code.

Code:
   PrintInt(GetThingCount());
   for(iterative = 0; iterative < GetSectorCount(); iterative = iterative + 1){
      wasCreatedOnce = wasCreatedOnce + GetSectorThingCount(iterative);
   }
   PrintInt(wasCreatedOnce);
   wasCreatedOnce = 0;


Used that code in class_sequencer.cog, with a long timer on the seqs (over 10 min... Once both reached 266, nothing else could be created, and the counts stayed at that until a seq was destroyed...) The thing count will NEVER go over the world count.

Why didn't yours work right? Well, look back over the format of for loops. I'll sum that up here for you...

for(initialization; CONDITIONAL; INCREMENTAL){}
(You had the last two backwards...)

SG-Fan, the problem with that is, the next time you create something, you lose your current thing reference. Let's say the first item you create it, and it has a thing number of 3. armory = 3 now. The next time you create something, say, that has a thing number of 5, armory now = 5. You no longer have a reference to thing number 3. Class cogs essentially just capture the messages for any templates with it listed as their cog. There's not a seperate class cog running for each thing created that has it as it's cog. The solution for this is to use a heap or static array to handle multiple objects, incrementing each time you create a new one (to go through the array appropriately).
_ _ _____________ _ _
Wolf Moon
Cast Your Spell On Me
Beware
The Woods At Night
The Wolf Has Come
2005-06-03, 11:42 AM #14
Bah, wish I hadn't made the mistake in the for loops. Yeah, you're right, it stops at 266 in Canyon Oasis. I still think it's better form to only check Things that actually exist, but oh well.

Something I'm not bothering to test at this point, because I forget how a no-thing-limit patched JK.exe decides, but what thing count does GetThingCount() return when using a patched JK.exe?

QM
2005-06-03, 12:03 PM #15
It'll still return the World Count. How it works is like this:
JK reads the JKL World Thing Count. Then it goes to allocate memory for these things. In the normal (nonpatched) JK, there's only a memory block large enough for 640 things. If you try to do more, JK can't find the memory, and gives up crashing. The patch redirects JK to try to allocate thing memory elsewhere, where there's more than that 640 sized block of open memory. The allocated amount is still however, done based on the world count. Sige would know this better, but I'm pretty sure that's how it works out.
_ _ _____________ _ _
Wolf Moon
Cast Your Spell On Me
Beware
The Woods At Night
The Wolf Has Come
2005-06-03, 10:44 PM #16
Quote:
Originally posted by LKOH_SniperWolf
SG-Fan, the problem with that is, the next time you create something, you lose your current thing reference. Let's say the first item you create it, and it has a thing number of 3. armory = 3 now. The next time you create something, say, that has a thing number of 5, armory now = 5. You no longer have a reference to thing number 3. Class cogs essentially just capture the messages for any templates with it listed as their cog. There's not a seperate class cog running for each thing created that has it as it's cog. The solution for this is to use a heap or static array to handle multiple objects, incrementing each time you create a new one (to go through the array appropriately).

I understand what your saying, but what I'm saying is that it DID work, which implies that there is a seperate cog for each object. I'll retest it to make sure I grabbed the right code, but in the end I got it to work. Incase you wanted to know, my test was spawning multiple objects with the classcog I posted; then when I activated them, they played a key but also checked if I already played it for that object (cog). At first it would only play it for the most recent object (which is the error you describe) but in the end I got it to work for the object selected.
Sam: "Sir we can't call it 'The Enterprise'"
Jack: "Why not!"

↑ Up to the top!