neato. Wonder when you'll teach us C++ mp dll basics? :D
Quote from: General BlackDragon on April 24, 2008, 12:03:45 AM
neato. Wonder when you'll teach us C++ mp dll basics? :D
C++ can be more difficult than the scriptor, but can also be more fun. Kudos if you can figure out what this does:
void instantMission::SendPKs(bool IsSetup, bool bTaunt)
{
Handle hPlayer;
if(IsSetup) { // Set things up
Vector NewPos;
for(i=0;i<MAX_TEAMS;i++) {
hPlayer=GetPlayerHandle(i);
if(hPlayer) { // FURY PK ATTACK!
if(IsAround(hndPK[i])) {
SetCurHealth(hndPK[i],GetMaxHealth(hndPK[i]));
SetCurAmmo(hndPK[i],GetMaxAmmo(hndPK[i]));
Attack(hndPK[i],hPlayer);
} else {
NewPos=GetPositionNear(EnemyRecyPos,75.0f,150.f);
hndPK[i]=BuildObject("fvsavmmpk",comp_team,NewPos);
Attack(hndPK[i],hPlayer);
PKs++;
}
}
}
if(bTaunt)
DoTaunt(TAUNTS_PK);
} else { // If PK is around, retarget to Player
for(i=0;i<MAX_TEAMS;i++) {
if(IsAround(hndPK[i])) {
hPlayer=GetPlayerHandle(i);
Attack(hndPK[i],hPlayer);
}
}
}
}(Out of curosity, but anyone know how to convert that to DLL Scriptor for the hell of it? :P)
I "know" C++, but I don't know the Scriptor.
It picks on the players in a not-nice way. AIP's can do the same thing. :wink:
y...eah -- ill stick with the scriptor *bites fist*
That looks like it has something to do with a fury attacking the player... :evil:
Don't sell yourselves short, guys.
Quote from: bigbadbogie on April 24, 2008, 01:09:10 AM
y...eah -- ill stick with the scriptor *bites fist*
you'd need an iq of 150 to understand any of that
Maybe I should Make a "Stalker Nielk1's DLL Time..."
Then again, I've only made test DLLs so far, nothing mutch else. (no time)
Well Sonic, I do not know about the scriptor, but in python that direct translation would be:
def SendPKs(IsSetup, bTaunt): # Wouldn't it be better if these two arguments were part of the class, not something to be passed in?
if IsSetup: # Set things up
for i in range(MAX_TEAMS):
hPlayer = GetPlayerHandle(i)
if hPlayer:
if self.hndPK[i]: # I have bool overloaded to do IsAlive2
self.hndPK[i].SetCurHealth(self.hndPK[i].GetmaxHealth())
self.hndPK[i].SetCurAmmo(self.hndPK[i].GetMaxAmmo())
self.hndPK[i].Attack(hPlayer)
else:
NewPos = self.EnemyRecyPos.GetPositionNear(75.0, 150.0)
self.hndPK[i] = BuildObject("fvsavmmpk", self.comp_team, NewPos)
self.hndPK[i].Attack(hPlayer)
self.PKs++
if bTaunt:
DoTaunt(TAUNTS_PK)
else: # If PK is around, retarget to Player
for i in range(MAX_TEAMS):
if self.hndPK[i]:
hPlayer = GetPlayerHandle(i)
self.hndPK[i].Attack(hPlayer) # Shouldn't you test that hPlayer exists first, or you could just inline the GetPlayerHandle call...
Although when in python I would recode it vastly differently...
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
While your on the topic of scripting i'd like to ask for some help on some code that has been giving some problems. I have a routine where the construction rig is created if he don't already exist & then i'd like to order him to build certain objects possibly at specified paths. so far the rig has ignored everything i've tried. I've been trying to get this portion to work so i can setup a new map with a second team allied to the other cpu team but it seems to get ignored. Below is the working code i have so far without the non-working code.
void instantMission::ExtraCPUTEAM()
{
m_erigCounter++;
if((m_erigCounter % (20 * m_GameTPS))==0)
{
if(!IsAround(m_erig))
{
m_erig=BuildObject("ivcons", m_SecondCompTeam, "Recycler");
SetIndependence(m_erig, low);
//AddToMessagesBox("UnitCreation: ivcons");
PrintConsoleMessage("UnitCreation: ivcons");
m_HaveRig=true;
}
Um, to keep the :-o factor down maybe you guys could take the python/C++ coding to another thread? This is "Introduction to Simple Scripting", to help new SP mission makers get started, and you might scare them off.
And Sonic's routine sent Furies after PK... wonder how he's doing? Many of the old gang have wandered off, probably due to Alzheimers... :)
Thanks...
-Av-
Sorry, we like to toss out code to see the reaction from the new guys. Issue with my code that I relized after I posted it was the Scriptor only reconizes one player, my code is multi-player. But still, it would be fun to convert it to other codes just to point out the pros and cons of each item. I still believe those wanting fast, easy and not to complex mission scripts to use the scriptor.
Oh, and the code is sending Furies named "PKs" after the players if certain events is triggered. Than every 10 seconds, the game re-runs the code to tell the Furies that are out to hunt the player. Very fun to watch your teammates bet blasted into the air and find a fury following them to the ends of the earth. >:D
Edit:
Split the topic so Avatar's topic can stay out of the :o factor.
GreenHeart: If you send me a PM with the link to your actual code (all of it) with a detailed description of what you are trying to do, I can take a look and give you my thoughts on what might be going wrong.
Thanks, I appreciate it.
I think the natural progression is like me... I started out with the Scriptor, then moved to C++. With help from the Community neither one is impossible to learn, despite much of it looking pretty scary when you're first starting out. That's the big thing, is not to get discouraged because what you're looking at is GREEK... it'll all come a bit at a time if you just keep plugging away, ask for help when needed, and don't give up...
And I agree completely, other than the "fast, easy, and not too complex" part... for SP missions I don't think you'll find anything you can't do with Scriptor simple because of the limitations of most SP missions. They usually all boil down to a few basic mission types, and the Scriptor has more than enough firepower to do pretty much anything.
MP is the 'new frontier', and I'm very interested in OM's python based scripter for that as it could open up some very cool possibilities for MP play when enough creative people learn how to use it. OTOH it looks like quite a few of us are getting comfortable with C++ and Nathan's been great at handing out DLL updates and sample code as well as advice... very cool.
-Av-
Don't forget with the scriptor, you can come up with a hybrid of C++ and Scriptor code. The source code for the scriptor's dll is available and can be tweeked. If you have functions that are common for you (say look for a pod x being built than do this..that...then this), you could have that written in C++ into the DLL Scriptor's common code. This is basicly how FE's portals are working, the code is in the base C++ code for the scriptor.
I like the scriptor's linear fashion for SP missions. One thing I noticed when writting DLLs in C++ was it was in circle fashion, not linear. Basicly, every turn the DLL executes the execute() function which in turn does things. To write an SP mission, you would have to have alot of checks in your code to say is this done, is that done, is it time yet, can I get some cofee?. With the scriptor, everything in executed in a linear fashion.
Edit: Example of what you could add to the scriptor's base addObject() function to look for a "Rose Curse" than act upon it
// Perhaps a Rose Curse?
if(strncmp(ODFName,"aprosec",7) == 0) {
RoseConstructorCurse();
AddToMessagesBox("Cursix: AIEEEEEE! Rose is casting a Curse!");
if(fRandomNum <= 0.66f) { // Curse has targeted Players...
for(i=0; i<MAX_TEAMS; i++) {
hndPlayer=GetPlayerHandle(i);
if(hndPlayer) { // We found a player
if(fRandomNum < 0.6) {
SetMaxHealth(hndPlayer,3000);
SetCurHealth(hndPlayer,1);
SetMaxAmmo(hndPlayer,100);
SetCurAmmo(hndPlayer,0);
} else { // Very LITTLE Chance of it helping you!
SetMaxHealth(hndPlayer,9000);
SetCurHealth(hndPlayer,9000);
SetMaxAmmo(hndPlayer,12000);
SetCurAmmo(hndPlayer,12000);
}
}
}
} else if(fRandomNum < 0.8f) { // Curse has targeted Recyclers...
AddHealth(enemy_recycler, 3000);
SelfDamage(recycler, 3000);
} else if(fRandomNum <= 0.81f) { // Curse has targeted Recyclers...
AddHealth(recycler, 3000);
SelfDamage(enemy_recycler, 3000);
} else if(fRandomNum < 0.85f) { // Er, what?
AddToMessagesBox("Rose: Wha? *blush*");
AddToMessagesBox("Cursix: Whew, Sonic kissed Rose, crisis averted.");
AddToMessagesBox("Sonic: Can I get some asprin now?");
AddToMessagesBox("Cursix: Sorry, all out.");
AddToMessagesBox("Sonic: DAMNIT!");
} else { // Curse has targeted MM...Oh hoot...
char MMGenderThing[12], MMNameThing[12];
if(fRandomNum2 <= 0.4f) {
strncpy(MMGenderThing,"him",sizeof(MMGenderThing));
strncpy(MMNameThing,"Maxwell",sizeof(MMNameThing));
} else if(fRandomNum2 <= 0.8f) {
strncpy(MMGenderThing,"her",sizeof(MMGenderThing));
strncpy(MMNameThing,"Wendy",sizeof(MMNameThing));
} else {
strncpy(MMGenderThing,"it",sizeof(MMGenderThing));
strncpy(MMNameThing,"MM",sizeof(MMNameThing));
}
AddToMessagesBox("Rose: That's odd...Nothing Happened.");
_snprintf(TempBuff,sizeof(TempBuff),"Reality: %s has malleted Rose for cursing %s.",
MMNameThing,MMGenderThing);
AddToMessagesBox(TempBuff);
}
hndCleanCancel=h;
bCleanCancel=true;
}
FYI, my CleanUp Function that executes a turn later:
// Use this function to do cleanup activities. This is always exected last
// in the execute function.
void instantMission::CleanUp()
{
// If a Cancel Pod was made, cleanup!
if(bCleanCancel) {
RemoveObject(hndCleanCancel);
bCleanCancel=false;
}
}
Which is to say that the Scriptor takes the C++ circle and breaks it for you, so that you can ignore the fact it loops ;)
Quote from: Sonic on April 24, 2008, 03:22:54 PM
Don't forget with the scriptor, you can come up with a hybrid of C++ and Scriptor code. The source code for the scriptor's dll is available and can be tweeked. If you have functions that are common for you (say look for a pod x being built than do this..that...then this), you could have that written in C++ into the DLL Scriptor's common code. This is basicly how FE's portals are working, the code is in the base C++ code for the scriptor.
And in my Python scriptor you can arbitrarilly load other dll's that have helper functions or what-not all you want, without needing to change the base scriptor dll and possible breaking other scripts if you are not careful and reuse it later. :P
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
I don't see how you would break other scripts. The scriptor lets you choose your 'base' DLL code. So your custom code will only be applied to the DLL you choose to compile with that code. Thus, my night light map is not affected by my custom code in my theory x/y map dll which used custom code.
Quote from: OvermindDL1 on April 24, 2008, 06:12:30 PM
And in my Python scriptor you can arbitrarilly load other dll's that have helper functions or what-not all you want, without needing to change the base scriptor dll and possible breaking other scripts if you are not careful and reuse it later. :P
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
I want to do that in C++. I was thinking about making some DLLs to do things like -anti shield towers- that monitored for any object of a classlabel (new ODF inheritance would let me fake a new class label) and then threw the appropriate things into vectors, maps, or arrays and processed them later.
ALSO, after the split my post about making a "Stalker Nielk1's DLL Time" looks odd and misplaced :-P
EDIT: I should state I am proficient in Java, but can get around many languages, which is why something obvious might not occur to me.
EDIT2:
So far I am editing existing DLLs:
(http://www.bzcomplex.com/gallery/albums/userpics/10002/thumb_bz2_0001.jpg) (http://www.bzcomplex.com/gallery/displayimage.php?album=10&pos=0)
I should note that was before I finally found the toggle for release build instead of debug build and stopped the odd debug over spawn that caused issues likes lost AI units and 1 bot meaning 10, all in the same spacial coordinate.
By that I meant if it reused that code, but some alteration was made that was not accounted for when used later on. I prefer explicit rather then implicit as coding terminology goes. :)
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
How can I program a mission DLL in C++, and have it call in other compiled mission DLL functions? Or is that imposable in C++ and I am thinking about Java's recompiled classes?
EDIT:
I'm just messing around now:
void instantMission::Execute(void)
{
ElapsedGameTime++;
player=GetPlayerHandle();
turn_counter++;
DoGenericStrategy();
DoNielk1();
}
void instantMission::DoNielk1()
{
if(n1QuakeOn)
{
if(GetCurAmmo(GetPlayerHandle()) == 0)
{
StopEarthQuake();
n1QuakeOn = false;
}else{
UpdateEarthQuake(GetCurAmmo(GetPlayerHandle()));
}
}else{
n1QuakeOn = true;
}
}
Just be sure to expose the other functions/classes/whatever from the other dll as per the normal library ways (external definition file, ccall exposing, ordinal link, etc... there are a few ways to do so), then just link it in to the dll that is going to use it, quite simple. :)
Do remember, pretty much nothing is impossible in C/C++, java is far far far more restrictive then C/C++ could ever be. :P
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
Quote from: OvermindDL1 on May 09, 2008, 05:05:58 PM
external definition file, ccall exposing, ordinal link, etc...
OK, you just threw terms at me that I don't yet begin to pretend to understand. I guess the correct question would be, how would YOU do it? I want it to be simple enough that anyone making a DLL can take my helper DLL and easily include it, then just have to send it the right info.
--
So, I would need to make a DLL from scratch, somehow include it into the BZ2 DLL, and then call its functions?
I am so used to just creating an instance of a precompiled Java class and then using it, how exactly do I go about using the DLL?
I take it that to make the DLL I just need a simple blank project set to compile as a DLL, and then the functions that are public are accessible. There might be a template in Visual Studio for DLL, I am not sure. The most I have done in C++ is make console APPS.
I'm just toying around atm, this was a fun bit of code for visual effect:
int team1color = 0;
bool team1colorup = true;
void instantMission::Execute(void)
{
...
TeamColorFun();
}
void instantMission::TeamColorFun(void)
{
if(team1colorup)
{
team1color+=5;
if(team1color>=255)
{
team1colorup = false;
}
}else{
team1color-=5;
if(team1color<=0)
{
team1colorup = true;
}
}
SetTeamColor(1, team1color, 255-team1color, 0);
}
Export functions as normal, set them to extern "C" calling convention (if you start up a new shared DLL project in VisualStudio you can see examples of that) and declspec them to be visible (also shown in a new shared dll project). To import into another just loadlibrary (look that up in the VisualStudio help, good example there) and load the function by name from that and assign it to a function pointer (also demonstrated on that msdn page). That would be the easiest way to do it if you are only dealing with a few and you do not want to hard link those files together (and it lets you see the return value of loadlibrary to see if it exists, if it doesn't then you can turn off certain features that it would have given, else if it returns a good value then the library is available and you can have special stuff happen).
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
... and you expect us numnuts to be able to do c++?? :-D
Everything I mentioned is C, not C++ (well, the extern "C" is C++, telling it to make the calling convention of that function/variable/etc... C style, thus it is not there for C...). :P
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
Quote from: bigbadbogie on May 11, 2008, 03:39:01 AM
... and you expect us numnuts to be able to do c++?? :-D
i quote myself
Well Nielk1 was the one who asked, as such I was responding to his question. :P
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
Ah, found the add object function... Should have looked at the interface of the DLLBase class I was inheriting in the first place. DOIY! This solves a lot of headaches.
This is kinda fun now, I love working in looped logic like this.
Is C different then C#? Just though I'd ask for clarification. I'm just getting started with C++, but the Dark Basic GDK looks very interesting for gaming applications. I've also come across a few code snippets that I'd like to experiment to help me get a clue about how C++ logically works. I'm also hoping that C++ eventually reveals some of it's cool little secrets. I guess it's more or less having a lot of patients and then connecting the dots as they reveal themselves...BNG
Very different.
C is a newer B (Yes there was such a language, was made for system management, C was made for making systems :P)
C++ is C with classes and templates (and a few more things now, but it started as just C with Classes).
C# is Microsoft's version of Java, quite literally that is what it was designed as. It is far far far more like Java then it is C++.
Just remember this about C/C++, there is no 'higher-level' (yes, technically C/C++ are higher level languages) language that maps as close to assembly, because of that it is fast, because of that you can make it do just about everything, things no other language could really ever hope to do, but because of that it can be very ugly at times. It is one of those languages that you may not be able to do something in it as fast as you could in Java or Python or what-not, but once you learn it, learn how it works, and *especially* get a good library built of of things you commonly use, then you can program circles around most other languages, probably the only language that could beat it in customizability would be Lisp. Quite literally though, because of the libraries I use I can generally program near anything in less code then Python (and especially Java, I think anyone who thinks coding in Java is shorter then C++ is an utter retard, I have yet to see any real code made in Java that is shorter then it's C++ counterpart), takes about the same time to actually program, and runs orders of magnitude faster. It is one of those languages that once you learn/master you look at near every other language in disgust, only using them for a singular feature or two that may make a certain quick script or app quicker to make... and yet I still learn near every language I can, learning is too addictive. :P
Quick example of its customizability. One of the main things about other languages like Java, Python, C#, etc... is memory management, most people say that C++ has none; that is true, to an extent, it is more proper to say that it is not 'built-in (and even that is false, the next version of the C++ standard supports a pluggable memory management model, at least I think it was accepted... proposed anyway), but if you want a simple memory management model, shared_ptr<SomeClass> fixes that, or if you want something a little bigger then plug in the Hans Boehm Collector to do most of it for you. So on and so forth...
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
Quote from: OvermindDL1 on May 13, 2008, 12:56:35 AM
Very different.
C is a newer B (Yes there was such a language, was made for system management, C was made for making systems :P)
C++ is C with classes and templates (and a few more things now, but it started as just C with Classes).
C# is Microsoft's version of Java, quite literally that is what it was designed as. It is far far far more like Java then it is C++.
What is J#? I noticed J# seemed to be Microsoft's Java.
Two things I am still getting used too: pointers and working old code. Java had neither.
J# Is more of a scripting language which I thought they killed off long ago.
And Java is nothing *but* pointers, you work with them all the time, you just cannot get direct access to the pointer value itself, one of the ways it is restrictive. C++'s 'pointer magic' may be one of the harder things to read in C++, but it makes for *very* fast code, and as such is generally only used for tight loops where that tight optimization can give huge benefits.
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
one thing i want to know - is c++ anything like the dll scriptor?? not the same commands obviously but is it written in the same context??
The command names are similar, but the style is not, BS-er's scriptor is more like Assembler, my Python BZ2 scriptor is more like C++ (but with curly-brackets?).
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
damn - oh well - i am good at the scriptor and i thought there might be 1 chance in a million that c++ might be anything like it
there goes my future as a programmer :cry:
I think in loops, so, this works best for me. The scriptor (BS-er's) seems so linear.
I'm learning, and I'm having fun.
Quote from: bigbadbogie on May 13, 2008, 10:05:47 AM
there goes my future as a programmer :cry:
http://www.cprogramming.com (http://www.cprogramming.com)
This may help you understand c/c++, but you have to be willing to read(a lot) :D
you first need a c/c++ compiler (visual basic, dev-C++ etc.)
Have fun :P
RR
*hanging myself with a snake*
Python perhaps then? :P
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
hehe -- good call
I was trying to make something rather dynamic that would set the hover height of each craft to a value randomly between the max and min set in the ODF. I found that to pull this off I would need to dynamically generate a new odf for each new unit.
I have decided to instead make the ODF list a series of alternate ODF names that the DLL will load randomly. This way I can cause variation in more than just hover height, but in simulated pilot skill by changing other physics properties on the ships. The cause for the original idea was for an easy to use (ODF maker end) system for populating a space map with ships in 3 dimensions rather than 2.
I will be sure to post the new code when I am done since it may be useful to many.
Quote from: Sonic on April 24, 2008, 03:22:54 PM
I like the scriptor's linear fashion for SP missions.
That was an absolute necessity given that it had to be simple enough for a nonprogrammer to use and use well. It was quite satisfying to see the first gang of newbies tinker with it for awhile and came up with some nice missions in a small amount of time.
Even though I am a programmer I liked that approach for single player as well, and would much rather use the Scriptor than make a C++ DLL for SP. Its limitations never really stopped me from making the mission I wanted to make.
While on the subject, I don't quite know how things are with the latest BZ2. Has the Scriptor lost compatability? Are there new features that the Scriptor doesn't support? If so I'll try to find some time to do an update, but I'll need help from the regulars to guide me.
Yes, just check the bottom of scriptutils.h for all the new functions and implement them and that should be good for everyone. :)
All functions that were previously in the scriptutils.h file are all still binary compatable, and I made a header that lets you dynamically link in all things, old and new, in the style that yours was, so if you do not want to add things manually, I can just give you my updated file, save you a lot of time.
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
OK here is some code for you all:
For the Header:
#include <vector>
public:
void queueLoopAnim(Handle h);
void startLoopAnim();
std::vector<Handle> waitingToLoopObjects;
std::vector<char*> waitingToLoopNames;
std::vector<float> waitingToLoopTime;
For CPP:
void instantMission::queueLoopAnim(Handle h)
{
char ODFName[64];
if(GetObjInfo(h, Get_CFG, ODFName)) //is a game object check (double duty to ODFName)
{
GetObjInfo(h, Get_ODF, ODFName);
char *loopAnimationName = new char[64];
float loopAnimationDelay;
OpenODF(ODFName);
GetODFString(ODFName, "GameObjectClass", "loopAnimationName", 64/*0*/, loopAnimationName);
GetODFFloat(ODFName, "GameObjectClass", "loopAnimationDelay", &loopAnimationDelay);
CloseODF(ODFName);
loopAnimationDelay *= m_GameTPS;
if((loopAnimationName != 0) & (loopAnimationDelay > 0))// not null
{
waitingToLoopObjects.push_back(h);
waitingToLoopNames.push_back(loopAnimationName);
waitingToLoopTime.push_back(loopAnimationDelay);
}
}
}
void instantMission::startLoopAnim()
{
for(int x = 0; x < waitingToLoopObjects.size(); x++)
{
--waitingToLoopTime[x];
if(waitingToLoopTime[x] < 0)
{
SetAnimation(waitingToLoopObjects[x],waitingToLoopNames[x], 0);
StartAnimation(waitingToLoopObjects[x]);
for(int y = x; y < (waitingToLoopObjects.size() - 1); y++)
{
waitingToLoopObjects[y] = waitingToLoopObjects[y + 1];
waitingToLoopNames[y] = waitingToLoopNames[y + 1];
waitingToLoopTime[y] = waitingToLoopTime[y + 1];
}
x--;
waitingToLoopObjects.resize(waitingToLoopObjects.size()- 1);
waitingToLoopNames.resize( waitingToLoopNames.size() - 1);
waitingToLoopTime.resize( waitingToLoopTime.size() - 1);
}
}
}
void instantMission::AddObject(Handle h)
{
...
queueLoopAnim(h);
}
void instantMission::Execute(void)
{
...
startLoopAnim();
}
This code is for pb4, so you know. It reads the custom lines
loopAnimationName = "" //name of animation for loop play
loopAnimationDelay = 0 //home long to wait to activate animation (good to prevent interference with deploy animation)
what does that do?
Makes a pre-made animation of a msh file play and loop repeatedly (something that like the Ornithopter in DuneZone/DuneCommand used to always be flapping) from the looks of it, although there is the occasional erroneous bit, like TempMsgStr in one of thoes and so forth. :)
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
:-o all that just to do this?:
(in the scriptor)
SetAnimation,Object,"anim",0
StartAnimation,Object
@OvermindDL1
I though I got rid of all my debugging output lines. Fixing.
@BBB
In English, it does a lot more BBB.
It automatically waits for the amount of time set in the ODF, and then starts the animation listed in the ODF.
If you look, only 2 lines actually choose the animation and start it.
The cause for the wait time is to allow for deploy animations to not get in the way.
It is useful code, and it is bulky because it is dynamic and reads the ODF for information.
Yea, the posted C++ can time it properly, loop it properly, etc...
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
truthfully I got some old BZC DLL source code from GBD that handled animated the REC, MUF, and SLF and adapted it to use vectors for dynamic size and scanning ODFs so any object can use it.
Also, for note, it is not that hard to do 'linear' programming in C++ either. An example (this is valid code and will compile and run as expected if I compiled it on my system as is, assuming I did not misspell anything):
This is the scheduler, you can probably ignore it, just normal write-once boilerplate code...
// This class is just a scheduler to keep things synced to the BZ2 world, you could ignore it...
typedef __coroutine__<void()> job_type;
typedef job_type::__self__ self_type;
class Scheduler
{
public:
typedef std::multimap<std::string,std::string>::const_iterator Iterator;
void newJob(function<void(self_type&)> fn)
{
add(job_type(fn));
}
void add(job_type job)
{
m_Map.insert(std::make_pair(m_CurTime+1, job));
}
void sleep(self_type &self, unsigned int ticks=0)
{
m_Map.insert(std::make_pair(m_CurTime+ticks+1, m_Current));
m_Current = job_type();
sleep.__yield__();
}
void run()
{
const std::pair<Iterator,Iterator> now = questions.equal_range(++m_CurTime);
for (Iterator i=now.first; i!=now.second; ++i)
{
m_Current = i->second;
m_Current()(nothrow);
if(m_Current) add(m_Current);
}
m_Map.erase(now.first, now.second);
}
Scheduler(unsigned int startTime=0)
:m_CurTime(startTime)
{
}
private:
multimap<unsigned int, job_type> m_Map;
unsigned int m_CurTime;
job_type m_Current;
};
// Just a convenience function, ignore me too
__forceinline void sleep(self_type &self, unsigned int ticks=0)
{
scheduler->sleep(self, ticks);
}
Here is the interesting code, yes, valid C++ and this would compile for me as is (again, assuming I spelled everything correctly):
// Create the scheduler somewhere, like in the missions Init and Load functions...
if(scheduler) delete scheduler;
scheduler = new Scheduler(m_ElapsedGameTime);
// Let's create a couple jobs
void watchForUnitDeathOnceASecondAndDoEndgameIfDead(self_type &self, Handle watchedUnit)
{
sleep(self); // Just to give time for the unit to be fully created by BZ2
while(IsAlive2(watchedUnit)
sleep(self, m_GameTPS); // m_GameTPS is one second worth of ticks
// If I ever reach here, the unit died
NoteGameoverWithCustomMessage("Vital unit died");
DoGameover(5*m_GameTPS);
}
void someBasicScriptedActions(self_type &self)
{
Handle superUnit = BuildObject("someSuperUnit", 12, "superUnitSpawnPath")
sleep(self); // Wait a tick to let BZ2 fully spawn it in
Attack(superUnit, m_Player, 9999); // May it follow and attack the player to the end of its days...
while(IsAlive2(superUnit))
sleep(self, m_GameTPS);
// The end of its days has occurred
AddToMessageBox("Super unit has been destroyed");
// So on and so forth here, use sleep to sleep some amount of ticks, nice linear programming style now
}
// And use it as such to create some new jobs somewhere:
scheduler->newJob(bind(watchForUnitDeathOnceASecondAndDoEndgameIfDead, _1, m_PlayerRecycler));
scheduler->newJob(bind(watchForUnitDeathOnceASecondAndDoEndgameIfDead, _1, m_SomeMissionCriticalUnit));
scheduler->newJob(bind(watchForUnitDeathOnceASecondAndDoEndgameIfDead, _1, m_SomeOtherMissionCriticalUnit));
scheduler->newJob(someBasicScriptedActions);
// And be sure to run it once a tick, in the Update/Execute function for example:
scheduler->run();
Nielk1's previous example, if ported to this style, would be a lot more simple as well, allow me to just copy/paste while making the necessary modifications:
For the Header:
public:
void fullLoopAnim(self_type &self, Handle h);
For CPP:
void instantMission::fullLoopAnim(self_type &self, Handle h)
{
char ODFName[64];
if(GetObjInfo(h, Get_CFG, ODFName)) //is a game object check (double duty to ODFName)
{
GetObjInfo(h, Get_ODF, ODFName);
char loopAnimationName[64];
float loopAnimationDelay;
OpenODF(ODFName);
GetODFString(ODFName, "GameObjectClass", "loopAnimationName", 64, loopAnimationName);
GetODFFloat(ODFName, "GameObjectClass", "loopAnimationDelay", &loopAnimationDelay);
CloseODF(ODFName);
loopAnimationDelay *= m_GameTPS;
if((loopAnimationName[0]!=0) & (loopAnimationDelay>0))// not null
{
while(IsAlive2(h))
{
SetAnimation(h,loopAnimationName, 0);
StartAnimation(h);
sleep(loopAnimationDelay);
}
}
}
}
void instantMission::AddObject(Handle h)
{
...
scheduler->newJob(bind(instantMission::fullLoopAnim, this, _1, h));
}
Also, Nielk1, be sure to clean your memory, you are leaking horribly in your example, I fixed it mine. Also fixed your string test that would *always* pass. Yours also does not check to see if the unit died, because if they die then you start sending commands to an invalid Handle. :P
Use your imagination for further examples, as you can see it is quite possible in C++ to do these things as well. Remember what I have said, C++ is one of those language where you can do anything, you may not always be able to do it 'pretty', but you can do it. :)
EDIT: Also, for the keenly aware around here, I have been experimenting with this style programming in C++ for many years now, posting up examples that are in the now disappeared programming board (as such I can not link to some of my old examples and old methods of doing things so you can see just how much better it is now, gotta love when links are killed, breaks pages...), my libraries just keep getting more and more refined over time.
I also have something that takes source code and properly converts it to html with all the nice color coding I usually use, need to change it to use bbcode tags sometime since I cannot post html here...
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
Would you mind helping with the leaking memory? I;m used to JAVA cleaning everything up for me.
The only think I can think of is where I said
char *loopAnimationName = new char[64];
I take it I need to delete that at the point where I remove it from the vector. I HATE VECTORS NOW. I cant believe I cant get a damn delete or remove function that asks for an index instead of a god forsaken iterator. The damn iterators beak the instant the thing they are iterating changes anyway. The change in index of so many elements is very wasteful, but I can't think of a better way.
EDIT: OK, I kinda cheated a bit and let the compiler tell me things I should have already known.
I tried:
delete waitingToLoopObjects[waitingToLoopObjects.size()- 1];
delete waitingToLoopNames[waitingToLoopNames.size() - 1];
delete waitingToLoopTime[waitingToLoopTime.size() - 1];
and since the first and last aren't pointers it nicely told me so and I commented them out. Does that stop the mem leak? Here is the new code so far:
void instantMission::queueLoopAnim(Handle h)
{
char ODFName[64];
if(GetObjInfo(h, Get_CFG, ODFName)) //is a game object check (double duty to ODFName)
{
GetObjInfo(h, Get_ODF, ODFName);
char *loopAnimationName = new char[64];
float loopAnimationDelay;
OpenODF(ODFName);
GetODFString(ODFName, "GameObjectClass", "loopAnimationName", 64/*0*/, loopAnimationName);
GetODFFloat(ODFName, "GameObjectClass", "loopAnimationDelay", &loopAnimationDelay);
CloseODF(ODFName);
loopAnimationDelay *= m_GameTPS;
if((loopAnimationName[0] != 0) & (loopAnimationDelay > 0))// not null
{
waitingToLoopObjects.push_back(h);
waitingToLoopNames.push_back(loopAnimationName);
waitingToLoopTime.push_back(loopAnimationDelay);
}
}
}
void instantMission::startLoopAnim()
{
for(int x = 0; x < waitingToLoopObjects.size(); x++)
{
if(IsAlive2(waitingToLoopObjects[x]))
{
--waitingToLoopTime[x];
if(waitingToLoopTime[x] < 0)
{
SetAnimation(waitingToLoopObjects[x],waitingToLoopNames[x], 0);
StartAnimation(waitingToLoopObjects[x]);
for(int y = x; y < (waitingToLoopObjects.size() - 1); y++)
{
waitingToLoopObjects[y] = waitingToLoopObjects[y + 1];
waitingToLoopNames[y] = waitingToLoopNames[y + 1];
waitingToLoopTime[y] = waitingToLoopTime[y + 1];
}
x--;
delete waitingToLoopNames[ waitingToLoopNames.size() - 1]; //delete NEW allocation
waitingToLoopObjects.resize(waitingToLoopObjects.size()- 1);
waitingToLoopNames.resize( waitingToLoopNames.size() - 1);
waitingToLoopTime.resize( waitingToLoopTime.size() - 1);
}
}else{ //oh my, a dead thing is in here, dispose of it
for(int y = x; y < (waitingToLoopObjects.size() - 1); y++)
{
waitingToLoopObjects[y] = waitingToLoopObjects[y + 1];
waitingToLoopNames[y] = waitingToLoopNames[y + 1];
waitingToLoopTime[y] = waitingToLoopTime[y + 1];
}
x--;
delete waitingToLoopNames[ waitingToLoopNames.size() - 1]; //delete NEW allocation
waitingToLoopObjects.resize(waitingToLoopObjects.size()- 1);
waitingToLoopNames.resize( waitingToLoopNames.size() - 1);
waitingToLoopTime.resize( waitingToLoopTime.size() - 1);
}
}
}
I like loops, and I think in them (probably something to do with OCD or some other undiagnosed illness).
I don't quite understand your code. You call a sleep command, is that asynchronous or does it stall the whole DLL (something I am afraid of doing)? The way I have mine coded it waits for the set amount of time before executing the animation, but your looks like it sets the animation, starts it, then waits? The purpose of the wait was to allow deploy animations to finish, so even setting the animation before it is done would mess it up wouldn't it?
EDIT2:
OK, new issue. I made the code have a separate CPP and H from the main code, so it has to be imported, its constructor called, and its functions called. That all works. Now, the issue is it uses the TPS variable to calculate the time, and, I do not know how to get it over to the new class. I am thinking about adding "m_LoopAnimator.shareTPS(m_GameTPS);", but I am unsure of where to put it; does it go in load, the constructor, somewhere else, or everywhere?
Have not read the whole post, can later, but for fixing the memory, you are correct about where you are leaking. Do not new the char array, rather just keep it a local, memset it fully empty each time before use, and stuff it in an std::string inside the vector, it will copy it and manage the memory properly (not the most efficient method, but very easy). The reason you cannot delete an index is because it is a vector, a vector is an array, an array is a contiguous group of memory, you cannot just delete something inside of it, that is why when you do it using a vector it makes a copy of the entire array, sans the section you chose not to include. If you want to be able to manage it easier then you might think of using std::list instead, it is less efficient then an array, but easier to use in those circumstances. Might also consider an std::map, better then the list in efficiency, but better then a vector in usage capability.
As for my version of your code, look a little higher, I am not using the standard sleep, I am using my own function called sleep (different signature), it only sleeps the little job, not the whole dll, not BZ2, etc... It sleeps like you sleep in my Python Scriptor, you create little different jobs (coroutines is more accurate, or in the case of my library, fibers) and you can pause in them, change between them without needing to return, so on and so forth, kinda like threads but vastly more lightweight and no concurrency issues since it is not actually pre-emptive. Fibers are nice, they have a bit of a bug so they are not too usable for, say, creating 10000 of them (the bug is that even though you tell it to create it with a stack of say 2k, it still creates a stack the size of your program stack, so it ends up being a good 2megs or so each regardless, it has been a bug in Windows since Win95 sad enough, still not even fixed in Vista I think, which is odd because .NET2.0 makes heavy use of Win32 Fibers), but for a few hundred or so then they are perfect.
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
Perhaps a list of structs containing the 3 variables. I wonder if that would work better. The TPS thing is really bothering me, and I would prefer to use the stop DLL format for now. When pb4 is done, is there any change of you getting the DLLs ready and out that use your system of threading instead? You have to understand my C++ knowledge is still pretty primitive.
Fiber stuff is pretty well documented, I have a few of my own ways of doing it as well, but you would not like them (faster, more efficient 'coroutines', but a lot of code noise, remember what I said about being able to do anything in C++, just not always 'pretty' :) ).
I could probably whip up a C++ library for BZ2 to do fiber/job/coroutine stuff, but a prerequisite (probably the only one) is to have Boost (http://www.boost.org/) installed properly. I should only need the function, bind, and so forth stuff in it, just header things, so compiling it would not be necessary. I could always just include the parts I need though, just depends on if I want to do the work or not.
But yes, make all the information you store into a struct or tuple, example:
// Using a struct:
struct waitingToLoopStruct
{
Handle h;
string name;
unsigned int delay;
}
// and define your list/array like this:
vector<waitingToLoopStruct> waitingToLoop;
// Or by using a tuple (note, this requires either the latest TR1 or later headers of the newest C++ standard, or boost installed, either work
vector<tuple<Handle, string, unsigned int> > waitingToLoop;
Also, "stop DLL format" is what?
Another also, the above example did not use threading, it used coroutines they are not anywhere near the same thing. :)
EDIT:As for the TPS, just pass it in as a constructor parameter to your class.
Example:
class myClass
{
unsigned int m_I;
public:
myClass(unsigned int i)
:m_I(i)
{}
}
// When you need to make the above class, construct it as:
myClass myClassInstance(42);
If you really want it default constructable then just set a variable in the class directly, or call a member function to set an internal variable, or give a link back to the parent class, or... or... or... Lot's of choices. :)
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
Opps, meant "Stock DLL Format"
I will get my code working again (the TPS), post the code, then move on to getting it to use the better list/struct.
I have a lot of trouble with iterators. Should I still try to use one or can I directly access the nodes in a linked list?
EDIT: OK, question because of how I moved to C++ from Java. What is 'unsigned'?
Evil double post...
New code:
LoopAnimator.h
//To use, place in header:
//#include "..\Shared\LoopAnimator.h"
//private:
// LoopAnimator m_LoopAnimator;
//
//Place the following in the end of the constructor
//m_LoopAnimator.shareTPS(m_GameTPS)
//in execute call:
//m_LoopAnimator.execute()
//in addObject call:
//m_LoopAnimator.addObject(h)
//and so on
#ifndef _LoopAnimator_
#define _LoopAnimator_
# pragma once
#include "..\..\source\fun3d\ScriptUtils.h"
#include <vector>
class LoopAnimator {
unsigned int m_GameTPS; // How many turns per second there are (10, 15, 20, or 30)
int i_first,
i_last;
Handle h_first,
h_last;
bool b_first,
b_last;
float f_first,
f_last;
std::vector<Handle> waitingToLoopObjects;
std::vector<char*> waitingToLoopNames;
std::vector<float> waitingToLoopTime;
std::vector<bool> waitingToLoopMode;
bool *b_array;
int b_count;
float *f_array;
int f_count;
int *h_array;
int h_count;
int *i_array;
int i_count;
public:
LoopAnimator(void);
void shareTPS(int inTPS);
virtual bool Save(bool missionSave);
virtual bool Load(bool missionSave);
virtual bool PostLoad(bool missionSave);
virtual void AddObject(Handle h);
virtual void DeleteObject(Handle h);
virtual void Execute(void);
};
#endif
LoopAnimator.cpp
#include "LoopAnimator.h"
#include <string.h>
LoopAnimator::LoopAnimator(void)
{
b_count = &b_last - &b_first - 1;
b_array = &b_first + 1;
f_count = &f_last - &f_first - 1;
f_array = &f_first + 1;
h_count = &h_last - &h_first - 1;
h_array = &h_first + 1;
i_count = &i_last - &i_first - 1;
i_array = &i_first + 1;
}
void LoopAnimator::shareTPS(int inTPS)
{
m_GameTPS = inTPS;
}
bool LoopAnimator::Load(bool missionSave)
{
if (missionSave) {
int i;
// init bools
if((b_array) && (b_count))
for (i = 0; i < b_count; i++)
b_array[i] = false;
// init floats
if((f_array) && (f_count))
for (i = 0; i < f_count; i++)
f_array[i] = 0.0f;
// init handles
if((h_array) && (h_count))
for (i = 0; i < h_count; i++)
h_array[i] = 0;
// init ints
if((i_array) && (i_count))
for (i = 0; i < i_count; i++)
i_array[i] = 0;
return true;
}
bool ret = true;
// bools
if (b_array != NULL)
Read(b_array, b_count);
// floats
if (f_array != NULL)
Read(f_array, f_count);
// Handles
if (h_array != NULL)
Read(h_array, h_count);
// ints
if (i_array != NULL)
Read(i_array, i_count);
return ret;
}
bool LoopAnimator::PostLoad(bool missionSave)
{
if (missionSave)
return true;
bool ret = true;
ConvertHandles(h_array, h_count);
return ret;
}
bool LoopAnimator::Save(bool missionSave)
{
if (missionSave)
return true;
bool ret = true;
// bools
if (b_array != NULL)
Write(b_array, b_count);
// floats
if (f_array != NULL)
Write(f_array, f_count);
// Handles
if (h_array != NULL)
Write(h_array, h_count);
// ints
if (i_array != NULL)
Write(i_array, i_count);
return ret;
}
void LoopAnimator::AddObject(Handle h)
{
// Start Queue Loop Animation
char ODFName[64];
if(GetObjInfo(h, Get_CFG, ODFName)) //is a game object check (double duty to ODFName)
{
GetObjInfo(h, Get_ODF, ODFName);
char *loopAnimationName = new char[64];
float loopAnimationDelay;
bool loopAnimationType;
OpenODF(ODFName);
GetODFString(ODFName, "GameObjectClass", "loopAnimationName", 64, loopAnimationName);
GetODFFloat(ODFName, "GameObjectClass", "loopAnimationDelay", &loopAnimationDelay);
GetODFBool(ODFName, "GameObjectClass", "loopAnimationType", &loopAnimationType);
CloseODF(ODFName);
loopAnimationDelay *= m_GameTPS;
if((loopAnimationType != 0)&&(loopAnimationType != 1)){loopAnimationType = 0;}
if((loopAnimationName[0] != 0) & (loopAnimationDelay > 0))// not null
{
waitingToLoopObjects.push_back(h);
waitingToLoopNames.push_back(loopAnimationName);
waitingToLoopTime.push_back(loopAnimationDelay);
waitingToLoopMode.push_back(loopAnimationType);
}
}
// End Queue Loop Animation
}
void LoopAnimator::DeleteObject(Handle h)
{
}
void LoopAnimator::Execute()
{
// Start Loop Animation
for(int x = 0; x < waitingToLoopObjects.size(); x++)
{
if(IsAlive2(waitingToLoopObjects[x]))
{
--waitingToLoopTime[x];
if(waitingToLoopTime[x] < 0)
{
SetAnimation(waitingToLoopObjects[x],waitingToLoopNames[x],waitingToLoopMode[x]);
StartAnimation(waitingToLoopObjects[x]);
for(int y = x; y < (waitingToLoopObjects.size() - 1); y++)
{
waitingToLoopObjects[y] = waitingToLoopObjects[y + 1];
waitingToLoopNames[y] = waitingToLoopNames[y + 1];
waitingToLoopTime[y] = waitingToLoopTime[y + 1];
waitingToLoopMode[y] = waitingToLoopMode[y + 1];
}
x--;
delete waitingToLoopNames[ waitingToLoopNames.size() - 1]; //delete NEW allocation
waitingToLoopObjects.resize(waitingToLoopObjects.size()- 1);
waitingToLoopNames.resize( waitingToLoopNames.size() - 1);
waitingToLoopTime.resize( waitingToLoopTime.size() - 1);
waitingToLoopMode.resize( waitingToLoopMode.size() - 1);
}
}else{ //oh my, a dead thing is in here, dispose of it
for(int y = x; y < (waitingToLoopObjects.size() - 1); y++)
{
waitingToLoopObjects[y] = waitingToLoopObjects[y + 1];
waitingToLoopNames[y] = waitingToLoopNames[y + 1];
waitingToLoopTime[y] = waitingToLoopTime[y + 1];
waitingToLoopMode[y] = waitingToLoopMode[y + 1];
}
x--;
delete waitingToLoopNames[ waitingToLoopNames.size() - 1]; //delete NEW allocation
waitingToLoopObjects.resize(waitingToLoopObjects.size()- 1);
waitingToLoopNames.resize( waitingToLoopNames.size() - 1);
waitingToLoopTime.resize( waitingToLoopTime.size() - 1);
waitingToLoopMode.resize( waitingToLoopMode.size() - 1);
}
}
// End Loop Animation
}
Definitely use iterators. Also, Java uses iterators pretty heavily as well so you should know how to use them, the usage is near identical.
And an unsigned int is exactly that, it is an int with no sign (no negative, only positive), good to do if your number is not supposed to ever be negative. :)
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
Now I am having new issues:
LoopAnimator.h
//To use, place in header:
//#include "..\Shared\LoopAnimator.h"
//private:
// LoopAnimator m_LoopAnimator;
//
//Place the following in the end of the constructor
//m_LoopAnimator.shareTPS(m_GameTPS)
//in execute call:
//m_LoopAnimator.execute()
//in addObject call:
//m_LoopAnimator.addObject(h)
//and so on
#ifndef _LoopAnimator_
#define _LoopAnimator_
# pragma once
#include "..\..\source\fun3d\ScriptUtils.h"
#include <vector>
#include <list>
class LoopAnimator {
unsigned int m_GameTPS; // How many turns per second there are (10, 15, 20, or 30)
int i_first,
i_last;
Handle h_first,
h_last;
bool b_first,
b_last;
float f_first,
f_last;
//std::vector<Handle> waitingToLoopObjects;
//std::vector<char*> waitingToLoopNames;
//std::vector<float> waitingToLoopTime;
//std::vector<bool> waitingToLoopMode;
struct loopQueue {
Handle loopObject;
std::string *loopName;
float loopTime;
bool loopMode;
};
std::list<loopQueue> waitingLoops;
bool *b_array;
int b_count;
float *f_array;
int f_count;
int *h_array;
int h_count;
int *i_array;
int i_count;
public:
LoopAnimator(void);
void shareTPS(int inTPS);
virtual bool Save(bool missionSave);
virtual bool Load(bool missionSave);
virtual bool PostLoad(bool missionSave);
virtual void AddObject(Handle h);
virtual void DeleteObject(Handle h);
virtual void Execute(void);
};
#endif
LoopAnimator.cpp
#include "LoopAnimator.h"
#include <string.h>
#include <list> //tmp
LoopAnimator::LoopAnimator(void)
{
b_count = &b_last - &b_first - 1;
b_array = &b_first + 1;
f_count = &f_last - &f_first - 1;
f_array = &f_first + 1;
h_count = &h_last - &h_first - 1;
h_array = &h_first + 1;
i_count = &i_last - &i_first - 1;
i_array = &i_first + 1;
}
void LoopAnimator::shareTPS(int inTPS)
{
m_GameTPS = inTPS;
}
bool LoopAnimator::Load(bool missionSave)
{
if (missionSave) {
int i;
// init bools
if((b_array) && (b_count))
for (i = 0; i < b_count; i++)
b_array[i] = false;
// init floats
if((f_array) && (f_count))
for (i = 0; i < f_count; i++)
f_array[i] = 0.0f;
// init handles
if((h_array) && (h_count))
for (i = 0; i < h_count; i++)
h_array[i] = 0;
// init ints
if((i_array) && (i_count))
for (i = 0; i < i_count; i++)
i_array[i] = 0;
return true;
}
bool ret = true;
// bools
if (b_array != NULL)
Read(b_array, b_count);
// floats
if (f_array != NULL)
Read(f_array, f_count);
// Handles
if (h_array != NULL)
Read(h_array, h_count);
// ints
if (i_array != NULL)
Read(i_array, i_count);
return ret;
}
bool LoopAnimator::PostLoad(bool missionSave)
{
if (missionSave)
return true;
bool ret = true;
ConvertHandles(h_array, h_count);
return ret;
}
bool LoopAnimator::Save(bool missionSave)
{
if (missionSave)
return true;
bool ret = true;
// bools
if (b_array != NULL)
Write(b_array, b_count);
// floats
if (f_array != NULL)
Write(f_array, f_count);
// Handles
if (h_array != NULL)
Write(h_array, h_count);
// ints
if (i_array != NULL)
Write(i_array, i_count);
return ret;
}
void LoopAnimator::AddObject(Handle h)
{
// Start Queue Loop Animation
char ODFName[64];
if(GetObjInfo(h, Get_CFG, ODFName)) //is a game object check (double duty to ODFName)
{
GetObjInfo(h, Get_ODF, ODFName);
//char *loopAnimationName = new char[64];
//float loopAnimationDelay;
//bool loopAnimationType;
loopQueue tmpHolder;
tmpHolder.loopObject = h;
OpenODF(ODFName);
GetODFString(ODFName, "GameObjectClass", "loopAnimationName", 64, tmpHolder.loopName);
GetODFFloat(ODFName, "GameObjectClass", "loopAnimationDelay", &tmpHolder.loopTime);
GetODFBool(ODFName, "GameObjectClass", "loopAnimationType", &tmpHolder.loopMode);
CloseODF(ODFName);
tmpHolder.loopTime *= m_GameTPS;
if((!(tmpHolder.loopName.empty())) && (tmpHolder.loopTime >= 0))// not null
{
waitingLoops.push_back(tmpHolder);
}
}
// End Queue Loop Animation
}
void LoopAnimator::DeleteObject(Handle h)
{
}
void LoopAnimator::Execute()
{
// Start Loop Animation
std::list<loopQueue>::iterator iter1 = waitingLoops.begin();
while( iter1 != waitingLoops.end() )
{
if(IsAlive2(iter1->loopObject))
{
--(iter1->loopTime);
if((iter1->loopTime) < 0)
{
SetAnimation(iter1->loopObject,iter1->loopName,iter1->loopMode);
StartAnimation(iter1->loopObject);
waitingLoops.remove(iter1);
}
}else{
waitingLoops.remove(iter1);
}
iter1++;
}
// End Loop Animation
}
Errors (I'm sure some early ones spawn later ones):
Error 1 error C2664: 'GetODFString' : cannot convert parameter 5 from 'std::string *' to 'char *' d:\Battlezone\DLLs\BZ2_1.3pb4rc1_DLLSource\Missions\Shared\LoopAnimator.cpp 125
Error 2 error C2228: left of '.empty' must have class/struct/union d:\Battlezone\DLLs\BZ2_1.3pb4rc1_DLLSource\Missions\Shared\LoopAnimator.cpp 130
Error 3 error C2664: 'SetAnimation' : cannot convert parameter 2 from 'std::string *' to 'Name' d:\Battlezone\DLLs\BZ2_1.3pb4rc1_DLLSource\Missions\Shared\LoopAnimator.cpp 154
Error 4 error C2664: 'std::list<_Ty>::remove' : cannot convert parameter 1 from 'std::list<_Ty>::_Iterator<_Secure_validation>' to 'const LoopAnimator::loopQueue &' d:\Battlezone\DLLs\BZ2_1.3pb4rc1_DLLSource\Missions\Shared\LoopAnimator.cpp 156
Error 5 error C2664: 'std::list<_Ty>::remove' : cannot convert parameter 1 from 'std::list<_Ty>::_Iterator<_Secure_validation>' to 'const LoopAnimator::loopQueue &' d:\Battlezone\DLLs\BZ2_1.3pb4rc1_DLLSource\Missions\Shared\LoopAnimator.cpp 159
The GetODFString last param requires a char*, which an std:string cannot be cast to, reason is that it will modify the string and std::string's are immutable. So do something like "char tempChar[65];" (be sure to memset it all to zero first) and after the call to GetODFString where tempChar is full of data, then just stuff the tempChar data into your string like "tmpHolder.loopName = tempChar".
Also, in your structure, your std::string should *NOT* be a pointer (change std::string *loopName; into std::string loopName; in other words).
For SetAnimation, that is a bug in the interface for BZ2 that cannot be easily changed now, to work around it do this on that line instead: SetAnimation(iter1->loopObject, (char*)(iter1->loopName.c_str()),iter1->loopMode);
As for the two list error's, instead of doing std::list<loopQueue>::iterator, it should be std::list<loopQueue>::const_iterator I think... Can't check right now to be sure.
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
Making it a const_iterator prevents lines like:
--(iter1->loopTime);
from working.
I was using the wrong function, I should have used erase rather than remove.
I got it to compile, I fixed the accidental string point, finally got it to accept a char array (it wouldn't before) and used you casting line. I will test it later.
EDIT: Now it AVs.
h
//To use, place in header:
//#include "..\Shared\LoopAnimator.h"
//private:
// LoopAnimator m_LoopAnimator;
//
//Place the following in the end of the constructor
//m_LoopAnimator.shareTPS(m_GameTPS)
//in execute call:
//m_LoopAnimator.execute()
//in addObject call:
//m_LoopAnimator.addObject(h)
//and so on
#ifndef _LoopAnimator_
#define _LoopAnimator_
# pragma once
#include "..\..\source\fun3d\ScriptUtils.h"
#include <vector>
#include <list>
class LoopAnimator {
unsigned int m_GameTPS; // How many turns per second there are (10, 15, 20, or 30)
int i_first,
i_last;
Handle h_first,
h_last;
bool b_first,
b_last;
float f_first,
f_last;
struct loopQueue {
Handle loopObject;
std::string loopName;
float loopTime;
bool loopMode;
};
std::list<loopQueue> waitingLoops;
bool *b_array;
int b_count;
float *f_array;
int f_count;
int *h_array;
int h_count;
int *i_array;
int i_count;
public:
LoopAnimator(void);
void shareTPS(int inTPS);
virtual bool Save(bool missionSave);
virtual bool Load(bool missionSave);
virtual bool PostLoad(bool missionSave);
virtual void AddObject(Handle h);
virtual void DeleteObject(Handle h);
virtual void Execute(void);
};
#endif
cpp
#include "LoopAnimator.h"
#include <string.h>
#include <list> //tmp
LoopAnimator::LoopAnimator(void)
{
b_count = &b_last - &b_first - 1;
b_array = &b_first + 1;
f_count = &f_last - &f_first - 1;
f_array = &f_first + 1;
h_count = &h_last - &h_first - 1;
h_array = &h_first + 1;
i_count = &i_last - &i_first - 1;
i_array = &i_first + 1;
}
void LoopAnimator::shareTPS(int inTPS)
{
m_GameTPS = inTPS;
}
bool LoopAnimator::Load(bool missionSave)
{
if (missionSave) {
int i;
// init bools
if((b_array) && (b_count))
for (i = 0; i < b_count; i++)
b_array[i] = false;
// init floats
if((f_array) && (f_count))
for (i = 0; i < f_count; i++)
f_array[i] = 0.0f;
// init handles
if((h_array) && (h_count))
for (i = 0; i < h_count; i++)
h_array[i] = 0;
// init ints
if((i_array) && (i_count))
for (i = 0; i < i_count; i++)
i_array[i] = 0;
return true;
}
bool ret = true;
// bools
if (b_array != NULL)
Read(b_array, b_count);
// floats
if (f_array != NULL)
Read(f_array, f_count);
// Handles
if (h_array != NULL)
Read(h_array, h_count);
// ints
if (i_array != NULL)
Read(i_array, i_count);
return ret;
}
bool LoopAnimator::PostLoad(bool missionSave)
{
if (missionSave)
return true;
bool ret = true;
ConvertHandles(h_array, h_count);
return ret;
}
bool LoopAnimator::Save(bool missionSave)
{
if (missionSave)
return true;
bool ret = true;
// bools
if (b_array != NULL)
Write(b_array, b_count);
// floats
if (f_array != NULL)
Write(f_array, f_count);
// Handles
if (h_array != NULL)
Write(h_array, h_count);
// ints
if (i_array != NULL)
Write(i_array, i_count);
return ret;
}
void LoopAnimator::AddObject(Handle h)
{
// Start Queue Loop Animation
char ODFName[64];
if(GetObjInfo(h, Get_CFG, ODFName)) //is a game object check (double duty to ODFName)
{
GetObjInfo(h, Get_ODF, ODFName);
//char *loopAnimationName = new char[64];
//float loopAnimationDelay;
//bool loopAnimationType;
loopQueue tmpHolder;
tmpHolder.loopObject = h;
OpenODF(ODFName);
char loopAnimationName[64];
GetODFString(ODFName, "GameObjectClass", "loopAnimationName", 64, &loopAnimationName[64]);//tmpHolder.loopName);
tmpHolder.loopName = loopAnimationName[64];
GetODFFloat(ODFName, "GameObjectClass", "loopAnimationDelay", &tmpHolder.loopTime);
GetODFBool(ODFName, "GameObjectClass", "loopAnimationType", &tmpHolder.loopMode);
CloseODF(ODFName);
tmpHolder.loopTime *= m_GameTPS;
if((!(tmpHolder.loopName.empty())) && (tmpHolder.loopTime >= 0))// not null
{
waitingLoops.push_back(tmpHolder);
}
}
// End Queue Loop Animation
}
void LoopAnimator::DeleteObject(Handle h)
{
}
void LoopAnimator::Execute()
{
// Start Loop Animation
std::list<loopQueue>::iterator/*const_iterator*/ iter1 = waitingLoops.begin();
while( iter1 != waitingLoops.end() )
{
//AV here?
if(IsAlive2(iter1->loopObject))
{
--(iter1->loopTime);
if((iter1->loopTime) < 0)
{
SetAnimation(iter1->loopObject,(char*)(iter1->loopName.c_str()),iter1->loopMode);
StartAnimation(iter1->loopObject);
//waitingLoops.remove(iter1);
waitingLoops.erase(iter1);
}
}else{
//waitingLoops.remove(iter1);
waitingLoops.erase(iter1);
}
iter1++;
}
// End Loop Animation
}
AV
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7471 |---- Battlezone II Log File ----
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7480 |//=====================================================
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7488 |App version: Patch 1.31.3pb4rc1 EDITOR May 13 2008 21:42:32
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7497 |Exception code: C0000005 (ACCESS VIOLATION) READING from 00000008h
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7505 |Message : Exception
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7513 |Error occurred at 5/19/2008 16:11:44.
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7521 |C:\Program Files\Battlezone II 1_3pb4rc1\BZ2Edit.exe, run by Compaq_Administrator.
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7530 |1 processor(s), type 586.
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7538 |Process Memory : 1787 MB free out of 2048 MB total
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7547 |Physical memory: 114 MB free out of 1023 MB total
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7555 |Page(swap) file: 1124 MB free out of 2461 MB total
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7563 |Fault address: 0D7B30C4 01:000020C4 C:\Program Files\Battlezone II 1_3pb4rc1\missions\instantEditor.dll
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7572 |
Registers:
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7580 |EAX:00000008
EBX:052C50B8
ECX:00000000
EDX:000B0000
ESI:0229FAF0
EDI:0229FB08
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7588 |CS:EIP:001B:0D7B30C4
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7596 |SS:ESP:0023:0229FAE0 EBP:0229FB10
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7605 |DS:0023 ES:0023 FS:003B GS:0000
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7613 |Flags:00210202
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7622 |
Call stack:
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7630 |Address Return Function SourceFile
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7640 |0D7B30C4 00000000 00401867 LoopAnimator::Execute+0x00000045 d:\battlezone\dlls\bz2_1.3pb4rc1_dllsource\missions\shared\loopanimator.cpp line 152
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7647 |00401867 00000000 00401EE5 AiMission::Update+0x0000021B
ERR |GLOBAL | ExceptionHandler:242 |16:11:42|7655 |00401EE5 00000000 0047028C AI_Update+0x00000006
ERR |GLOBAL | ExceptionHandler:242 |16:11:43|7664 |0047028C 00000000 00470E07 MoveOneTurn+0x00000109
ERR |GLOBAL | ExceptionHandler:242 |16:11:43|7672 |00470E07 00000000 00585DEA MissionHandler::State::RunState::Process+0x000003BE
ERR |GLOBAL | ExceptionHandler:242 |16:11:43|7680 |00585DEA 00000000 0046E6F7 RunCodes::Process+0x0000019F
ERR |GLOBAL | ExceptionHandler:242 |16:11:43|7689 |0046E6F7 00000000 00585DEA MissionHandler::Process+0x00000011
ERR |GLOBAL | ExceptionHandler:242 |16:11:43|7697 |00585DEA 00000000 00585EF2 RunCodes::Process+0x0000019F
ERR |GLOBAL | ExceptionHandler:242 |16:11:43|7706 |00585EF2 00000000 00459F1C Main::MessagePump+0x0000004A
ERR |GLOBAL | ExceptionHandler:242 |16:11:43|7713 |00459F1C 00000000 00459C5E HandledMain+0x000003EC
ERR |GLOBAL | ExceptionHandler:242 |16:11:43|7722 |00459C5E 00000000 0045778F HandledMain+0x0000012E
ERR |GLOBAL | ExceptionHandler:242 |16:11:43|7730 |0045778F 00000000 005969F7 WinMain+0x00000059
ERR |GLOBAL | ExceptionHandler:242 |16:11:43|7739 |005969F7 00000000 7C816FD7 memset+0x00000191
ERR |GLOBAL | ExceptionHandler:242 |16:11:43|7747 |7C816FD7 00000000 00000000 RegisterWaitForInputIdle+0x00000049
ERR |GLOBAL | ExceptionHandler:242 |16:11:43|7755 |
Most likely it is deleting something using the erase member, which then of course invalidates the iterator, then you increase it to the next, which, since being invalid then, is now going to possibly deallocated memory. Queue up what you want to delete and delete it later or something, a flag on each struct would work perfect for that.
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
That is why I don't like iterators.
I might just make my own list struct node and use that, so I can directly access the next node and use simple pointer redirection to remove items from the list. To make another loop though all the elements in yet another way is a bit redundant; why not make the first loop use that system?
What would you suggest instead of iteration, or is my struct node a better idea?
Is there a RemoveObject in the AddObject function?
I GOT IT!
I had to increment the iterator and pass in the old value.
And RD, this code is actually a separate file called by the main DLL, its function names are only the same for convenience.
Now the code isn't setting the animation name correctly, so I am going to have to check that variable at various locations to see where it goes wrong. With luck this module will be ready for use for anyone who can use bare C++.
EDIT: Found the issue, and the issue I had for so long. I, by accident, this time passed in the last latter of the char array. Before I was trying to pass in the whole array. So now, I know to pass in the first letter of the array, and let the code 'dump' the rest of the letters into the adjacent memory that is also part of the array! I still store it as a string for memory saving purposes, but the code works!
EDIT2:
Here is the code. If it checks out with you OM, I will zip it up and put it on my forum in the DLL section and link it here for anyone to use. I like this style of programming, whith multiple classes for different functions.
H file
//To use, place in header:
//#include "..\Shared\LoopAnimator.h"
//private:
// LoopAnimator m_LoopAnimator;
//
//Place the following in the end of the constructor
//m_LoopAnimator.shareTPS(m_GameTPS)
//in execute call:
//m_LoopAnimator.execute()
//in addObject call:
//m_LoopAnimator.addObject(h)
//and so on
#ifndef _LoopAnimator_
#define _LoopAnimator_
# pragma once
#include "..\..\source\fun3d\ScriptUtils.h"
#include <vector>
#include <list>
class LoopAnimator {
unsigned int m_GameTPS; // How many turns per second there are (10, 15, 20, or 30)
int i_first,
i_last;
Handle h_first,
h_last;
bool b_first,
b_last;
float f_first,
f_last;
struct loopQueue {
Handle loopObject;
std::string loopName;
//char loopName[64];
float loopTime;
bool loopMode;
};
std::list<loopQueue> waitingLoops;
bool *b_array;
int b_count;
float *f_array;
int f_count;
int *h_array;
int h_count;
int *i_array;
int i_count;
public:
LoopAnimator(void);
void shareTPS(int inTPS);
virtual bool Save(bool missionSave);
virtual bool Load(bool missionSave);
virtual bool PostLoad(bool missionSave);
virtual void AddObject(Handle h);
virtual void DeleteObject(Handle h);
virtual void Execute(void);
};
#endif
CPP file
#include "LoopAnimator.h"
#include <string.h>
#include <list> //tmp
LoopAnimator::LoopAnimator(void)
{
b_count = &b_last - &b_first - 1;
b_array = &b_first + 1;
f_count = &f_last - &f_first - 1;
f_array = &f_first + 1;
h_count = &h_last - &h_first - 1;
h_array = &h_first + 1;
i_count = &i_last - &i_first - 1;
i_array = &i_first + 1;
}
void LoopAnimator::shareTPS(int inTPS)
{
m_GameTPS = inTPS;
}
bool LoopAnimator::Load(bool missionSave)
{
if (missionSave) {
int i;
// init bools
if((b_array) && (b_count))
for (i = 0; i < b_count; i++)
b_array[i] = false;
// init floats
if((f_array) && (f_count))
for (i = 0; i < f_count; i++)
f_array[i] = 0.0f;
// init handles
if((h_array) && (h_count))
for (i = 0; i < h_count; i++)
h_array[i] = 0;
// init ints
if((i_array) && (i_count))
for (i = 0; i < i_count; i++)
i_array[i] = 0;
return true;
}
bool ret = true;
// bools
if (b_array != NULL)
Read(b_array, b_count);
// floats
if (f_array != NULL)
Read(f_array, f_count);
// Handles
if (h_array != NULL)
Read(h_array, h_count);
// ints
if (i_array != NULL)
Read(i_array, i_count);
return ret;
}
bool LoopAnimator::PostLoad(bool missionSave)
{
if (missionSave)
return true;
bool ret = true;
ConvertHandles(h_array, h_count);
return ret;
}
bool LoopAnimator::Save(bool missionSave)
{
if (missionSave)
return true;
bool ret = true;
// bools
if (b_array != NULL)
Write(b_array, b_count);
// floats
if (f_array != NULL)
Write(f_array, f_count);
// Handles
if (h_array != NULL)
Write(h_array, h_count);
// ints
if (i_array != NULL)
Write(i_array, i_count);
return ret;
}
void LoopAnimator::AddObject(Handle h)
{
// Start Queue Loop Animation
char ODFName[64];
if(GetObjInfo(h, Get_CFG, ODFName)) //is a game object check (double duty to ODFName)
{
GetObjInfo(h, Get_ODF, ODFName);
//char *loopAnimationName = new char[64];
//float loopAnimationDelay;
//bool loopAnimationType;
loopQueue tmpHolder;
tmpHolder.loopObject = h;
OpenODF(ODFName);
char loopAnimationName[64];
GetODFString(ODFName, "GameObjectClass", "loopAnimationName", 64, &loopAnimationName[0]);//causes the code to dump into the adjacent memory, which happens to be part of the array. This consuzed me for so long
tmpHolder.loopName = loopAnimationName;
//GetODFString(ODFName, "GameObjectClass", "loopAnimationName", 64, &tmpHolder.loopName[0]);
GetODFFloat(ODFName, "GameObjectClass", "loopAnimationDelay", &tmpHolder.loopTime);
GetODFBool(ODFName, "GameObjectClass", "loopAnimationType", &tmpHolder.loopMode);
CloseODF(ODFName);
tmpHolder.loopTime *= m_GameTPS;
if((!(tmpHolder.loopName.empty())) && (tmpHolder.loopTime >= 0))// not null
{
waitingLoops.push_back(tmpHolder);
}
}
// End Queue Loop Animation
}
void LoopAnimator::DeleteObject(Handle h)
{
}
void LoopAnimator::Execute()
{
// Start Loop Animation
std::list<loopQueue>::iterator/*const_iterator*/ iter1 = waitingLoops.begin();
while( iter1 != waitingLoops.end() )
{
//AV here?
if(IsAlive2(iter1->loopObject))
{
--(iter1->loopTime);
if((iter1->loopTime) < 0)
{
SetAnimation(iter1->loopObject,(char*)(iter1->loopName.c_str()),iter1->loopMode);
StartAnimation(iter1->loopObject);
//waitingLoops.remove(iter1);
waitingLoops.erase(iter1++); //avoid invalidation by iterating the iterator but using the old value
}else{
iter1++;
}
}else{
//waitingLoops.remove(iter1);
waitingLoops.erase(iter1++);
}
//iter1++;
}
// End Loop Animation
}
You should default the m_GameTPS to ten, just in-case a user forgets to call the function (or, as stated before, make the constructor require it...). You have a lot of unused things in there, but they would not really harm anything. However, you are not Save/Load'ing your list data, without that then your world will not stay synced across computers (your code is not multiplayer compatible in other words).
Your char array's you should have be one more in length then you will actually use (make it 65 instead of 64 for example), and be certain to memset it all to zero (example: memset(ODFName, 0, sizeof(ODFName));). Do that to all of your char array's that you have.
Can look more later, those are just the immediate obvious things I see on a quick scroll through the code box.
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
What must the save and load functions have?
In save first put an integer of how big your list is, then serialize out each list element, for the string you will again but an integer of the length, then serialize out each character individually (there is a better way to do it, as I do in my python scriptor, but this is easier to understand). Do the same thing in load but replace the saves with loads. It is a very usual method of serializing things, you should learn it and get used to it, my programs use that style heavily (with 'prettier' syntax sugar and more caching, but still). :)
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
I was trying to look at the stock code again, and I think I see another fancy use of C++'s memory allocation at work here. Since all the ints are defined in a row, they faked an array by saying the first element was the start of the array. Since its all sequential, it all works. My question is, what prevents some random memory from getting in the way and the program skipping over it when making the ints but not when reading?
EDIT:
This has a ton of issues. I might need someone else to do this part so I have something to base my later DLLs off of.
bool LoopAnimator::PostLoad(bool missionSave)
{
if (missionSave)
return true;
bool ret = true;
if(!waitingLoops.empty())
{
int sizeL = waitingLoops.size();
Handle loopObjects[sizeL];
std::list<loopQueue>::iterator iter1 = waitingLoops.begin();
int curPos = 0;
while( iter1 != waitingLoops.end() )
{
loopObjects[curPos] = iter1->loopObject;
curPos++;
iter1++;
}
ConvertHandles(loopObjects, sizeL);
}
return ret;
}
bool LoopAnimator::Save(bool missionSave)
{
if (missionSave)
return true;
bool ret = true;
if(!waitingLoops.empty())
{
int sizeL = waitingLoops.size();
Handle loopObjects[sizeL];
//std::string loopName;
float loopTimes[sizeL];
bool loopModes[sizeL];
std::list<loopQueue>::iterator iter1 = waitingLoops.begin();
int curPos = 0;
Write(sizeL,1);//will grab this to know how many strings ahead
while( iter1 != waitingLoops.end() )
{
loopObjects[curPos] = iter1->loopObject;
loopTimes[curPos] = iter1->loopTime;
loopModes[curPos] = iter1->loopMode;
Write((char*)(iter1->loopName.c_str()),(iter1->loopName.size()));
curPos++;
iter1++;
}
Write(loopObjects, sizeL);
Write(loopTimes, sizeL);
Write(loopModes, sizeL);
}
return ret;
}
Memory in classes is defined in the order that it appears, ergo nothing will end up 'between' things. :)
From a quick looksee (assuming waitingLoops is the only thing you need to save, you don't need your gametps stuff too?) I see at least one bug. Your Handle loopObjects[sizeL]; array will not compile, don't bother with an array and just pass each Handle individually (passing in 1 as the number of elements parameter of course) to the ConvertHandles function.
Hmm, ditto with your array declarations of your timer and all such, let me type of something real quick (no say if this works, I am typing it out in this little posting quick window, but the thinking is right, also you forgot a Load function):
bool LoopAnimator::PostLoad(bool missionSave)
{
if (missionSave)
return true;
foreach(yourWaitingLoopsStruct &s, waitingLoops)
{
ConvertHandles(s.loopObject, 1);
}
return true;
}
bool LoopAnimator::Load(bool missionSave)
{
if (missionSave)
return true;
// Make sure to clear your waitingLoops first, it might not be in a Load function...
waitingLoops.clear(); // Or whatever the call was...
int size;
Read(&size, 1); // Don't forget the & for absolute values like this in both read and write, remember, it operates on memory, not values
waitingLoops.resize(size)
foreach(yourWaitingLoopsStruct &s, waitingLoops) // This is something in my code, you can figure out what it does, yes it compiles fine on my pc...
{
Read(&(s.loopObject), 1);
Read(&(s.loopTime), 1);
Read(&(s.loopMode), 1);
int strSize;
Read(&strSize, 1);
char *tempStr = new char[strSize+1]; // Could do char tempStr[1024], possible buffer overflow, but can you even have a string that long in a bz2 odf anyway?, would be a lot faster in execution though...
memset(tempStr, 0, strSize+1); // sizeof() will not work in these cases, sizeof is compile-time, not run-time
Read((void*)tempStr, strSize);
s.loopName = std::string(tempStr);
delete [] tempStr;
}
return true;
}
bool LoopAnimator::Save(bool missionSave)
{
if (missionSave)
return true;
int size = waitingLoops.size();
Write(&size, 1); // Don't forget the & for absolute values like this in both read and write, remember, it operates on memory, not values
foreach(yourWaitingLoopsStruct &s, waitingLoops) // This is something in my code, you can figure out what it does, yes it compiles fine on my pc...
{
Write(&(s.loopObject), 1);
Write(&(s.loopTime), 1);
Write(&(s.loopMode), 1);
int strSize = s.loopName.size();
Write(&strSize, 1);
Write((void*)s.loopName.c_str(), strSize);
}
return true;
}
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
I hadn't done the load function yet because I was having so much trouble.
I'm learning a lot about the way C++ uses memory addresses and all that now, so this is a big help for all of my future programming.
foreach loop eh? For each Struct in my List. I keep calling it forevery, which is wrong and doesn't compile, which is why I don' use those much.
I will try your code, or use it as a base at least, in a few hours and post my results and modifications for review. If it finally looks right I will test it when I find someone willing.
I forgot about the TPS cause I was rather annoyed the way I had to move it in in the first place.
I need to find a notebook and jot some of these things down (order of memory, automatically filling the values so first and last work, and iterating the iterator in the erase function call.
EDIT:
I had to change the foreach to get it to work (perhaspe yours was a boost foreach):
for each(loopQueue &s in waitingLoops)
but it still doesnt like the &s so I used s.
Does saving in single player to a file use these function, or are they only used in saving the state of the server's game for a new client? I ask because a saved game files does not work corectly how I have it now.
EDIT2:
I was never calling my 2nd Classe's load and save functions from the main body. DOIY!
EDIT3:
I did a quick test and the values are garbage data during the post load. What did I do wrong?
H
//To use, place in header:
//#include "..\Shared\LoopAnimator.h"
//private:
// LoopAnimator m_LoopAnimator;
//
//Place the following in the end of the constructor
//m_LoopAnimator.shareTPS(m_GameTPS)
//in execute call:
//m_LoopAnimator.execute()
//in addObject call:
//m_LoopAnimator.addObject(h)
//and so on
//
//be sure to call the same, load, and post load functions int he base DLL class at the end of those functions
#ifndef _LoopAnimator_
#define _LoopAnimator_
# pragma once
#include "..\..\source\fun3d\ScriptUtils.h"
#include <vector>
#include <list>
class LoopAnimator {
unsigned int m_GameTPS; // How many turns per second there are (10, 15, 20, or 30)
struct loopQueue {
Handle loopObject;
std::string loopName;
//char loopName[64];
float loopTime;
bool loopMode;
};
std::list<loopQueue> waitingLoops;
public:
LoopAnimator(void);
void shareTPS(int inTPS);
virtual bool Save(bool missionSave);
virtual bool Load(bool missionSave);
virtual bool PostLoad(bool missionSave);
virtual void AddObject(Handle h);
virtual void DeleteObject(Handle h);
virtual void Execute(void);
};
#endif
CPP
#include "LoopAnimator.h"
#include <string.h>
#include <list> //tmp
LoopAnimator::LoopAnimator(void)
{
m_GameTPS = 10; //default
}
void LoopAnimator::shareTPS(int inTPS)
{
m_GameTPS = inTPS;
}
bool LoopAnimator::PostLoad(bool missionSave)
{
if (missionSave)
return true;
char TempMsgStr[1024];
for each(loopQueue s in waitingLoops)//for each(loopQueue &s in waitingLoops)
{
sprintf_s(TempMsgStr, "B- Handle: %08X Name: %s Time: %d", s.loopObject, s.loopName, s.loopTime);
PrintConsoleMessage(TempMsgStr);
ConvertHandles(&(s.loopObject), 1);//ConvertHandles(&s.loopObject, 1);
sprintf_s(TempMsgStr, "A- Handle: %08X Name: %s Time: %d", s.loopObject, s.loopName, s.loopTime);
PrintConsoleMessage(TempMsgStr);
}
return true;
}
bool LoopAnimator::Load(bool missionSave)
{
if (missionSave)
return true;
// Make sure to clear your waitingLoops first, it might not be in a Load function...
waitingLoops.clear(); // Or whatever the call was...
int size;
Read(&size, 1); // Don't forget the & for absolute values like this in both read and write, remember, it operates on memory, not values
waitingLoops.resize(size);
for each(loopQueue s in waitingLoops)
{
Read(&(s.loopObject), 1);
Read(&(s.loopTime), 1);
Read(&(s.loopMode), 1);
int strSize;
Read(&strSize, 1);
char *tempStr = new char[strSize+1]; // Could do char tempStr[1024], possible buffer overflow, but can you even have a string that long in a bz2 odf anyway?, would be a lot faster in execution though...
memset(tempStr, 0, strSize+1); // sizeof() will not work in these cases, sizeof is compile-time, not run-time
Read((void*)tempStr, strSize);
s.loopName = std::string(tempStr);
delete [] tempStr;
}
Read(&m_GameTPS,1);
return true;
}
bool LoopAnimator::Save(bool missionSave)
{
if (missionSave)
return true;
int size = waitingLoops.size();
Write(&size, 1); // Don't forget the & for absolute values like this in both read and write, remember, it operates on memory, not values
for each(loopQueue s in waitingLoops) // This is something in my code, you can figure out what it does, yes it compiles fine on my pc...
{
Write(&(s.loopObject), 1);
Write(&(s.loopTime), 1);
Write(&(s.loopMode), 1);
int strSize = s.loopName.size();
Write(&strSize, 1);
Write((void*)s.loopName.c_str(), strSize);
}
Write(&m_GameTPS,1);
return true;
}
void LoopAnimator::AddObject(Handle h)
{
// Start Queue Loop Animation
char ODFName[64];
memset(ODFName, 0, sizeof(ODFName));//do this to all char arrays for bz2
if(GetObjInfo(h, Get_CFG, ODFName)) //is a game object check (double duty to ODFName)
{
GetObjInfo(h, Get_ODF, ODFName);
//char *loopAnimationName = new char[64];
//float loopAnimationDelay;
//bool loopAnimationType;
loopQueue tmpHolder;
tmpHolder.loopObject = h;
OpenODF(ODFName);
char loopAnimationName[65]; //one more in length
memset(loopAnimationName, 0, sizeof(loopAnimationName));//do this to all char arrays for bz2
GetODFString(ODFName, "GameObjectClass", "loopAnimationName", 64, &loopAnimationName[0]);//causes the code to dump into the adjacent memory, which happens to be part of the array. This consuzed me for so long
tmpHolder.loopName = loopAnimationName;
//GetODFString(ODFName, "GameObjectClass", "loopAnimationName", 64, &tmpHolder.loopName[0]);
GetODFFloat(ODFName, "GameObjectClass", "loopAnimationDelay", &tmpHolder.loopTime);
GetODFBool(ODFName, "GameObjectClass", "loopAnimationType", &tmpHolder.loopMode);
CloseODF(ODFName);
tmpHolder.loopTime *= m_GameTPS;
if((!(tmpHolder.loopName.empty())) && (tmpHolder.loopTime >= 0))// not null
{
waitingLoops.push_back(tmpHolder);
}
}
// End Queue Loop Animation
}
void LoopAnimator::DeleteObject(Handle h)
{
//cleanup is handled in execute loop
}
void LoopAnimator::Execute()
{
// Start Loop Animation
std::list<loopQueue>::iterator/*const_iterator*/ iter1 = waitingLoops.begin();
while( iter1 != waitingLoops.end() )
{
if(IsAlive2(iter1->loopObject))
{
--(iter1->loopTime);
if((iter1->loopTime) < 0)
{
SetAnimation(iter1->loopObject,(char*)(iter1->loopName.c_str()),iter1->loopMode);
StartAnimation(iter1->loopObject);
//waitingLoops.remove(iter1);
waitingLoops.erase(iter1++); //avoid invalidation by iterating the iterator but using the old value
}else{
iter1++;
}
}else{
//waitingLoops.remove(iter1);
waitingLoops.erase(iter1++);
}
//iter1++;
}
// End Loop Animation
}
Mine should be correct, and yes, my foreach is BOOST_FOREACH, so if you have boost installed (as I keep recommending :) ) then your for iteration loops can become a whole lot more simple using that.
Your "for each(..." thing is no where near C++, some other language, python it looks like? :P
And yes, saving/loading singleplayer uses these functions, as well as multiplayer to keep things synced.
As for memory in C/C++, it does it just like assembler, which is just how the hardware does it, no wierd abstraction layers like the crap Java adds on top, this is as basic as it gets, try not to make it too complex in your mind and it becomes very simple. :)
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
<sidetracked comment>
One of these days I'm going to be able to understand this thread. :D
</sidetracked comment>
This is the for each I am using:
http://blogs.msdn.com/arich/archive/2004/09/08/227139.aspx
I will keep probing the load and save functions since they appear to be failing.
That is psuedo-code in that linked url, not real code. :P
Besides boost's foreach works perfect for anything that implements the iterator interface or can be cast to the iterator interface (like a basic C-style array).
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
The code from that link compiles though? And it even loops right!
It is looping now, but somewhere I flubbed up. Either it doesn't load right, or it doest save right, because I end up with junk data. With that junk data the timers are some huge numbers and even if you wait for the whole count down the handle and animation names are trashed too.
EDIT:
Well, the for each loops looped, but they didn't increment correctly. Strangest thing.
I noticed cause the data was all identical when I started probing for memory addresses.
It works, time for final packaging and testing...
EDIT2: Since it can save and load in single player correctly with all values in tact, it should work in MP.
Now I have the files here: http://www.bzcomplex.com/forum/viewtopic.php?f=21&t=75
Does not compile for me, it has no clue what the each identifier is, nothing in scope.
In your InstantMission constructor the gameTPS is not filled out yet, you will be giving the class junk data (well, maybe ten or zero most likely, depending on whether it is preset or not...). You should set it in the initial setup function (the one that is only called once at start), and if you do not serialize it inside your class then reset it in postload as well.
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
The Constructor for my class sets its internal TPS and saves it too, what the class it is attached to does is no matter as long as they call the shareTPS function where I note so in my instructions.
You should look for where that gameTPS variable is being set, make sure you are calling your set function *after* that point, specifically, if they call it in the constructor, make sure they call it *after* the "EnableHighTPS(m_GameTPS);" call. If that call occurs elsewhere (it may not always be in the constructor) then make sure it is called right after whenever that takes place.
(http://www.overminddl1.com/image_scripts/image_sig.php?type=ODL1signitures&image/sig.gif)
Generated by OvermindDL1's Signature Auto-Add Script (http://overminddl1.com) via GreaseMonkey (http://greasemonkey.mozdev.org/)
That's A good point. I will make that amendum to the instructions. Where I have it being placed now the default DLL can use it.