Jump to content
  • Announcements

    • Xmat

      Pravidlo pro postování v TTT

      Do sekce Tipy, triky, tutoriály nepatří žádné dotazy.   Postujte sem vaše návody, tipy a různé další věci jež uznáte za vhodné sdělit zdejšímu osazenstvu, ale veškeré dotazy směřujte do sekce Všeobecná diskuse.
    • Replik

      Seznam návodů a důležitých témat v této sekci

      Pro lepší přehlednost jsem vytvořil tento seznam, který vás, méně zkušené, lépe provede touto sekcí. Věřím, že zde najdete, co hledáte. Vypsané jsou návody, které jsou oficiálně uznané jako návody. Běžné diskuze, které neposkytují postupy a rady zvěřejněny nejsou.   Instalace vlastního MaNGOS Serveru Díky těmto návodům budete (měli by jste být) schopni vytvořit a následně spustit váš vlastní server. Nastavení je pro verze s i bez datadisku.   Instalace MaNGOS Serveru (bez datadisku TBC) - Autor Benny Instalace MaNGOS Serveru (s datadiskem TBC) - Autor Malfik Instalace MaNGOS Serveru v prostředí Linux - Autor charlie Instalace MaNGOS Serveru v prostředí Linux - Autor kupkoid   Chyby a jejich řešení při přihlašování k serveru - Autor Cybe   Zálohování uživatelských dat   Dávkový soubor (BAT soubor) pro vytvoření SQL záloh - Autor Replik   Kompilování - tvoření vlastních release (revizí)   Tvorba kompilací pro Win32 (MangoScript) - Autor bLuma   Ostatní - těžko zařaditelné, ale neznamená to, že nejsou dobré   VIP Systém - Autor charlie Tvorba Webových stránek pro MaNGOS - Autor zahuba Tvorba teleportačních NPC (MangoScript) - Autor Replik Registrační web (původně předělaná SPORA) Funkční pro Antrix i MaNGOS - Autor Replik Nastavení a spuštění Minimanager pro MaNGOS - Autor BlackMartin Nastavení MaNGOS Website - Autor Artorius   Samozřejmě jsou zde i jiné návody, ale tyto jsou nejvíce používané, proto věřím, že vám budou nápomocné. Tuto sekci budeme upravovat podle potřeby. Pokud by jste něco nenašli nebo si nevěděli rady, hledejte na fóru a teprve potom založte vlastní topik. Pokud nějaký autor vytvoří kvalitní návod a chtěl by ho zveřejnit i v tomto seznamu, doporučuji, aby mi napsal zprávu skrze PM.   Díky a přeji hezký den na WoWResource   Replik
    • Aristo

      Příspěvky tam, kde nemají co dělat

      Dodržujte zákaz přispívání do topiků s repaky pokud si to zakladatelé nepřejí!! Opakované psaní příspěvků bude trestáno warnem.
    • Aristo

      Používání spoilerů

      Poslední dobou má většina uživatelů fora zvláštní nutkání postovat extrémně dlouhé texty nebo kódy, které zabírají v nejedenom případu i 80% obsahu celé stránky a hodně tak zvedají nepřehlednost v topiku. Chtěl bych všechny uživatele požádat, aby při postování citací, jakýchkoliv kódů, errorů, atp... delších než 30 řádků používali funkci spoileru.   Funkci vyvoláte příkazem [spoiler] text [/spoiler]   Ukázka:  
Sign in to follow this  
Hookyns

[Custom spell] Jak na to!

Recommended Posts

Zdravím,

před pár dny jsem řešil problém s tím, jak udělat vlastní spell následně nascriptovaný v Core. Bohužel mi zde nebyl schopen nikdo pomoci, tak jsem byl odkázán sám na sebe, jako vždy. Řešení jsem nenalezl ani na google na zahraničních fórech/portálech a tak jsem procházel core a snažil jsem se něco najít. Našel jsem nějaké věci rozprášené po celém core a zjišťoval jsem co a jak.

 

Tento způsob vyžaduje editaci klienta.

 

Pojďme tedy k věci.

 

Co je potřeba?

  • Nějaký DBC editor (například MyDBCEditor)
  • MPQ editor (například Ladik's MPQ Editor)
  • PNG <-> BLP converter (pokud tedy chcete vlastní ikonu u spellu - ingame)
  • Nějaký edtor audia (pokud chcete ke spellu vlastní zvuk)
  • Samozřejmě funkční server (včetně source) s databází, apod.
  • Nějaké všeobecné znalosti DBC, databáze, serveru -> C++
  • Znalosti editace DBC a klienta tj. práce s DBC editorem a MPQ editorem
  • Hlava a trpělivost

Postup

1)
Vy
tvoření patche

