Useful Scripting Resources

Maps and the art of mapmaking.

Moderator: Moderators

Othmaar
Leprechaun
Leprechaun
Posts: 22
Joined: 27 Sep 2006
Location: Oslo, Norway

Useful Scripting Resources

Postby Othmaar » Sep 28 2006, 2:43

Hello all!
My first topic in this forum. I start by kissing everyones ass. Below you find a Generic MapScript that contain useful data for veterans and newbies alike, and a couple of nice-to-have functions. Lots of commenting for guidance. Newbies should be able to learn a little by studying the code. At least after memorizing that f.... manual ;)

Code: Select all

--****************************************************
-- Heroes of Might and Magic V v1.3 generic MapScript
-- By Othmaar, September 2006 for file version 1.3
--****************************************************

-- Disclaimer: I do not <blah bla insert lots of legal stuff> so my ass is covered
-- Copyright and stuff: Nival and Ubisoft <bla blah insert lots and lots and lots of
-- LOTS of legal stuff> so my ass is really really covered

-- A thx to folks at Elrath.com ... and my mother, and all my friends and ... <goes on and on>

-- *****
-- DATA
-- *****

-- These arrays contain game data neatly compiled for your convenience
-- So you can use ID to find names, types/class for heroes and creatures.

-- The arrTownTypeNames corresponds to the game's Town Type IDs +1
-- Had to increase by 1 because LUA starts array indexes at 1
arrTownTypeNames = {'Haven', 'Sylvan', 'Academy', 'Dungeon', 'Necropolis', 'Inferno'};

-- The arrHeroClassNames use the same index as the Town Types.
arrHeroClassNames = {'Knight', 'Ranger', 'Wizard', 'Sorcerer', 'Necromancer', 'Demon Lord'};

-- arrHeroInternalNames are the ones used in scripts
arrHeroInternalNames = {'Axel', 'Beatrice', 'Brem', 'Christian', 'Freyda', 'Giar', 'Glen', 'Godric', 'Isabell', 'Maeve', 'Mardigo', 'Nathaniel', 'Nicolai', 'Orrin', 'Sarge', 'Ving', 'Diraya', 'Elleshar', 'Ergar', 'Gillion', 'Heam', 'Ildar', 'Itil', 'Linaas', 'Metlirn', 'Nadaur', 'Ossir', 'Astral', 'Cyrus', 'Faiz', 'Havez', 'Isher', 'Maahir', 'Nur', 'Razzak', 'Sufi', 'Tan', 'Timerkhan', 'Zehir', 'Almegir', 'Dalom', 'Eruina', 'Ferigl', 'Inagost', 'Kelodin', 'Menel', 'Ohtar', 'Ohtarig', 'Raelag', 'Segref', 'Urunir', 'Arberrar', 'Berein', 'Effig', 'Gles', 'Muscip', 'Nemor', 'Nikolay', 'Pelt', 'Straker', 'Tamika', 'Agrael', 'Biara', 'Calid', 'Deleb', 'Efion', 'Erasia', 'Gamor', 'Grok', 'Guarg', 'Jazaz', 'Marder', 'Nymus', 'Oddrema', 'Sovereign', 'Veyer'};

-- arrHeroGameNames are the ones the players can see
arrHeroGameNames = {'Freyda', 'Beatrice', 'Rutger', 'Vittorio', 'UNKNOWN', 'Giar', 'Glen', 'Godric', 'Isabel', 'Maeve', 'Laszlo', 'Ellaine', 'Nicolai', 'Dougal', 'Klaus', 'Irina', 'Dirael', 'Vinrael', 'Ergar', 'Gilraen', 'Findan', 'Alaron', 'Ylthin', 'Wyngaal', 'Anwen', 'Talanar', 'Ossir', 'Nur', 'Cyrus', 'Faiz', 'Havez', 'Razzak', 'Maahir', 'Nathir', 'Narxes', 'Jhora', 'Galib', 'Temkhan', 'Zehir', 'Yrbeth', 'Lethos', 'Eruina', 'Sorgal', 'Sinitar', 'Shadya', 'Kythra', 'Ohtar', 'Vayshan', 'Raelag', 'Segref', 'Yrwanna', 'Zoltan', 'Markal', 'Raven', 'Kaspar', 'Naadir', 'Deirdre', 'Nicolai', 'Vladimir', 'Orson', 'Lucretia', 'Agrael', 'Biara', 'Grawl', 'Deleb', 'Alastor', 'Erasial', 'Gamor', 'Grok', 'Guarg', 'Nebiros', 'Marbas', 'Nymus', 'Jezebeth', 'Sovereign', 'Veyer'};

