Jump to content

SFO: bugs/inconsistencies/clarifications


Recommended Posts

On 6/27/2020 at 2:03 PM, DavidW said:

Pretty sure it’s correct.

Me too, otherwise the SSL compiler would complain... It's simply little weird notation to me... I'd write: scsroot=stratagems&tutu_var=%tutu_var%, but whatever...

Link to post
46 minutes ago, Luke said:

Me too, otherwise the SSL compiler would complain... It's simply little weird notation to me... I'd write: scsroot=stratagems&tutu_var=%tutu_var%, but whatever...

But I don’t want the string ‘scsroot’ replaced by ‘stratagems’. I want the string ‘%scsroot%’ replaced.

Link to post
31 minutes ago, Luke said:

Another initialization issue: clone_template doesn't make use of the variable "tv". Is that intended?

I'm asking because you set it when launching clone_store and clone_creature...

I think I’m going to use this question to say that I’m probably not going to answer much more, unless it’s an actual bug or it catches my interest for some reason. It’s too time-consuming and goes against my ‘this is as-is, I don’t support it’ intention.

Link to post
9 minutes ago, DavidW said:

unless it’s an actual bug or it catches my interest for some reason...

OK, I simply find that issue similar to the missing "arguments" string in "enforce_hp"... Sure, maybe it's not critical, but still something that should be taken into account...?

Anyway, I really like reading your code and the "functional programming" way of doing things, sorry if I ask you too many questions 😅

Link to post

No need to be sorry! I don't mind at all (and I'm really glad you're interested in the code), I just wanted to flag that I wasn't always going to respond. I'll probably go back through this thread next time I do a proper update of the library (thouh I'm a bit cautious modifying the existing code too much even when it seems logical, because it's being used all over SCS and you never know when surprise problems will turn up.)

Link to post
Posted (edited)
19 hours ago, DavidW said:

I'll probably go back through this thread next time I do a proper update of the library

OK, I'll keep reporting issues/oddities then...

Missing "warning =" here (after STR_VAR).

Edited by Luke
Link to post
Posted (edited)

In case you're interested, my version of "ENFORCE_HP" *should* correctly handle dual-class characters. Note that:

  • I assume the CRE being patched is a playable CLASS
  • my notation is a bit different from yours
  • I'm not interested in "at_best", "at_worst" and the like: I simply want the maximum (possibly adjusted by the INI variable "hitpoint_percentage")
Spoiler

DEFINE_ACTION_MACRO ~HPCLASS_2DA~ BEGIN
	LOCAL_SET "read_hpclass" = 0
	LOCAL_SET "i" = 0
	LOCAL_SPRINT "class" ""
	LOCAL_SPRINT "table" ""
	LOCAL_SET "cols" = 0
	LOCAL_SET "read_hptable" = 0
	LOCAL_SET "j" = 0
	LOCAL_SET "level" = 0
	LOCAL_SET "sides" = 0
	LOCAL_SET "rolls" = 0
	LOCAL_SET "modifier" = 0

	COPY_EXISTING - "HPCLASS.2DA" "override"
		READ_2DA_ENTRIES_NOW "read_hpclass" 0	// Column count starts from 0
		FOR ("i" = 3; "%i%" < "%read_hpclass%"; "i" += 1) BEGIN
			READ_2DA_ENTRY_FORMER "read_hpclass" "%i%" 0 "class"
			READ_2DA_ENTRY_FORMER "read_hpclass" "%i%" 1 "table"
			INNER_PATCH_FILE "%table%.2da" BEGIN
				COUNT_2DA_COLS "cols"
				READ_2DA_ENTRIES_NOW "read_hptable" "%cols%"
				FOR ("j" = 0; "%j%" < "%read_hptable%"; "j" += 1) BEGIN
					READ_2DA_ENTRY_FORMER "read_hptable" "%j%" 0 "level"
					READ_2DA_ENTRY_FORMER "read_hptable" "%j%" 1 "sides"
					READ_2DA_ENTRY_FORMER "read_hptable" "%j%" 2 "rolls"
					READ_2DA_ENTRY_FORMER "read_hptable" "%j%" 3 "modifier"
					//PATCH_PRINT "class = %class%, level = %level%, sides = %sides%, rolls = %rolls%, modifier = %modifier%"
					SET $hp_increment_max("%class%" "%level%") = ("%sides%" * "%rolls%") + "%modifier%"
				END
			END
		END
	BUT_ONLY_IF_IT_CHANGES
END
  
LAUNCH_ACTION_MACRO ~HPCLASS_2DA~



