EQEmulator Forums

EQEmulator Forums (https://www.eqemulator.org/forums/index.php)
-   Development::Server Code Submissions (https://www.eqemulator.org/forums/forumdisplay.php?f=669)
-   -   Spell Blocking (now with Intra-Zone restrictions!) (https://www.eqemulator.org/forums/showthread.php?t=26051)

AndMetal 08-29-2008 04:40 PM

Spell Blocking (now with Intra-Zone restrictions!)
 
Apparently in my original post, I just put %i instead of %.3f for the coordinates, so they weren't being executed correctly in the query. Here is the final code:

In zone/zonedb.h, after
Code:

  47 struct PetRecord {
  48        uint32 npc_type;
  49        bool temporary;
  50 };

add
Code:

// Spell Blocking by AndMetal
struct SpellBlocking_Struct{
        sint32        id;                        // -1 = database error, 0 = no matches in the database, 1+ = id in the blocked_spells table
        int8        type;                // 0 = not blocked, 1 = zone wide, 2 = specific coords in the zone
        char*        message;        // Message to send to the client saying the message failed, for customizability
};

and a little farther down, after
Code:

  277        /*
  278          * Misc stuff.
  279          * PLEASE DO NOT ADD TO THIS COLLECTION OF CRAP UNLESS YOUR METHOD
  280          * REALLY HAS NO BETTER SECTION
  281          */
  282        bool    logevents(const char* accountname,int32 accountid,int8 status,const char* charname,const char* target, const char* descriptiontype, const char* description,int event_nid);
  283        void        GetEventLogs(const char* name,char* target,int32 account_id=0,int8 eventid=0,char* detail=0,char* timestamp=0, CharacterEventLog_Struct* cel=0);

add
Code:

        // Spell Blocking by AndMetal
        SpellBlocking_Struct        GetSpellBlock(int16 spell_id, uint16 zone_id, float x, float y, float z);

In zone/zonedb.cpp, after
Code:

1268 /*
 1269  solar: this is never actually called, client_process starts an async query
 1270  instead and uses GetAccountInfoForLogin_result to process it..
 1271  */
 1272 bool ZoneDatabase::GetAccountInfoForLogin(int32 account_id, sint16* admin, char* account_name, int32* lsaccountid, int8* gmspeed, bool* revoked,bool* gmhideme) {
 1273        char errbuf[MYSQL_ERRMSG_SIZE];
 1274    char *query = 0;
 1275    MYSQL_RES *result;
 1276
 1277        if (RunQuery(query, MakeAnyLenString(&query, "SELECT status, name, lsaccount_id, gmspeed, revoked, hideme FROM account WHERE id=%i", account_id), errbuf, &result)) {
 1278                safe_delete_array(query);
 1279                bool ret = GetAccountInfoForLogin_result(result, admin, account_name, lsaccountid, gmspeed, revoked,gmhideme);
 1280                mysql_free_result(result);
 1281                return ret;
 1282        }
 1283        else
 1284        {
 1285                cerr << "Error in GetAccountInfoForLogin query '" << query << "' " << errbuf << endl;
 1286                safe_delete_array(query);
 1287                return false;
 1288        }
 1289
 1290        return false;
 1291 }

add
Code:

/* Spell Blocking by AndMetal
To Do: load blocked_spells table into shared memory, should keep the database from exploding, but as-is allows for changes to take effect immediately
*/
SpellBlocking_Struct ZoneDatabase::GetSpellBlock(int16 spell_id, uint16 zone_id, float x, float y, float z) {
        // Database stuff
        char errbuf[MYSQL_ERRMSG_SIZE];
    char *query = 0;
    MYSQL_RES *result;
    MYSQL_ROW row;

        SpellBlocking_Struct blocked;
        blocked.id = 0;

        if(RunQuery(query, MakeAnyLenString(&query, "SELECT bs.id, bs.type, bs.message FROM blocked_spells bs "
                "WHERE bs.zoneid = %i "
                "AND bs.spellid = %i "
                // Create a virtual box of spell blockage, but still include NULLs
                "AND ((%.3f BETWEEN (bs.x - bs.x_diff) AND (bs.x + bs.x_diff)) OR (x IS NULL) OR (x_diff IS NULL)) "
                "AND ((%.3f BETWEEN (bs.y - bs.y_diff) AND (bs.y + bs.y_diff)) OR (y IS NULL) OR (y_diff IS NULL))"
                "AND ((%.3f BETWEEN (bs.z - bs.z_diff) AND (bs.z + bs.z_diff)) OR (z IS NULL) OR (z_diff IS NULL))"
                // Type 2s first, in case there is a message to show for that specific spot in the zone
                "ORDER BY bs.type DESC, bs.id ASC LIMIT 1", zone_id, spell_id, x, y, z), errbuf, &result)) {
                safe_delete_array(query);
                if(mysql_num_rows(result) != 0) { // Did we get anything from the query?
                        row = mysql_fetch_row(result);
                        blocked.id = atoi(row[0]);
                        blocked.type = atoi(row[1]);
                        blocked.message = row[2];
                        mysql_free_result(result);
                }
                else
                        mysql_free_result(result);
        }
        else {
                blocked.id = -1;
                cerr << "Error in GetSpellBlock query '" << query << "' " << errbuf << endl;
                safe_delete_array(query);
        }
        return blocked;
}

Lastly, in zone/spells.cpp, after
Code:

1254        // angelox start
 1255
 1256        if(IsEffectInSpell(spell_id, SE_Levitate) && !zone->CanLevitate()){
 1257                        if(IsClient()){
 1258                                if(!CastToClient()->GetGM()){
 1259                                        Message(13, "You can't levitate in this zone.");
 1260                                        return false;
 1261                                }
 1262                        }
 1263                }

add
Code:

        // Spell Blocking by AndMetal
        if(IsClient() && !CastToClient()->GetGM()) { // First the easy stuff, should pass over this if they're a regular mob
                SpellBlocking_Struct blocked = database.GetSpellBlock(spell_id, GetZoneID(), GetX(), GetY(), GetZ());
                if(blocked.id > 0) {
                        if(blocked.message != NULL)
                                Message(13, blocked.message); // Output message from the DB
                        else if(blocked.type == 2)
                                Message(13, "You can't cast this spell here. Try somewhere else."); // Default client message, hinting that it's just that part of the zone
                        else if(blocked.type == 1)
                                Message(13, "You can't cast this spell here."); //Default client message
                        return false; // Prevents the spell from casting
                }
        }

New table, blocked_spells
Code:

CREATE TABLE `blocked_spells` (
  `id` int(11) NOT NULL auto_increment,
  `spellid` mediumint(8) unsigned NOT NULL default '0',
  `type` tinyint(4) NOT NULL default '0',
  `zoneid` int(4) NOT NULL default '0',
  `x` float default NULL,
  `y` float default NULL,
  `z` float default NULL,
  `x_diff` float default NULL,
  `y_diff` float default NULL,
  `z_diff` float default NULL,
  `message` varchar(255) default NULL,
  `description` varchar(255) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM;

and a few starting values
Code:

INSERT INTO blocked_spells (spellid, type, zoneid, message, description) VALUES (1771, 1, 71, 'A voice whispers in your mind: "There are no heroes here..."', 'Prevent CoH in airplane');
INSERT INTO blocked_spells (spellid, type, zoneid, x, y, z, x_diff, y_diff, z_diff, message, description) VALUES (1771, 2, 162, 657, -325, 418.6, 44.6, 44.6, 15, 'A voice whispers in your mind: "There are no heroes here..." Try somewhere else.', 'Prevent CoH in ssratemple before Emperor\'s Room');

This time I did test out the intra-zone portion (in this case, in ssratemple), and it does prevent the casting & gives the correct messages.

Long term, it would probably be better to load the blocked_spells table into shared memory, otherwise a query is run against the database every single time a client casts a spell and doesn't have a GM flag. In addition, it might be worth exploring adding this check both at the end of the spell being cast, as well as at the beginning. Otherwise, I hope others fine this method more useful than the hard-coded blocks currently in the source.

trevius 08-29-2008 05:52 PM

Nice! I will have to do some testing on that :)

cavedude 08-30-2008 02:09 PM

I have this on TGC and it works great! A small change to the query though, CoH could still be used within Emp's throne room, this corrects that (obviously, you'd use these inserts instead of the ones above):

Code:

INSERT INTO blocked_spells (spellid, type, zoneid, message, description) VALUES (1771, 1, 71, 'A voice whispers in your mind: "There are no heroes here..."', 'Prevent CoH in airplane');
INSERT INTO blocked_spells (spellid, type, zoneid, x, y, z, x_diff, y_diff, z_diff, message, description) VALUES (1771, 2, 162, 818, -325, 418.6, 208, 143, 15, 'A voice whispers in your mind: "There are no heroes here..." Try somewhere else.', 'Prevent CoH in ssratemple before Emperor\'s Room');

PEQ will add more entries as they are needed as well :)

KLS 09-01-2008 01:50 AM

When this goes in it will be loaded into zone memory, and cleared on shutdown, like doors.

Also I changed a lot of the NULL capable fields because it kinda made loading into memory more complicated than it needed to be. If someone really wants to have it disregard the entire axis they can set the axis point to 0 and axis diff to 1000000.

trevius 09-01-2008 03:45 AM

So far, this looks really good. I was wondering if there was a way to block ALL spells with a single entry using this. Maybe something like %% or *.* could go in the field to set it for ALL spells? Basically to make an area silence effect. I already have a project for a fun idea that could really use that hehe. I was planning to make a long lasting silence debuff spell to do it, but it would be much better if it could just be done with this new feature :)

Any ideas?

KLS 09-01-2008 04:16 AM

it could probably be modified to do so, if spellid == 0 or something. Probably wouldn't be too hard to modify what I'm going to put in to do that.

ChaosSlayerZ 03-19-2010 08:18 PM

would it be possible to rather block specific spell from being casted in specific zone, to make that a specific spell can ONLY be casted in specific zone ? (rather having to block it in 300+ zones)?


All times are GMT -4. The time now is 12:20 AM.

Powered by vBulletin®, Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.