-- arrHeroTypes contain one entry for each hero and corresponds
-- to the arrTownTypeNames and arrHeroClassNames
arrHeroTypes = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6};


-- arrUnitTypeNames is like TownTypeNames but with an added 'Neutrals'
-- to make the Neutral unitnames available for scripts
arrUnitTypeNames = {'Haven', 'Sylvan', 'Academy', 'Dungeon', 'Necropolis', 'Inferno', 'Neutrals'};

-- The arrUnitIndex is the game's ID for the first tier creature sorted
-- to correspond to the arrUnitTypeNames
arrUnitIndex = {1, 43, 57, 71, 29, 15, 85};

-- arrUnitTypeTiers contains the total number of different creatures for each type
arrUnitTypeTiers = {14, 14, 14, 14, 14, 14, 6}

-- creature tiers in the order of the arrTownTypeNames array (see above).
-- Index corresponds to the game's Creature IDs
arrUnitNames = {'Peasant', 'Conscript', 'Archer', 'Marksman', 'Footman', 'Squire', 'Griffin', 'Imperial Griffin', 'Priest', 'Inquisitor', 'Cavalier', 'Paladin', 'Angel', 'Archangel', 'Imp', 'Familiar', 'Horned Demon', 'Overseer Demon', 'Hell Hound', 'Cerberus', 'Succubus', 'Succubus Mistress', 'Hell Charger', 'Nightmare', 'Pit Fiend', 'Pit Lord', 'Devil', 'Archdevil', 'Skeleton', 'Skeleton Archer', 'Zombie', 'Plague Zombie', 'Ghost', 'Spectre', 'Vampire', 'Vampire Lord', 'Lich', 'Archlich', 'Wight', 'Wraith', 'Bone Dragon', 'Spectral Dragon', 'Pixie', 'Sprite', 'Blade Dancer', 'War Dancer', 'Hunter', 'Master Hunter', 'Druid', 'Druid Elder', 'Unicorn', 'Silver Unicorn', 'Treeant', 'Ancient Treeant', 'Green Dragon', 'Emerald Dragon', 'Gremlin', 'Master Gremlin', 'Stone Gargoyle', 'Obsidian Gargoyle', 'Iron Golem', 'Steel Golem', 'Mage', 'Archmage', 'Djinn', 'Djinn Sultan', 'Rakshasa Rani', 'Rakshasa Raja', 'Colossus', 'Titan', 'Scout', 'Assassin', 'Blood Maiden', 'Blood Fury', 'Minotaur', 'Minotaur Guard', 'Dark Rider', 'Grim Raider', 'Hydra', 'Deep Hydra', 'Shadow Witch', 'Shadow Matriarch', 'Shadow Dragon', 'Black Dragon', 'Fire Elemental', 'Water Elemental', 'Earth Elemental', 'Air Elemental', 'Black Knight', 'Phoenix'};

-- Default amounts of creatures for each tier
-- for the fnSetHeroCreatures function below
defaultamounts = {0, 100, 0, 10, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0};

-- **********
-- FUNCTIONS
-- **********
-- The function includes debug print commands

-- fnGetHeroData takes a hero's InternalName as argument
-- and returns an array of:{heroID, internalname, gamename, typeID}
-- * heroID is just the index of the arrHeroInternalNames array
-- * internalname is the heroname the player never sees.
-- * gamename is the heroname the player sees.
-- * typeID is the ID for the hero's type and class (see above).
function fnGetHeroData(internalname)
   local herodata;
   local heroID;
   local numberofheroes;
   
   print('Gathering herodata for ' .. internalname .. '(internalname)');
   numberofheroes = length(arrHeroInternalNames);
   
   for heroID = 1, numberofheroes, 1 do
      if internalname == arrHeroInternalNames[heroID] then
         herodata = {heroID, internalname, arrHeroGameNames[heroID], arrHeroTypes[heroID]};
         break;
      end;
   end;
   return herodata;
end;

-- Gets the herodata for the first hero
function fnGetFirstHero(player)
   local heroes;
   local herodata;
   local hero;
   
   heroes = GetPlayerHeroes(player);
   herodata = fnGetHeroData(heroes[0]);
   return herodata;
end;