DEFINE_PATCH_FUNCTION "ENFORCE_HP"
BEGIN
	// Get the IDS name of the CRE CLASS
	READ_BYTE 0x273 "class_num"
	LOOKUP_IDS_SYMBOL_OF_INT "class_name" "CLASS" "%class_num%"

	// Determine if the CRE in question is a playable CLASS
	LPF "IS_PLAYABLE_CLASS"
	STR_VAR
		"class" = "%class_name%"
	END

	// Determine if the CRE in question is a dual-class character
	LPF "IS_DUAL_CLASS"
	STR_VAR
		"class" = "%class_name%"
	RET
		"is_dual_class"
	END

	// If the CRE in question is a Dual-class character, then get its original class
	PATCH_IF ("%is_dual_class%") BEGIN
		LPF "READ_ORIGINAL_CLASS"
		RET
			"original_class"
		END
	END ELSE BEGIN
  		// Determine if the CRE in question is a non-human multi-class – Optional...?
		LPF "IS_VALID_MULTI-CLASS"
		STR_VAR
			"class" = "%class_name%"
		END
  	END

	// Get the IDS name of the its KIT
	READ_LONG 0x244 "kit_num"
	LPF "REVERSE_LONG"
	INT_VAR
		"input" = "%kit_num%"
	RET
		"kit_num" = "output"
	END
	LOOKUP_IDS_SYMBOL_OF_INT "kit_name" "KIT" "%kit_num%"

	// Strip "MAGESCHOOL_" from "%kit_name%" if needed – This is because specialist mages are stripped of "MAGESCHOOL_" in "HPCLASS.2DA"
	PATCH_IF (("%kit_name%" STRING_MATCHES_REGEXP "^MAGESCHOOL_.+$") == 0) BEGIN
		INNER_PATCH_SAVE "kit_name" "%kit_name%" BEGIN
			REPLACE_TEXTUALLY CASE_SENSITIVE EXACT_MATCH "MAGESCHOOL_" ""
		END
	END

	// Initialization
	SET "number_of_classes" = 0
	SET "hp_total" = 0

	// Main
	WHILE ("%class_name%" STRING_COMPARE_CASE "") BEGIN
		SET "number_of_classes" = "%number_of_classes%" + 1
		LPF "RETURN_FIRST_ENTRY"
		STR_VAR
			"separator" = "_"
			"list" = "%class_name%"
		RET
			"class_name" = "list"
			"entry"
		END

		PATCH_MATCH "%number_of_classes%" WITH
			"1" BEGIN
				READ_BYTE 0x234 "level"

				PATCH_IF "%is_dual_class%" BEGIN
					//PATCH_PRINT ~Determine if the CLASS "%entry%" is the original class~
					PATCH_IF ("%entry%" STRING_EQUAL "%original_class%") BEGIN
						// CLASS ~%entry%~ is the original class
						PATCH_IF (VARIABLE_IS_SET $hp_increment_max("%kit_name%" "1")) BEGIN
							// The original class can be a KIT
							FOR ("i" = 1; "%i%" <= "%level%"; "i" += 1) BEGIN
								SET "hp_total" = "%hp_total%" + $hp_increment_max("%kit_name%" "%i%")
							END
						END ELSE BEGIN
							FOR ("i" = 1; "%i%" <= "%level%"; "i" += 1) BEGIN
								SET "hp_total" = "%hp_total%" + $hp_increment_max("%entry%" "%i%")
							END
						END
					END ELSE BEGIN
						// CLASS ~%entry%~ is the current/active class
						READ_BYTE 0x235 "level2"
						SET "diff" = "%level%" - "%level2%"
						PATCH_IF ("%diff%" > 0) BEGIN
							FOR ("i" = "%level2%" + 1; "%i%" <= "%level%"; "i" += 1) BEGIN
								SET "hp_total" = "%hp_total%" + $hp_increment_max("%entry%" "%i%")
							END
						END ELSE BEGIN
							PATCH_WARN "~%SOURCE_FILE%~ (~%class_name%~, ~%kit_name%~) is an incomplete dual-class character (active_class = %level%; inactive_class = %level2%). Deliberate?"
						END
					END
				END ELSE BEGIN	// Multi-class or Single-class
					PATCH_IF ("%class_name%" STRING_EQUAL_CASE "") BEGIN
						// Single-class
						PATCH_IF (VARIABLE_IS_SET $hp_increment_max("%kit_name%" "1")) BEGIN
							// Single-class characters can have a KIT
							FOR ("i" = 1; "%i%" <= "%level%"; "i" += 1) BEGIN
								SET "hp_total" = "%hp_total%" + $hp_increment_max("%kit_name%" "%i%")
							END
						END ELSE BEGIN
							FOR ("i" = 1; "%i%" <= "%level%"; "i" += 1) BEGIN
								SET "hp_total" = "%hp_total%" + $hp_increment_max("%entry%" "%i%")
							END
						END
					END ELSE BEGIN
						// First class of a Multi-class
						FOR ("i" = 1; "%i%" <= "%level%"; "i" += 1) BEGIN
							SET "hp_total" = "%hp_total%" + $hp_increment_max("%entry%" "%i%")
						END
					END
				END
			END

			"2" BEGIN
				READ_BYTE 0x235 "level2"

				PATCH_IF "%is_dual_class%" BEGIN
					//PATCH_PRINT ~Determine if the CLASS "%entry%" is the original class~
					PATCH_IF ("%entry%" STRING_EQUAL "%original_class%") BEGIN
						// CLASS ~%entry%~ is the original class
						PATCH_IF (VARIABLE_IS_SET $hp_increment_max("%kit_name%" "1")) BEGIN
							// The original class can be a KIT
							FOR ("i" = 1; "%i%" <= "%level2%"; "i" += 1) BEGIN
								SET "hp_total" = "%hp_total%" + $hp_increment_max("%kit_name%" "%i%")
							END
						END ELSE BEGIN
							FOR ("i" = 1; "%i%" <= "%level2%"; "i" += 1) BEGIN
								SET "hp_total" = "%hp_total%" + $hp_increment_max("%entry%" "%i%")
							END
						END
					END ELSE BEGIN
						// CLASS ~%entry%~ is the current/active class
						READ_BYTE 0x234 "level"
						SET "diff" = "%level2%" - "%level%"
						PATCH_IF ("%diff%" > 0) BEGIN
							FOR ("i" = "%level%" + 1; "%i%" <= "%level2%"; "i" += 1) BEGIN
								SET "hp_total" = "%hp_total%" + $hp_increment_max("%entry%" "%i%")
							END
						END ELSE BEGIN
							PATCH_WARN "~%SOURCE_FILE%~ (~%class_name%~, ~%kit_name%~) is an incomplete dual-class character (inactive_class = %level%; active_class = %level2%). Deliberate?"
						END
					END
				END ELSE BEGIN	// Multi-class
					// Second class of a Multi-class
					FOR ("i" = 1; "%i%" <= "%level2%"; "i" += 1) BEGIN
						SET "hp_total" = "%hp_total%" + $hp_increment_max("%entry%" "%i%")
					END
				END
			END

			"3" BEGIN
				// Third class of a Multi-class
				READ_BYTE 0x236 "level3"
				FOR ("i" = 1; "%i%" <= "%level3%"; "i" += 1) BEGIN
					SET "hp_total" = "%hp_total%" + $hp_increment_max("%entry%" "%i%")
				END
			END

			DEFAULT
		END
	END

	PATCH_IF ("%number_of_classes%" == 1) OR ("%is_dual_class%") BEGIN
		WRITE_SHORT 0x24 ("%hp_total%")
		WRITE_SHORT 0x26 ("%hp_total%")
	END ELSE BEGIN
		WRITE_SHORT 0x24 ("%hp_total%" / "%number_of_classes%")
		WRITE_SHORT 0x26 ("%hp_total%" / "%number_of_classes%")
	END
