This patch by Monsterovich allows all ACS functions that use sector tags or line IDs to work on individual sectors/lines. This extends the flexibility of ACS scripting and greatly reduces the need to assign a different tag to each sector when implementing complex effects because ACS scripts are not forced to use the restrictive "sector tag/line id" system.
diff -c srcorig/p_acs.cpp src/p_acs.cpp
*** srcorig/p_acs.cpp 2013-07-03 03:47:30.000000000 +0300
--- src/p_acs.cpp 2014-04-12 17:45:14.348356800 +0300
***************
*** 153,158 ****
--- 153,171 ----
//============================================================================
//
+ // num
+ //
+ // [MOVICH] Convert value to number with formula
+ //
+ //============================================================================
+
+ inline int num(int x)
+ {
+ return -x - 1;
+ }
+
+ //============================================================================
+ //
// uallong
//
// Read a possibly unaligned four-byte little endian integer from memory.
***************
*** 4200,4205 ****
--- 4213,4235 ----
ACSF_SpawnDecal,
ACSF_CheckFont,
+ /* [MOVICH] ACS */
+ ACSF_SectorCount,
+ ACSF_LineCount,
+ ACSF_Num,
+ ACSF_PointInSector,
+ ACSF_SectorTag,
+ ACSF_LineID,
+ /* not tested yet, but works :) */
+ ACSF_GetLineStartX,
+ ACSF_GetLineStartY,
+ ACSF_GetLineEndX,
+ ACSF_GetLineEndY,
+ ACSF_FrontSector,
+ ACSF_BackSector,
+ ACSF_CheckFloor,
+ ACSF_CheckCeiling,
+
// ZDaemon
ACSF_GetTeamScore = 19620, // (int team)
ACSF_SetTeamScore, // (int team, int value)
***************
*** 5197,5202 ****
--- 5227,5346 ----
case ACSF_CheckFont:
// bool CheckFont(str fontname)
return V_GetFont(FBehavior::StaticLookupString(args[0])) != NULL;
+ /* [MOVICH] Functions */
+ case ACSF_SectorCount:
+ {
+ return numsectors;
+ }
+
+ case ACSF_LineCount:
+ {
+ return numlines;
+ }
+
+ case ACSF_Num: // int Num(int x)
+ {
+ return num(args[0]);
+ }
+
+ case ACSF_PointInSector: // int PointInSector(int x, int y)
+ {
+ return num(P_PointInSector(args[0], args[1])->sectornum);
+ }
+
+ case ACSF_SectorTag: // int SectorTag(int num)
+ {
+ if (args[0] >= 0 || num(args[0]) >= numsectors)
+ return 0;
+
+ if (sectors)
+ return sectors[num(args[0])].tag;
+ }
+
+ case ACSF_LineID: // int LineID(int num)
+ {
+ if (args[0] >= 0 || num(args[0]) >= numlines)
+ return 0;
+
+ if (lines)
+ return lines[num(args[0])].id;
+ }
+
+ case ACSF_GetLineStartX: // int GetLineStartX(int num)
+ {
+ if (args[0] >= 0 || num(args[0]) >= numlines)
+ return 0;
+
+ if (lines)
+ return lines[num(args[0])].v1->x;
+ }
+
+ case ACSF_GetLineStartY: // int GetLineStartY(int num)
+ {
+ if (args[0] >= 0 || num(args[0]) >= numlines)
+ return 0;
+
+ if (lines)
+ return lines[num(args[0])].v1->y;
+ }
+
+ case ACSF_GetLineEndX: // int GetLineEndX(int num)
+ {
+ if (args[0] >= 0 || num(args[0]) >= numlines)
+ return 0;
+
+ if (lines)
+ return lines[num(args[0])].v2->x;
+ }
+
+ case ACSF_GetLineEndY: // int GetLineEndY(int num)
+ {
+ if (args[0] >= 0 || num(args[0]) >= numlines)
+ return 0;
+
+ if (lines)
+ return lines[num(args[0])].v2->y;
+ }
+
+ case ACSF_FrontSector: // int FrontSector(int linenum)
+ {
+ if (args[0] >= 0 || num(args[0]) >= numlines)
+ return 0;
+
+ if (lines)
+ return num(lines[num(args[0])].frontsector->sectornum);
+ }
+
+ case ACSF_BackSector: // int BackSector(int linenum)
+ {
+ if (args[0] >= 0 || num(args[0]) >= numlines)
+ return 0;
+
+ if (lines)
+ return num(lines[num(args[0])].backsector->sectornum);
+ }
+
+ case ACSF_CheckFloor: // bool CheckFloor(int num, str texture)
+ {
+ if (args[0] >= 0 || num(args[0]) >= numsectors)
+ return 0;
+
+ FTexture *tex = TexMan.FindTexture(FBehavior::StaticLookupString(args[1]));
+
+ if (sectors && sectors[num(args[0])].GetTexture(sector_t::floor) == tex->GetID())
+ return 1;
+ }
+
+ case ACSF_CheckCeiling: // bool CheckCeiling(int num, str texture)
+ {
+ if (args[0] >= 0 || num(args[0]) >= numsectors)
+ return 0;
+
+ FTexture *tex = TexMan.FindTexture(FBehavior::StaticLookupString(args[1]));
+
+ if (sectors && sectors[num(args[0])].GetTexture(sector_t::ceiling) == tex->GetID())
+ return 1;
+ }
default:
break;
diff -c srcorig/p_acs.h src/p_acs.h
*** srcorig/p_acs.h 2013-07-03 03:47:30.000000000 +0300
--- src/p_acs.h 2014-04-12 17:29:58.124336872 +0300
***************
*** 45,50 ****
--- 45,52 ----
class FFont;
class FileReader;
+ // [MOVICH]
+ int num(int x);
enum
{
diff -c srcorig/p_spec.cpp src/p_spec.cpp
*** srcorig/p_spec.cpp 2013-07-03 03:47:30.000000000 +0300
--- src/p_spec.cpp 2014-04-11 17:17:43.060110721 +0300
***************
*** 198,203 ****
--- 198,218 ----
int P_FindSectorFromTag (int tag, int start)
{
+ /* [MOVICH] Return sector number (for example: -1 is a zero sector) */
+ if (tag < 0)
+ {
+ if (num(tag) >= numsectors)
+ {
+ Printf("ACS: attempted to access nonexistent sector %d\n", num(tag));
+ return -1;
+ }
+
+ int number = num(tag);
+ if (start == number)
+ return -1;
+ return number;
+ }
+
start = start >= 0 ? sectors[start].nexttag :
sectors[(unsigned) tag % (unsigned) numsectors].firsttag;
while (start >= 0 && sectors[start].tag != tag)
***************
*** 209,214 ****
--- 224,244 ----
int P_FindLineFromID (int id, int start)
{
+ /* [MOVICH] Return line number (for example: -1 is a zero line) */
+ if (id < 0)
+ {
+ if (num(id) >= numlines)
+ {
+ Printf("ACS: attempted to access nonexistent linedef %d\n", num(id));
+ return -1;
+ }
+
+ int number = num(id);
+ if (start == number)
+ return -1;
+ return number;
+ }
+
start = start >= 0 ? lines[start].nextid :
lines[(unsigned) id % (unsigned) numlines].firstid;
while (start >= 0 && lines[start].id != id)
The idea is very simple: if tag/lineid given to any function is negative, it is a sector or line number. -1 is the first sector, -2 is the second, etc... This keeps compatibilty with all the functions that work on tags/lineids while adding new functionality.
To convert between simple sector/line numbers and "negative tags", the formula "-x - 1" is used. It is abbeviated as function num(int x), though the name should probably be changed. num(int x) converts both ways. num(num(x)) == x
Possible uses:
ACS gamemodes that grealy modify ANY MAP. Previously, it was possible to apply only one modification to the majority of sectors because they were all tagged 0. Now any sector can be modified individually.
Complex scripting effects are now a lot easier and don't require a lot of different tags. Want a huge city map with a lot of lit sectors (with different light levels) that can turn on/off? Tag them with ONE tag, then in the ACS iterate over all sectors, check if they have that tag, collect the list of such sectors. When switching the lights off, save their lightlevels. And when turning them back on, restore their light levels individually.
Just for fun: weapon that edit heights/lightlevels/colors/fog of ANY sector. Just like Sectorcraft but on ANY map!
// RAINBOW DOOM!!!! Color each sector in different color!
script 1 OPEN
{
print(s:"Sectors: ", d:SectorCount(), s:" Lines:", d:LineCount());
for (int i = 0; i < SectorCount(); i++)
{
// this part of code is laggy on slow CPUs in software mode because of colormap generation.
Sector_SetColor(num(i), Random(0, 255), Random(0,255), Random(0, 255), 0);
Light_ChangeToValue(num(i), 255);
Delay(1);
}
}
// Color all VISITED sectors in green.
script 2 enter
{
// Notice that PointInSector returns sector number in "negative tag" format.
int sector = PointInSector(GetActorX(0), GetActorY(0));
Sector_SetColor(sector, 192, 255, 192);
print(s:"Sector: ", d:num(sector), s:" with tag: ", d:SectorTag(sector));
Delay(1);
restart;
}
The list of additional functions that are implemented: All sector and line numbers are in "negative tag" format. This is done so results could be passed straight to old sector/line functions that accept tags.
Probably, more functions should be added to take advantage of the new system.
Spoiler: New ACS functions
int SectorCount() - returns number of sectors on the map
int LineCount() - returns number of linedefs on the map.
int PointInSector(int x, int y) - returns the sector that contains the given 2D point
int SectorTag(int sector) - returns the tag of given sector
int LineID(int line) - returns the LineID of given linedef
int FrontSector(int line) - returns the front sector of the line
int BackSector(int line) - returns the back sector of the line (or 0 if there is none)
int GetLineStartX(int line)
int GetLineStartY(int line)
int GetLineEndX(int line)
int GetLineEndY(int line)
--- These functions return vertex coordinates for given line.
int CheckFloor(int sector, str texture)
int CheckCeiling(int sector, str texture)
--- These functions check floor/ceiling texture in given sector. I don't know whenther the code is correct. (That's not my patch)
Compatibility issue: negative tags are supported and, in fact, actually used in some cases. (I think CIF3 did, for example.)
Design: tags exist to give arbitrary identifiers to sectors (and other map elements) instead of relying on index, which is a value that can be changed by simply deleting one element and saving the map back.
-ve tags are already in some released projects.
A simple edit to a map can change a large number of the sector/line identifiers.
Editors can apply tag ranges.
Even in older map formats, sector tags are unlikely to run out and in UDMF I believe that they are (virtually?) unlimited.
Gez wrote:Compatibility issue: negative tags are supported and, in fact, actually used in some cases. (I think CIF3 did, for example.)
O_o how does that even work? And why doesn't Doom Builder let me use negative tags then?
Gez wrote:tags exist to give arbitrary identifiers to sectors (and other map elements) instead of relying on index, which is a value that can be changed by simply deleting one element and saving the map back.
Direct access to sectors/lines is not supposed to be a replacement for tags. Sector/line numbers should not be hardcoded, they should be retrieved using functions like PointInSector. Or by iterating over all sectors and selecting the ones that match some criteria.
Korshun wrote:O_o how does that even work? And why doesn't Doom Builder let me use negative tags then?
Maybe it uses unsigned variables. I think in CIF3's case, it's used for thing IDs, and the actors are given a negative TID by ACS rather than in the editor. Sure, things aren't lines or sectors, but they're all map elements and if something can be done to retrieve a line or sector in particular, there should be an equivalent function to retrieve things in the same way.
Korshun wrote:they should be retrieved using functions like PointInSector. Or by iterating over all sectors and selecting the ones that match some criteria.
Then WFDS I guess. Instead of using indices, I'd want to use objects for that. In pseudocode, not "int index = getsectornum(conditions); dostufftosectornum(index);" but "sector sec = getsector(conditions); dostufftosector(sec);"
I, too, want to use actual objects instead of numbers. But everything is an integer in ACS. And I, too, want the same feature for actors (like I said in the first post). But neither I nor Monsterovich have an idea on how to get actor numbers that don't change during the game.
Compatflag is a good way to make support for "negative tags", which used in mods like CIF3 (0.01% of mods).
Why ACS has no "#version" For example: GLSL has it!
Last edited by Monsterovich on Sun Apr 13, 2014 6:08 am, edited 1 time in total.
At first I was a bit iffy about this suggestion for the same reasons posted above (namely that sector/line numbers change like crazy and scrips are gonna super-break), but after some thought, this might actually have some potential since operations in the form of "get sector ID from tag, do stuff via sector ID" is change-safe and allows for things not doable before (e.g. the building window example in the OP). The feature would indeed allow folks to screw things up if used badly, but the same can be said about lots of features.
The implementation might be troublesome though (since negative tags exist), and this does seem like a very Doomscripty thing -- in fact, it almost feels like it'd be something you could do with it out of the box. I'm guessing, of course.
Perhaps. If we're talking just sectors/lines, there's limited potential for use of these features in gameplay mods. All possibilities I can think of are either exceptionally weird or map-breaky things, though examples to the contrary would be good to know if anyone's got some.
I honestly can't think of any real world use for this.
The only time I ever needed something like this was when I tried to apply a machine generated colored light table to an existing map - and these days with UDMF even that 's no longer necessary because it can be written directly to the map data.
For regular mapping this is of no use whatsoever and for gameplay mods even less.
NO! That's wrong. That could be very useful for gameplay mods. We can choose any sector/line in ACS, using numbers as "negative tags", without "pain-in-ass" (seriously) tag marking. Also, I think, you didn't read this thread and just closed this suggestion, because you don't like.
Graf Zahl wrote:For regular mapping this is of no use whatsoever
Spoiler:
Direct access to sectors/lines is not supposed to be a replacement for tags. Sector/line numbers should not be hardcoded, they should be retrieved using functions like PointInSector. Or by iterating over all sectors and selecting the ones that match some criteria.
Last edited by Monsterovich on Thu Apr 17, 2014 4:33 am, edited 6 times in total.
Monsterovich wrote:NO! That's wrong. That could be very useful for gameplay mods. We can choose any sector/line in ACS, using numbers, without "pain-in-ass" tags. Also, I think, you didn't read this thread and just closed this suggestion.
Do... what... exactly? With which line/sector? There is absolutely zero frame of reference for any line/sector values, you know, being completely blind to how the map is structured. Or any map. You don't even know what map it's going to be on.
Edit: You edited your post, and somehow made it worse. Congrats.
Monsterovich wrote:using numbers as "negative tags", without "pain-in-ass" (seriously) tag marking
You can't use negative tags. They are already used for tagging lines/things and sectors as per normal. You can't just change the rules already in place, breaking a selection of mods, just because you want something else.
Monsterovich wrote:Also, I think, you didn't read this thread and just closed this suggestion, because you don't like.
You are going to have to explain this. A programmer doesn't like useless and uninformed implementations of features? Where the hell do I sign up?