Nebudu zde popisovat, jak se patch vytváří a jak se s MPQ editorem zachází.

Vytvořte
nový .MPQ
soubor a pojmenujte jej "
patch-X
".

V patchy vytvořte složku
DBFilesClient
, do které na konci nahrajete všechny vámi upravené DBC soubory.

 

2)
Editace
Spell.dbc

Zde je nutné vytvořit
nový záznam
(řádek), s ID větší než poslední ID v řadě. Já jsem začínal od 100 000.

Vytvořil jsem PDF s menším přehledem polí, které budeme potřebovat.

Vytvořte si tedy nový záznam a vyplňte sloupce podle představy vašeho spellu.

 

Spell Icon

Pokud tedy chcete vlastní ikonku u spellu, musíte vytvořit obrázek 24x24px v PNG, překonvertovat na BLP a tento obrázek nahrát do patche, kam si jej umístíte je jen na vás.

Poté otevřeme SpellIcon.dbc a vložíme nový záznam. První sloupec je ID Icony (zvolte vlastní) a druhý je cesta k našemu obrázku (od kořenového adresáře patche).

!! POZOR !! na konci cesty nebude přípona, bude to tedy cca "
custom\images\mojeico
".

 

3)
Inicializace spellu

V core upravíme soubor
SpellMgr.cpp.

Půjdeme na
řádek cca 2900
, kde by měly být
case od switche

Můžete zkusit hledat
case 72446: // Mark of the Fallen Champion (Deathbringer Saurfang)

Přidejte zde další case s vašim spellID ->
case: #spellid
Stačí jen tohle na jeden řádek, nemusí zde být break;

 

4)
Vytvoření scriptu
pro spell

Tuhle část nemám plně zmapovanou, bude to tedy na vás si to doplnit.

Možná se vám nebude pozdávat můj syntax, jsem webdeveloper a C++ jsem se nikdy neučil, jsem zvyklý na PHP syntax, tak to píši tak.

 