END
                                       
// Helper functions

DEFINE_PATCH_FUNCTION "IS_DUAL_CLASS"
STR_VAR
	"class" = ""
RET
	"is_dual_class"
BEGIN
	PATCH_MATCH "%class%" WITH
		"FIGHTER_MAGE" "FIGHTER_CLERIC" "FIGHTER_THIEF" "MAGE_THIEF" "CLERIC_MAGE" "CLERIC_THIEF" "FIGHTER_DRUID" "CLERIC_RANGER" BEGIN
			SET "not_single_class" = 1
		END
		DEFAULT
			SET "not_single_class" = 0
			SET "is_dual_class" = 0
	END

	PATCH_IF "%not_single_class%" BEGIN
		READ_LONG 0x10 "flags"
		SET "x" = (BIT3 BOR BIT4 BOR BIT5 BOR BIT6 BOR BIT7 BOR BIT8)
		SET "y" = "%flags%" BAND "%x%"

		PATCH_MATCH "%y%" WITH
			"8" "16" "32" "64" "128" "256" BEGIN	// One of those flags is set => Dual-class
				SET "is_dual_class" = 1
				LPF "IS_VALID_DUAL-CLASS"	// Optional...?
				END
			END
			"0" BEGIN	// None of those flags is set => Multi-class
				SET "is_dual_class" = 0
			END
			DEFAULT
				PATCH_FAIL "~%SOURCE_FILE%~ cannot have more than one original class!"
		END
	END
