Bläddra i källkod

Added script support to status changes (#8786)

Lemongrass3110 6 månader sedan
förälder
incheckning
7a80ecb6f2
7 ändrade filer med 90 tillägg och 11 borttagningar
  1. 2 1
      db/import-tmpl/status.yml
  2. 2 1
      db/pre-re/status.yml
  3. 2 1
      db/re/status.yml
  4. 2 1
      db/status.yml
  5. 6 1
      doc/status.txt
  6. 71 4
      src/map/status.cpp
  7. 5 2
      src/map/status.hpp

+ 2 - 1
db/import-tmpl/status.yml

@@ -38,8 +38,9 @@
 #   EndOnStart:               List of Status Changes that will end when the status activates. (Optional)
 #   EndReturn:                List of Status Changes that will end when the status activates and won't give its effect. (Optional)
 #   EndOnEnd:                 List of Status Changes that will end when the status becomes inactive. (Optional)
+#   Script:                   Script to execute, when starting the status change. (Optional)
 ###########################################################################
 
 Header:
   Type: STATUS_DB
-  Version: 3
+  Version: 4

+ 2 - 1
db/pre-re/status.yml

@@ -38,11 +38,12 @@
 #   EndOnStart:               List of Status Changes that will end when the status activates. (Optional)
 #   EndReturn:                List of Status Changes that will end when the status activates and won't give its effect. (Optional)
 #   EndOnEnd:                 List of Status Changes that will end when the status becomes inactive. (Optional)
+#   Script:                   Script to execute, when starting the status change. (Optional)
 ###########################################################################
 
 Header:
   Type: STATUS_DB
-  Version: 3
+  Version: 4
 
 Body:
   - Status: Stone

+ 2 - 1
db/re/status.yml

@@ -38,11 +38,12 @@
 #   EndOnStart:               List of Status Changes that will end when the status activates. (Optional)
 #   EndReturn:                List of Status Changes that will end when the status activates and won't give its effect. (Optional)
 #   EndOnEnd:                 List of Status Changes that will end when the status becomes inactive. (Optional)
+#   Script:                   Script to execute, when starting the status change. (Optional)
 ###########################################################################
 
 Header:
   Type: STATUS_DB
-  Version: 3
+  Version: 4
 
 Body:
   - Status: Stone

+ 2 - 1
db/status.yml

@@ -38,11 +38,12 @@
 #   EndOnStart:               List of Status Changes that will end when the status activates. (Optional)
 #   EndReturn:                List of Status Changes that will end when the status activates and won't give its effect. (Optional)
 #   EndOnEnd:                 List of Status Changes that will end when the status becomes inactive. (Optional)
+#   Script:                   Script to execute, when starting the status change. (Optional)
 ###########################################################################
 
 Header:
   Type: STATUS_DB
-  Version: 3
+  Version: 4
 
 Footer:
   Imports:

+ 6 - 1
doc/status.txt

@@ -3,7 +3,7 @@
 //===== By: ==================================================
 //= rAthena Dev Team
 //===== Last Updated: ========================================
-//= 20221216
+//= 20241109
 //===== Description: =========================================
 //= Explanation of the status.yml file and structure.
 //============================================================
@@ -285,6 +285,11 @@ EndOnEnd: List of status that will end when the status becomes inactive.
 
 ---------------------------------------
 
+Script:	Script to execute, when starting the status change. When this is set all battle relevant status will be recalculated, after
+		executing the script. Scripted status changes can only be started on players.
+
+---------------------------------------
+
 Notes:
 
 By default, statuses are 'Buff' (those that aren't explicitely given the 'Debuff' flag) which are removable by 'map_quit' in combination with 'battle_config.debuff_on_logout'.

+ 71 - 4
src/map/status.cpp

@@ -4092,11 +4092,21 @@ int status_calc_pc_sub(map_session_data* sd, uint8 opt)
 		current_equip_opt_index = -1;
 	}
 
-	if (sc->count && sc->getSCE(SC_ITEMSCRIPT)) {
-		std::shared_ptr<item_data> data = item_db.find(sc->getSCE(SC_ITEMSCRIPT)->val1);
+	if (sc->count){
+		if( status_change_entry* sce = sc->getSCE(SC_ITEMSCRIPT); sce != nullptr ){
+			std::shared_ptr<item_data> data = item_db.find(sc->getSCE(SC_ITEMSCRIPT)->val1);
 
-		if (data && data->script)
-			run_script(data->script, 0, sd->bl.id, 0);
+			if (data && data->script)
+				run_script(data->script, 0, sd->bl.id, 0);
+		}
+
+		for( sc_type type = SC_NONE; type < SC_MAX; type = static_cast<sc_type>( type + 1 ) ){
+			if( status_change_entry* sce = sc->getSCE( type ); sce != nullptr ){
+				if( std::shared_ptr<s_status_change_db> scdb = status_db.find( type ); scdb != nullptr && scdb->script != nullptr ){
+					run_script( scdb->script, 0, sd->bl.id, 0 );
+				}
+			}
+		}
 	}
 
 	pc_bonus_script(sd);
@@ -10181,6 +10191,12 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 	if( !sc )
 		return 0; // Unable to receive status changes
 
+	// Scripted status changes only work for players for the time being
+	if( scdb->script != nullptr && bl->type != BL_PC ){
+		ShowError( "status_change_start: Failed to start the scripted status change %d on a non player.\n", type );
+		return 0;
+	}
+
 	if( bl->type != BL_NPC && status_isdead(*bl) && ( type != SC_NOCHAT && type != SC_JAILED ) ) // SC_NOCHAT and SC_JAILED should work even on dead characters
 		return 0;
 
@@ -15751,6 +15767,33 @@ int16 AttributeDatabase::getAttribute(uint16 level, uint16 atk_ele, uint16 def_e
 	return this->attr_fix_table[level-1][atk_ele][def_ele];
 }
 
+s_status_change_db::s_status_change_db(){
+	this->type = SC_NONE;
+	this->icon = EFST_BLANK;
+	this->state = {};
+	this->calc_flag = {};
+	this->opt1 = OPT1_NONE;
+	this->opt2 = OPT2_NONE;
+	this->opt3 = OPT3_NORMAL;
+	this->look = OPTION_NOTHING;
+	this->flag = {};
+	this->skill_id = 0;
+	this->endonstart = {};
+	this->fail = {};
+	this->endreturn = {};
+	this->endonend = {};
+	this->min_duration = 1;
+	this->min_rate = 0;
+	this->script = nullptr;
+}
+
+s_status_change_db::~s_status_change_db(){
+	if( this->script != nullptr ){
+		script_free_code( this->script );
+		this->script = nullptr;
+	}
+}
+
 const std::string StatusDatabase::getDefaultLocation() {
 	return std::string(db_path) + "/status.yml";
 }
@@ -16232,6 +16275,25 @@ uint64 StatusDatabase::parseBodyNode(const ryml::NodeRef& node) {
 		}
 	}
 
+	if( this->nodeExists( node, "Script" ) ){
+		std::string script;
+
+		if( !this->asString( node, "Script", script ) ){
+			return 0;
+		}
+
+		if( status->script ){
+			script_free_code( status->script );
+			status->script = nullptr;
+		}
+
+		status->script = parse_script( script.c_str(), this->getCurrentFile().c_str(), this->getLineNumber(node["Script"]), SCRIPT_IGNORE_EXTERNAL_BRACKETS );
+	}else{
+		if( !exists ){
+			status->script = nullptr;
+		}
+	}
+
 	if (!exists) {
 		this->put(status_id, status);
 	}
@@ -16245,6 +16307,11 @@ void StatusDatabase::loadingFinished(){
 	for( auto& entry : *this ){
 		auto& status = entry.second;
 
+		if( status->script != nullptr ){
+			// Maybe some have already been set in the database, but just to be sure, trigger recalculation for everything battle relevant
+			status->calc_flag |= this->SCB_BATTLE;
+		}
+
 		if (status->type == SC_HALLUCINATION && !battle_config.display_hallucination) // Disable Hallucination.
 			status->icon = EFST_BLANK;
 

+ 5 - 2
src/map/status.hpp

@@ -3135,7 +3135,6 @@ struct s_status_change_db {
 	uint32 opt3;						///< OPT3_
 	uint32 look;						///< OPTION_ Changelook
 	std::bitset<SCF_MAX> flag;			///< SCF_ Flags, enum e_status_change_flag
-	bool display;						///< Display status effect/icon (for certain state)
 	uint16 skill_id;					///< Associated skill for (addeff) duration lookups
 	std::vector<sc_type> endonstart;	///< List of SC that will be ended when this SC is activated
 	std::vector<sc_type> fail;			///< List of SC that causing this SC cannot be activated
@@ -3143,11 +3142,15 @@ struct s_status_change_db {
 	std::vector<sc_type> endonend;		///< List of SC that will be ended when this SC ends
 	t_tick min_duration;				///< Minimum duration effect (after all status reduction)
 	uint16 min_rate;					///< Minimum rate to be applied (after all status reduction)
+	struct script_code* script;			///< Script to execute, when starting the status change.
+
+	s_status_change_db();
+	~s_status_change_db();
 };
 
 class StatusDatabase : public TypesafeCachedYamlDatabase<uint16, s_status_change_db> {
 public:
-	StatusDatabase() : TypesafeCachedYamlDatabase("STATUS_DB", 3) {
+	StatusDatabase() : TypesafeCachedYamlDatabase("STATUS_DB", 4, 3) {
 		// All except BASE and extra flags.
 		SCB_BATTLE.set();
 		SCB_BATTLE.reset(SCB_BASE);