V jádře je příkladový script, a ten vypadá následovně
:
class spell_ex_5581 : public SpellScriptLoader {
public:
spell_ex_5581() : SpellScriptLoader("spell_ex_5581") { }

class spell_ex_5581SpellScript : public SpellScript {
// initialize script, this macro does compile time check for type of the function - prevents possible issues
// if you have assigned wrong type of function to a hook you'll receive type conversion error during build
// this line is required, otherwise you'll get XXXHandlerFunction - identifier not found errors
PrepareSpellScript(spell_ex_5581SpellScript);

std::string localVariable;
char* localVariable2;

// function called on server startup
// checks if script has data required for it to work
bool Validate(SpellInfo const* /*spellEntry*/) {
// check if spellid 70522 exists in dbc, we will trigger it later
if (!sSpellMgr->GetSpellInfo(SPELL_TRIGGERED))
return false;
return true;
}

// function called just after script is added to spell
// we initialize local variables if needed
bool Load() {
localVariable = "we're using local variable";
localVariable2 = new char();
return true;
// return false - script will be immediately removed from the spell
// for example - we don't want this script to be executed on a creature
// if (GetCaster()->GetTypeID() != TYPEID_PLAYER)
// return false;
}

// function called just before script delete
// we free allocated memory
void Unload() {
delete localVariable2;
}

void HandleBeforeCast() {
// this hook is executed before anything about casting the spell is done
// after this hook is executed all the machinery starts
sLog->outInfo(LOG_FILTER_GENERAL, "Caster just finished preparing the spell (cast bar has expired)");
}

void HandleOnCast() {
// cast is validated and spell targets are selected at this moment
// this is a last place when the spell can be safely interrupted
sLog->outInfo(LOG_FILTER_GENERAL, "Spell is about to do take reagents, power, launch missile, do visuals and instant spell effects");
}

void HandleAfterCast() {
sLog->outInfo(LOG_FILTER_GENERAL, "All immediate Actions for the spell are finished now");
// this is a safe for triggering additional effects for a spell without interfering
// with visuals or with other effects of the spell
//GetCaster()->CastSpell(target, SPELL_TRIGGERED, true);
}

SpellCastResult CheckRequirement() {
// in this hook you can add additional requirements for spell caster (and throw a client error if reqs're not passed)
// in this case we're disallowing to select non-player as a target of the spell
//if (!GetExplTargetUnit() || GetExplTargetUnit()->ToPlayer())
//return SPELL_FAILED_BAD_TARGETS;
return SPELL_CAST_OK;
}


void HandleDummyLaunch(SpellEffIndex /*effIndex*/) {
sLog->outInfo(LOG_FILTER_GENERAL, "Spell %u with SPELL_EFFECT_DUMMY is just launched!", GetSpellInfo()->Id);
}

void HandleDummyLaunchTarget(SpellEffIndex /*effIndex*/) {
uint64 targetGUID = 0;
if (Unit* unitTarget = GetHitUnit())
targetGUID = unitTarget->GetGUID();
// we're handling SPELL_EFFECT_DUMMY in effIndex 0 here
sLog->outInfo(LOG_FILTER_GENERAL, "Spell %u with SPELL_EFFECT_DUMMY is just launched at it's target: " UI64FMTD "!", GetSpellInfo()->Id, targetGUID);
}

void HandleDummyHit(SpellEffIndex /*effIndex*/) {
sLog->outInfo(LOG_FILTER_GENERAL, "Spell %u with SPELL_EFFECT_DUMMY has hit!", GetSpellInfo()->Id);
}

void HandleDummyHitTarget(SpellEffIndex /*effIndex*/) {
sLog->outInfo(LOG_FILTER_GENERAL, "SPELL_EFFECT_DUMMY is hits it's target!");
// make caster cast a spell on a unit target of effect
if (Unit* target = GetHitUnit())
GetCaster()->CastSpell(target, SPELL_TRIGGERED, true);
}

void HandleBeforeHit() {
sLog->outInfo(LOG_FILTER_GENERAL, "Spell is about to hit target!");
}

void HandleOnHit() {
sLog->outInfo(LOG_FILTER_GENERAL, "Spell just hit target!");
}

void HandleAfterHit() {
sLog->outInfo(LOG_FILTER_GENERAL, "Spell just finished hitting target!");
}

void FilterTargets(std::list<unit*>& /*targetList*/) {
// usually you want this call for Area Target spells
sLog->outInfo(LOG_FILTER_GENERAL, "Spell is about to add targets from targetList to final targets!");
}

// register functions used in spell script - names of these functions do not matter
void Register() {
// we're registering our functions here
BeforeCast += SpellCastFn(spell_ex_5581SpellScript::HandleBeforeCast);
OnCast += SpellCastFn(spell_ex_5581SpellScript::HandleOnCast);
AfterCast += SpellCastFn(spell_ex_5581SpellScript::HandleAfterCast);
OnCheckCast += SpellCheckCastFn(spell_ex_5581SpellScript::CheckRequirement);
// function HandleDummy will be called when spell is launched, independant from targets selected for spell, just before default effect 0 launch handler
OnEffectLaunch += SpellEffectFn(spell_ex_5581SpellScript::HandleDummyLaunch, EFFECT_0, SPELL_EFFECT_DUMMY);
// function HandleDummy will be called when spell is launched at target, just before default effect 0 launch at target handler
OnEffectLaunchTarget += SpellEffectFn(spell_ex_5581SpellScript::HandleDummyLaunchTarget, EFFECT_0, SPELL_EFFECT_DUMMY);
// function HandleDummy will be called when spell hits it's destination, independant from targets selected for spell, just before default effect 0 hit handler
OnEffectHit += SpellEffectFn(spell_ex_5581SpellScript::HandleDummyHit, EFFECT_0, SPELL_EFFECT_DUMMY);
// function HandleDummy will be called when unit is hit by spell, just before default effect 0 hit target handler
OnEffectHitTarget += SpellEffectFn(spell_ex_5581SpellScript::HandleDummyHitTarget, EFFECT_0, SPELL_EFFECT_DUMMY);
// this will prompt an error on startup because effect 0 of spell 49375 is set to SPELL_EFFECT_DUMMY, not SPELL_EFFECT_APPLY_AURA
//OnEffectHitTarget += SpellEffectFn(spell_gen_49375SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_APPLY_AURA);
// this will make HandleDummy function to be called on first != 0 effect of spell 49375
//OnEffectHitTarget += SpellEffectFn(spell_gen_49375SpellScript::HandleDummy, EFFECT_FIRST_FOUND, SPELL_EFFECT_ANY);
// this will make HandleDummy function to be called on all != 0 effect of spell 49375
//OnEffectHitTarget += SpellEffectFn(spell_gen_49375SpellScript::HandleDummy, EFFECT_ALL, SPELL_EFFECT_ANY);
// bind handler to BeforeHit event of the spell
BeforeHit += SpellHitFn(spell_ex_5581SpellScript::HandleBeforeHit);
// bind handler to OnHit event of the spell
OnHit += SpellHitFn(spell_ex_5581SpellScript::HandleOnHit);
// bind handler to AfterHit event of the spell
AfterHit += SpellHitFn(spell_ex_5581SpellScript::HandleAfterHit);
// bind handler to OnUnitTargetSelect event of the spell
//OnUnitTargetSelect += SpellUnitTargetFn(spell_ex_5581SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_CASTER);
}
};

// Vytvoříme spell
SpellScript* GetSpellScript() const {
return new spell_ex_5581SpellScript();
}
}

 