END


DEFINE_PATCH_FUNCTION "READ_ORIGINAL_CLASS"
RET
	"original_class"
BEGIN
	READ_LONG 0x10 "flags"

	// Determine the original class
	CLEAR_ARRAY "array"
	DEFINE_ASSOCIATIVE_ARRAY "array" BEGIN
		"FIGHTER" => "BIT3"
		"MAGE" => "BIT4"
		"CLERIC" => "BIT5"
		"THIEF" => "BIT6"
		"DRUID" => "BIT7"
		"RANGER" => "BIT8"
	END

	PHP_EACH "array" AS "class" => "bit" BEGIN
		PATCH_IF (("%flags%" BAND "%bit%") == "%bit%") BEGIN
			TEXT_SPRINT "original_class" "%class%"
		END
	END
END


DEFINE_PATCH_FUNCTION "IS_PLAYABLE_CLASS"
STR_VAR
	"class" = ""
BEGIN
	PATCH_MATCH "%class%" WITH
		"MAGE"
		"FIGHTER"
		"CLERIC"
		"THIEF"
		"BARD"
		"PALADIN"
		"FIGHTER_MAGE"
		"FIGHTER_CLERIC"
		"FIGHTER_THIEF"
		"FIGHTER_MAGE_THIEF"
		"DRUID"
		"RANGER"
		"MAGE_THIEF"
		"CLERIC_MAGE"
		"CLERIC_THIEF"
		"FIGHTER_DRUID"
		"FIGHTER_MAGE_CLERIC"
		"CLERIC_RANGER"
		"SORCERER"
		"MONK"
		"SHAMAN"
			BEGIN
			END

		DEFAULT
			PATCH_FAIL "~%SOURCE_FILE%~ => you cannot use ~ENFORCE_HP~ with the non-playable CLASS ~%class%~"
	END
END

DEFINE_PATCH_FUNCTION "IS_VALID_MULTI-CLASS"
STR_VAR
	"class" = ""
BEGIN
	PATCH_IF (("%class%" STRING_CONTAINS_REGEXP "_") == 0) BEGIN
		READ_BYTE 0x272 "race_num"
		LOOKUP_IDS_SYMBOL_OF_INT "race_name" "RACE" "%race_num%"
		PATCH_IF ("%race_name%" STRING_EQUAL_CASE "HUMAN") BEGIN
			PATCH_WARN "~%SOURCE_FILE%~ (~%class_name%~, ~%race_name%~) is a HUMAN multi-class. Deliberate?"
		END
	END
END

DEFINE_PATCH_FUNCTION "IS_VALID_DUAL-CLASS"
BEGIN
	READ_BYTE 0x272 "race_num"
	LOOKUP_IDS_SYMBOL_OF_INT "race_name" "RACE" "%race_num%"
	PATCH_IF ("%race_name%" STRING_COMPARE_CASE "HUMAN") BEGIN
		PATCH_WARN "~%SOURCE_FILE%~ (~%race_name%~) is a non-HUMAN dual-class. Deliberate?"
	END
END

 

 

Edited by Luke
Link to post
Posted (edited)
  • The variable "loc" written here and here is defined and initialized as an INTEGER variable (so you might want to replace that "STR_VAR loc=xxx" with "INT_VAR loc=xxx"...). This occurs several times and it fake works because the string under consideration is indeed an integer. Anyway, you might want to check that out...
  • The function CRE_delete_opcodes doesn't take any variable called file_ext (just "arguments"...)
  • After this instruction (that should probably be "^.+\.cre$"...?), you might want to throw "PATCH_IF (SOURCE_SIZE > 0x2D3) BEGIN ... END", just in case... Actually, you might want to do it after every COPY_EXISTING_REGEXP so as to skip corrupted files...
  • This READ_SHORT should be READ_LONG!
  • Regarding this complain: I'm 100% sure you can solve it via the so-called PATCH_REINCLUDE trick...
  • You often write (integer) in place of #integer when using WRITE_ASCII (example here). According to WeiDU's docs, #integer is the correct form.
Edited by Luke
Link to post
Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...