Fixed NPC_, LEVI and LEVC. Only DIAL and INFO left.

actorid
Nicolay Korslund 15 years ago
parent 514fe3795a
commit e5a64eeb5e

@ -0,0 +1,79 @@
#ifndef _ESM_LEVLISTS_H
#define _ESM_LEVLISTS_H
#include "esm_reader.hpp"
namespace ESM {
/*
* Leveled lists. Since these have identical layout, I only bothered
* to implement it once.
*
* We should later implement the ability to merge leveled lists from
* several files.
*/
struct LeveledListBase
{
enum Flags
{
AllLevels = 0x01, // Calculate from all levels <= player
// level, not just the closest below
// player.
Each = 0x02 // Select a new item each time this
// list is instantiated, instead of
// giving several identical items
}; // (used when a container has more
// than one instance of one leveled
// list.)
int flags;
unsigned char chanceNone; // Chance that none are selected (0-255?)
// Record name used to read references. Must be set before load() is
// called.
const char *recName;
struct LevelItem
{
std::string id;
short level;
};
std::vector<LevelItem> list;
void load(ESMReader &esm)
{
esm.getHNT(flags, "DATA");
esm.getHNT(chanceNone, "NNAM");
if(esm.isNextSub("INDX"))
{
int len;
esm.getHT(len);
list.resize(len);
}
else return;
// TODO: Merge with an existing lists here. This can be done
// simply by adding the lists together, making sure that they are
// sorted by level. A better way might be to exclude repeated
// items. Also, some times we don't want to merge lists, just
// overwrite. Figure out a way to give the user this option.
for(int i=0; i<list.size(); i++)
{
LevelItem &li = list[i];
li.id = esm.getHNString(recName);
esm.getHNT(li.level, "INTV");
}
}
};
struct CreatureLevList : LeveledListBase
{ CreatureLevList() { recName = "CNAM"; } };
struct ItemLevList : LeveledListBase
{ ItemLevList() { recName = "INAM"; } };
}
#endif

@ -0,0 +1,137 @@
#ifndef _ESM_NPC_H
#define _ESM_NPC_H
#include "esm_reader.hpp"
#include "loadcont.hpp"
#include "defs.hpp"
namespace ESM {
/*
* NPC definition
*/
struct NPC
{
// Services
enum Services
{
// This merchant buys:
Weapon = 0x00001,
Armor = 0x00002,
Clothing = 0x00004,
Books = 0x00008,
Ingredients = 0x00010,
Picks = 0x00020,
Probes = 0x00040,
Lights = 0x00080,
Apparatus = 0x00100,
RepairItem = 0x00200,
Misc = 0x00400,
// Other services
Spells = 0x00800,
MagicItems = 0x01000,
Potions = 0x02000,
Training = 0x04000, // What skills?
Spellmaking = 0x08000,
Enchanting = 0x10000,
Repair = 0x20000
};
enum Flags
{
Female = 0x0001,
Essential = 0x0002,
Respawn = 0x0004,
Autocalc = 0x0008,
Skeleton = 0x0400, // Skeleton blood effect (white)
Metal = 0x0800 // Metal blood effect (golden?)
};
#pragma pack(push)
#pragma pack(1)
struct NPDTstruct52
{
short level;
char strength, intelligence, willpower, agility,
speed, endurance, personality, luck;
char skills[27];
short health, mana, fatigue;
char disposition;
char reputation; // Was "factionID", but that makes no sense.
char rank, unknown, u2;
int gold;
}; // 52 bytes
struct NPDTstruct12
{
short level;
char disposition, reputation, rank,
unknown1, unknown2, unknown3;
int gold; // ?? not certain
}; // 12 bytes
struct AIDTstruct
{
// These are probabilities
char hello, u1, fight, flee, alarm, u2, u3, u4;
// The last u's might be the skills that this NPC can train you
// in?
int services; // See the Services enum
}; // 12 bytes
#pragma pack(pop)
NPDTstruct52 npdt52;
NPDTstruct12 npdt12; // Use this if npdt52.gold == -10
int flags;
InventoryList inventory;
SpellList spells;
AIDTstruct AI;
bool hasAI;
std::string name, model, race, cls, faction, script,
hair, head; // body parts
void load(ESMReader &esm)
{
npdt52.gold = -10;
model = esm.getHNOString("MODL");
name = esm.getHNOString("FNAM");
race = esm.getHNString("RNAM");
cls = esm.getHNString("CNAM");
faction = esm.getHNString("ANAM");
head = esm.getHNString("BNAM");
hair = esm.getHNString("KNAM");
script = esm.getHNOString("SCRI");
esm.getSubNameIs("NPDT");
esm.getSubHeader();
if(esm.getSubSize() == 52) esm.getExact(&npdt52, 52);
else if(esm.getSubSize() == 12) esm.getExact(&npdt12, 12);
else esm.fail("NPC_NPDT must be 12 or 52 bytes long");
esm.getHNT(flags, "FLAG");
inventory.load(esm);
spells.load(esm);
if(esm.isNextSub("AIDT"))
{
esm.getHExact(&AI, sizeof(AI));
hasAI = true;
}
else hasAI = false;
esm.skipRecord();
}
};
}
#endif