Trochu zjednodušeně:
class spell_priklad : public SpellScriptLoader {
public:
	spell_priklad() : SpellScriptLoader("spell_priklad") { }

	 class spell_prikladSpellScript : public SpellScript {
		PrepareSpellScript(spell_prikladSpellScript);

		// Tato funkce se má údájně volat při přidání spellu na postavu
		bool Load() {
			// return false - script will be immediately removed from the spell
			// for example - we don't want this script to be executed on a creature
			// if (GetCaster()->GetTypeID() != TYPEID_PLAYER)
			// return false;
		}

		// Tato funkce se má údajně volat před odebráním spellu
		void Unload() {
			// akce
		}

		// Požadavky pro spell...
		SpellCastResult CheckRequirement() {
			// in this hook you can add additional requirements for spell caster (and throw a client error if reqs're not passed)
			// in this case we're disallowing to select non-player as a target of the spell
			//if (!GetExplTargetUnit() || GetExplTargetUnit()->ToPlayer())
			//return SPELL_FAILED_BAD_TARGETS;
			return SPELL_CAST_OK;
		}

		// Tahle funkce se volá po vycastění spellu
		void HandleAfterCast() {
			// Nějaká akce třeba teleportace, snížení HP => DMG, apod.
		}

		// Zaregistrujeme funkce, které budeme chtít provádět při použití spellu
		void Register() {
			AfterCast += SpellCastFn(spell_prikladSpellScript::HandleAfterCast);
			OnCheckCast += SpellCheckCastFn(spell_prikladSpellScript::CheckRequirement);
		}
	};

	// Vytvoříme spell
	SpellScript* GetSpellScript() const {
		return new spell_prikladSpellScript();
	}
};

// Musíme přidat ještě "odkaz" do ScriptLoaderu
void AddSC_spell_priklad() {
new spell_priklad();
}

 

5)
Vložit záznam
do databáze

Aby se náš script mohl nějak zavolat, musíme někde určit, jaký script k našemu spellu patří.

To uděláme ve world DB v tabulce
spell_script_names
, kde
první sloupec je ID
našeho spellu a
druhý je naše ScriptName

 

 

Po dokončení všech kroků jen zavřete otevřené MPQ a vložíte jej do WoWDir\Data, zkompilujete core a zapnete server (v serverových DBC musí být také námi editované .dbc soubory).

Ingame jen dáte .learn NAŠE_SPELL_ID a můžete si (snad) užívat kýženého výsledku.

 

EDIT: Reaguji na příspěvek od uživatele frytiks, takže odpověď pro všechny

Tam kde zmiňuji Spell ID (tedy ID našeho spellu), myslím tím ID, které jste zadali v DBC, což je ID spellu.

 

Původní topic:

Zdravím,

snažím se vytvořit vlastní spell, ale potřeboval bych radu od zkušenějších.

 

Chci vytvořit spell, který mě "teleportuje" na vybrané místo, tím vybraným místem myslím to, že po kliku na spell se objeví ten zelený kruh, abych vybral místo (např. Force of Nature), a po potvrzení místa se provede C++ script na teleport.

 

Co jsem zatím pochopil, spell se musí vytvořit ve spell.dbc, to jsem udělal, vytvořil jsem si i vlastní ikonu pro spell a ingame to funguje, spell jsem se normálně naučil, odebírá požadované množství many a castí se požadovanou dobu.

Ale jak tam mám přidat ten výběr pozice a hlavně jak to ve výsledku navázat na core, abych mohl nascriptovat akci?

 

V core mám vytvořený soubor se scriptem pro spell podle příkladu, který je v core obsažen, ale nějak nechápu, jak si server daný script přiřadí k tomu spellu.

Ano, udává se tam scriptname a v DB jsem našel spell_script_names, kde se přiřazují ID spellu ke scriptname, ale nezdá se mi, že by se script při castu spustil.

 

Proto píši tento topic a žádám někoho o radu.

3.3.5a (nejnovější rev)

 

 

EDIT:

Tak už jsem přišel na to, jak napojit vytvořený spell ze spell.dbc na C++ script tak, aby se po castingu provedl.

Teď ještě jak udělat ten targeting (výběr místa tím zeleným kruhem).

 

Edited by Hookyns
  • Upvote 4

Share this post


Link to post
Share on other sites

Promazáno na žádost(topic byl transformován z dotazu na návod), přesunuto do sekce TTT.

 

EDIT: Vyvěšeno. :)

Edited by Wolf Officious

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

×