-- The function fnHeroSetArmy is slightly complicated.
-- There are two arguments, each of which is an array.
-- * herodata corresponds to the result of the
--   fnGetHeroData function detailed above.
-- * amountarray is an array of the target amount of
--   creatures for each tier based on the race supplied
--   in the argument herodata.
--
-- The function does not return any values, but it is
-- not 100% robust. Errors can be provoked if not used.
-- correctly.
--
-- Basically the function iterates through all the armies
-- of the specified hero and removes/adds creatures
-- according to the amountarray. However, if a remove
-- operation would result in no armies left for the hero,
-- the game will leave one creature of the type last removed.
-- Therefore there is a built in tracking system that removes
-- any leftovers. In theory this should never happen more than
-- once for each function call, and the rest should never be
-- more that 1 unit of the creature.
function fnHeroSetArmy(herodata, amountarray)
   local hero;
   local herogamename;
   local type;
   local creature;
   local creaturename;
   local tier;
   local lasttier;
   local amount;
   local amounttarget;
   local amounttoremove;
   local amounttoadd;
   local restcreature;
   
   hero = herodata[2];
   herogamename = herodata[3];
   type = herodata[4];
   creature = arrUnitIndex[type];
   lasttier = creature+arrUnitTypeTiers[type]-1;
   
   if not amountarray then
      amountarray = defaultamounts;
   end;
   
   print('Beginning to set armies for: ' .. herogamename);
   
   for tier = creature, lasttier, 1 do
      amount = GetHeroCreatures(hero, tier) or 0;
      creaturename = arrUnitNames[tier];
      amounttarget = amountarray[tier] or 0;
      amounttoremove = amount-amounttarget;
      amounttoadd = amounttarget-amount;
      if amount>0 then
         print('Hero has ' .. amount .. ' ' .. creaturename);
         print('Target amount is: ' .. amounttarget);
      end;
      
      if amounttoremove>0 and amount>0 then
         print('Removing ' .. amounttoremove .. ' ' .. creaturename);
         RemoveHeroCreatures(hero, tier, amounttoremove);
         -- If all creatures have been attempted removed
         -- one will always be left. We have to track it
         -- and remove it in the end. It should never
         -- happen more than once per function call (in theory:p).
         amount = GetHeroCreatures(hero, tier) or 0;
         if amount>amounttarget then
            print('Leftover detected: ' .. creaturename .. '!');
            restcreature = tier;
         end;
      end;
      
      if amounttoadd>0 then
         print('Adding ' .. amounttoadd .. ' ' .. arrUnitNames[tier]);
         AddHeroCreatures(hero, tier, amounttoadd);
      end;
   end;
   if restcreature then
      -- Note that if one tries to remove all creatures from
      -- the hero, the restcreature will reimain.
      print('Removing the leftover ' .. arrUnitNames[restcreature]);
      RemoveHeroCreatures(hero, restcreature, 1);
   end;
end;

-- fnInitialize: This function is mostly meant to
-- show how all the above code can be used. Actually
-- its my main debug code :)
function fnInitialize()
   local player;
   local herodata;
   local heroID;
   local internalname;
   local gamename;
   local type;
   local typename;
   local class;
   local firstunit;
   local unitname;
   
   player = GetCurrentPlayer();
   herodata = fnGetFirstHero(player);
   heroID = herodata[1];
   internalname = herodata[2];
   gamename = herodata[3];
   type = herodata[4];
   typename = arrTownTypeNames[type];
   class = arrHeroClassNames[type];
   firstunit = arrUnitIndex[type];
   unitname = arrUnitNames[firstunit];
   print('Current player: ' .. player);
   print('Town Type: ' .. typename);
   print('Hero InternalName: ' .. internalname);
   print('Hero GameName: ' .. gamename);
   print('Class: ' .. class);
   fnHeroSetArmy(herodata, defaultamounts);
end;

fnInitialize();

-- Lots of cool code you made goes here :)
O.

User avatar
alavris
Scout
Scout
Posts: 164
Joined: 06 Jan 2006
Location: Poland
Contact:

Postby alavris » Sep 28 2006, 8:38

Thanks! Any piece of code in this forum is appreciated :)
.

meows
Peasant
Peasant
Posts: 75
Joined: 11 Oct 2006

Postby meows » Oct 26 2006, 7:05

Thanks much for the code. I keep checking to see if anyone else has
blessed us with usefull snips of additional code. :)

User avatar
fiur
Pixie
Pixie
Posts: 129
Joined: 06 Jan 2006

Postby fiur » Oct 26 2006, 7:31

great work Othmaar !!!


Return to “Mapmaking Guild”

Who is online

Users browsing this forum: No registered users and 2 guests