@ -21,11 +21,13 @@
#include "loadgmst.hpp"
#include "loadingr.hpp"
#include "loadland.hpp"
#include "loadlevlist.hpp"
#include "loadligh.hpp"
#include "loadlocks.hpp"
#include "loadltex.hpp"
#include "loadmgef.hpp"
#include "loadmisc.hpp"
#include "loadnpc.hpp"
#include "loadnpcc.hpp"
#include "loadpgrd.hpp"
#include "loadrace.hpp"

@ -1,177 +0,0 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (loadlevlist.d) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
http://www.gnu.org/licenses/ .
*/
module esm.loadlevlist;
import esm.imports;
import esm.loadcrea;
import monster.modules.random : randInt;
/*
* Leveled lists. Since these have identical layout, I only bothered
* to implement it once.
*
* We should later implement the ability to merge leveled lists from
* several files.
*
*/
// Moved here for template / DMD bug reasons...
struct _ListItem
{
Item item; // Definded in records.d
short level;
}
struct LeveledListT(bool creature)
{
char[] id;
LoadState state;
enum Flags
{
AllLevels = 0x01, // Calculate from all levels <= player
// level, not just the closest below
// player.
Each = 0x02 // Select a new item each time this
// list is instantiated, instead of
// giving several identical items
} // (used when a container has more
// than one instance of one leveled
// list.)
alias _ListItem ListItem;
Flags flags;
ubyte chanceNone; // Chance that none are selected (0-255??)
ListItem list[];
// Get a random creature from this list
Creature* instCreature(short PCLevel)
in
{
assert(creature);
}
body
{
int index = instIndex(PCLevel);
if(index == -1) return null; // No creature was selected
Creature *c = cast(Creature*)list[index].item.getPtr(ItemType.Creature);
assert(c != null);
return c;
}
// Get a random item from the list
Item *instItem(short PCLevel)
in
{
assert(!creature);
}
body
{
int index = instIndex(PCLevel);
if(index == -1) return null;
return &list[index].item;
}
// Get a random index in the right range
int instIndex(short PCLevel)
{
int top, bottom, i;
// TODO: Find out if this is indeed correct.
// Test if no creature is to be selected
if(randInt(0, 255) < chanceNone) return -1;
// Find the highest item below or equal to the Player level
for(i=list.length-1; i>=0; i--)
if(list[i].level <= PCLevel) break;
top = i;
// Select none if the player level is too low
if(top < 0) return -1;
// Now find the lowest element to select
if(flags & Flags.AllLevels) bottom = 0;
else
{
// Find the lowest index i such that item[i] has the same
// level as item[top].
do { i--; }
while(i>=0 && list[i].level == list[top].level);
bottom = i+1;
}
// Select a random item
return randInt(bottom, top);
}
void load()
{with(esFile){
flags = cast(Flags)getHNUint("DATA");
chanceNone = cast(ubyte) getHNByte("NNAM");
if(hasMoreSubs())
list = getRegion().allocateT!(ListItem)(getHNInt("INDX"));
else list = null;
// TODO: Merge the lists here. This can be done simply by adding
// the lists together, making sure that they are sorted by
// level. A better way might be to exclude repeated items. Also,
// some times we don't want to merge lists, just overwrite. Figure
// out a way to give the user this option.
foreach(ref ListItem l; list)
{
static if(creature)
{
getSubNameIs("CNAM");
l.item = actors.lookup(tmpHString());
}
else
{
getSubNameIs("INAM");
// Morrowind.esm actually contains an invalid reference,
// to "imperial cuirass" in the list
// "random_imp_armor". We just ignore it to avoid
// irritating warning messages.
char[] tmp = tmpHString();
if( (tmp != "imperial cuirass") || (id != "random_imp_armor")
|| (getSpecial() != SpecialFile.Morrowind) )
l.item = items.lookup(tmp);
//l.item = items.lookup(tmpHString());
}
l.level = getHNShort("INTV");
}
}}
}
alias LeveledListT!(false) LeveledItems;
alias LeveledListT!(true) LeveledCreatures;
ListID!(LeveledCreatures) creatureLists;
ListID!(LeveledItems) itemLists;

