Fixed NPC_, LEVI and LEVC. Only DIAL and INFO left.
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
|
@ -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…
Reference in New Issue