@ -1,203 +0,0 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (loadnpc.d) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
http://www.gnu.org/licenses/ .
*/
module esm.loadnpc;
import esm.imports;
import esm.loadcont;
import esm.loadrace;
import esm.loadclas;
import esm.loadfact;
import esm.loadbody;
/*
* NPC definition
*/
struct NPC
{
// Services
enum Services : int
{
// Buys
Weapon = 0x00001,
Armor = 0x00002,
Clothing = 0x00004,
Books = 0x00008,
Ingredients = 0x00010,
Picks = 0x00020,
Probes = 0x00040,
Lights = 0x00080,
Apparatus = 0x00100,
RepairItem = 0x00200,
Misc = 0x00400,
// Services
Spells = 0x00800,
MagicItems = 0x01000,
Potions = 0x02000,
Training = 0x04000, // What skills?
Spellmaking = 0x08000,
Enchanting = 0x10000,
Repair = 0x20000
}
enum Flags
{
Female = 0x0001,
Essential = 0x0002,
Respawn = 0x0004,
Autocalc = 0x0008,
Skeleton = 0x0400, // Skeleton blood effect (white)
Metal = 0x0800 // Metal blood effect (golden?)
}
align(1) struct NPDTstruct52
{
short level;
byte strength, intelligence, willpower, agility,
speed, endurance, personality, luck;
byte skills[27];
short health, mana, fatigue;
byte disposition;
byte reputation; // Was "factionID", but that makes no sense.
byte rank, unknown, u2;
int gold;
}
align(1) struct NPDTstruct12
{
short level;
byte disposition, reputation, rank,
unknown1, unknown2, unknown3;
int gold; // ?? Not sure
}
align(1) struct AIDTstruct
{
// These are probabilities
byte hello, u1, fight, flee, alarm, u2, u3, u4;
// The u's might be the skills that this NPC can train you in
Services services;
static assert(AIDTstruct.sizeof == 12);
}
static assert(NPDTstruct52.sizeof==52);
static assert(NPDTstruct12.sizeof==12);
union
{
NPDTstruct52 npdt52;
NPDTstruct12 npdt12; // Use this if npdt52.gold == -10
}
Flags flags;
InventoryList inventory;
SpellList spells;
AIDTstruct AI;
bool hasAI;
mixin LoadT;
MeshIndex model;
Race* race;
Class* cls;
Faction* faction;
Script* script;
BodyPart* hair, head;
void load()
{with(esFile){
npdt52.gold = -10;
model = getOMesh();
name = getHNOString("FNAM");
race = getHNPtr!(Race)("RNAM", races);
cls = getHNPtr!(Class)("CNAM", classes);
faction = getHNPtr!(Faction)("ANAM", factions);
head = getHNPtr!(BodyPart)("BNAM", bodyParts);
hair = getHNPtr!(BodyPart)("KNAM", bodyParts);
script = getHNOPtr!(Script)("SCRI", scripts);
getSubNameIs("NPDT");
getSubHeader();
if(getSubSize() == 52) readExact(&npdt52, npdt52.sizeof);
else if(getSubSize() == 12) readExact(&npdt12, npdt12.sizeof);
else fail("NPC_NPDT must be 12 or 52 bytes long");
flags = cast(Flags) getHNInt("FLAG");
inventory.load();
spells.load();
if(isNextSub("AIDT"))
{
readHExact(&AI, AI.sizeof);
hasAI = true;
}
else hasAI = false;
skipRecord();
makeProto();
// Clean this up a little later, eg. no point in storing the
// structs outside the function any longer. Same goes for most of
// the load*.d structures.
if(npdt52.gold == -10)
{
proto.setInt("level", npdt12.level);
proto.setInt("gold", npdt12.gold);
proto.setInt("disposition", npdt12.disposition);
proto.setInt("reputation", npdt12.reputation);
proto.setInt("rank", npdt12.rank);
// TODO: Autocalculate the rest?
}
else
{
proto.setInt("level", npdt52.level);
proto.setInt("gold", npdt52.gold);
proto.setInt("baseStrength", npdt52.strength);
proto.setInt("baseIntelligence", npdt52.intelligence);
proto.setInt("baseWillpower", npdt52.willpower);
proto.setInt("baseAgility", npdt52.agility);
proto.setInt("baseSpeed", npdt52.speed);
proto.setInt("baseEndurance", npdt52.endurance);
proto.setInt("basePersonality", npdt52.personality);
proto.setInt("baseLuck", npdt52.luck);
proto.setInt("baseMaxHealth", npdt52.health);
proto.setInt("baseMaxMana", npdt52.mana);
proto.setInt("baseMaxFatigue", npdt52.fatigue);
}
}}
}
ListID!(NPC) npcs;
Loading…
Cancel
Save