Merge pull request #2 from pdub206/beta

Beta
This commit is contained in:
pdub206 2026-01-02 07:27:48 -08:00 committed by GitHub
commit 18c92e2357
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
77 changed files with 8375 additions and 3273 deletions

220
AGENTS.md Normal file
View file

@ -0,0 +1,220 @@
# AGENTS.md
## Purpose
This file defines the rules, expectations, and constraints for **automated agents**
(including AI coding assistants, LLMs, bots, and scripted tools) contributing to this
codebase.
The goal is to:
- Preserve long-term maintainability
- Avoid licensing and provenance risk
- Prevent architectural drift
- Ensure consistency with established design decisions
- Make human review straightforward and reliable
All automated agents MUST comply with the requirements in this document.
---
## Scope
This repository is a **C-based MUD engine** derived from CircleMUD/tbaMUD and is
under active development, including significant refactors to core systems such as:
- Skills and proficiency
- Combat resolution
- Object and mobile data models
- Online Creation (OLC)
- Persistence and serialization
- Builder and immortal tooling
Changes often have **far-reaching consequences** across gameplay, balance, and
world data integrity.
---
## General Rules for Automated Agents
### 1. No Unreviewed Structural Changes
Automated agents MUST NOT:
- Reorganize directories
- Rename files
- Merge or split source files
- Introduce new subsystems
- Replace existing systems wholesale
Unless **explicitly instructed** to do so.
Incremental, targeted changes are required.
---
### 2. Preserve Existing Behavior Unless Directed
If a function, macro, or subsystem already exists:
- Do NOT change semantics
- Do NOT “simplify” logic
- Do NOT remove edge-case handling
- Do NOT refactor stylistically
Unless the user **explicitly requests** that behavior be changed.
Backward compatibility is a priority.
---
### 3. Minimal Diffs Are Mandatory
Automated agents must:
- Change only what is necessary
- Avoid drive-by formatting edits
- Avoid re-indentation unless required
- Avoid renaming variables unless necessary for correctness
If a fix can be achieved with a 3-line change, a 30-line rewrite is unacceptable.
---
### 4. Follow Existing Code Style Exactly
This codebase intentionally reflects legacy CircleMUD conventions.
Agents MUST:
- Match indentation style
- Match brace placement
- Match naming conventions
- Match macro usage patterns
Do not introduce modern C idioms, new abstractions, or stylistic preferences.
---
## Licensing and Provenance Requirements
### 5. No Third-Party Code Injection
Automated agents MUST NOT:
- Paste code from external projects
- Introduce snippets from blogs, gists, StackOverflow, or forums
- Reproduce code from GPL-incompatible sources
All code must be **original**, **derivative of existing repository code**, or
**explicitly authorized** by the user.
If uncertain, ask before proceeding.
---
### 6. Do Not Assume License Changes
Do NOT:
- Modify license headers
- Remove attribution
- Add new license files
- Assume relicensing is permitted
Licensing is handled deliberately and conservatively.
---
## Technical Expectations
### 7. Full-Context Awareness Required
Before modifying a subsystem, automated agents MUST:
- Read all related `.c` and `.h` files
- Identify existing macros, helpers, and patterns
- Understand how data flows through the system
Guessing or partial understanding is not acceptable.
---
### 8. Prefer Existing Helpers and Macros
If functionality already exists:
- Reuse it
- Extend it minimally if needed
- Do not reimplement logic elsewhere
Duplication increases maintenance cost and risk.
---
### 9. Explicitly Note Assumptions
When producing code or recommendations, agents MUST:
- State assumptions clearly
- Identify uncertainties
- Call out areas that require human confirmation
Silent assumptions are dangerous.
---
## Data Integrity and World Safety
### 10. Protect World Files and Player Data
Automated agents must treat:
- World files
- Player files
- OLC data
- Serialized objects/mobs/rooms
As **production data**.
Do NOT:
- Change file formats casually
- Break backward compatibility
- Introduce implicit migrations
Any data format change must be explicit and documented.
---
## Communication Expectations
### 11. Be Direct and Precise
Agent output should:
- Use technical language appropriate to experienced developers
- Avoid verbosity for its own sake
- Avoid motivational or conversational filler
- Focus on correctness and clarity
---
### 12. Ask Before Acting When Uncertain
If instructions are ambiguous or risky:
- STOP
- Ask clarifying questions
- Do not guess intent
Incorrect confidence is worse than delay.
---
## Enforcement
Failure to comply with this document may result in:
- Rejection of changes
- Reversion of commits
- Loss of permission to contribute
This applies equally to humans and automated agents.
---
## Summary
This codebase prioritizes:
- Stability over novelty
- Clarity over cleverness
- Intentional design over convenience
Automated agents are welcome collaborators **only** when they operate within
these constraints.

View file

@ -1,21 +1,18 @@
***Files for Miranthas MUD.***
***Files for Cataclysm MUD.***
Miranthas MUD is a continuation of tbaMUD/CircleMUD, which is built on DIKU MUD.
The code here is freeware to honor that tradition.
Cataclysm MUD is a continuation of tbaMUD/CircleMUD, which is built on DIKU MUD.
Due to licensing issues with tbaMUD, all licensing attributions should remain the
same and not changed from the original, pre-LGPL license model.
Due to the sensitive nature of topics found in this setting, all characters and
players are 18+. The game world is derived from several inspirational sources,
most notably the former Armageddon MUD.
TO be clear: **this is not an LGPL licensed fork**.
Roleplay is highly encouraged, but not enforced.
Features in Miranthas MUD Alpha release:
Changes from stock tbaMUD 2025 to Cataclysm MUD v1.0.0-alpha:
* The city of Caleran is available for exploration
* Experience points and levels are removed in favor of skill based progression
* Initial skills/spells based partly on tbaMUD code and 5e conversion (to be cleaned up in later release)
* Expanded emoting system for roleplay
* Permanent character death - aka. hardcore mode
* Permanent character death
* A hybrid "5e-like" system where:
- [ ] Legacy THAC0 systems are removed in favor of the modern 5e system
- [ ] Your skill level translates to a proficiency bonus on a per-skill level
@ -44,24 +41,72 @@ Features in Miranthas MUD Alpha release:
* Backgrounds are now available for PC's and NPC's
* Account system for tracking players/characters over long periods of time
Features to be implemented in the next few releases:
Changes in v1.1.0-alpha:
* Race/species selection
* Subclass selection
* Cleaned up legacy practice system code
* Added skill caps for classes to limit ability of everyone to reach skill level 100 (and respective proficiency)
* Race/species selection and stat ranges (elves have higher dex, dwarves have higher str, etc)
* Renamed move to stamina in code to reflect how much energy is used for certain actions
* Species have base hit/mana/stamina now, plus their class modifier rolls
* Prioritized stats during character generation
* Ability to change ldesc of PC/NPC's
* Ability to look in certain directions to see what is 1-3 rooms away
* PC's and NPC's can now have an age set between 18-65
* "audit armor" and "audit melee" commands for immortals (formerly "acaudit") to check non-compliant items
* Minor score output change to only show quest status while on a quest, PC/NPC name, sdesc, and current ldesc
* Added ability to reroll initial stats if they are not to player's liking, and undo reroll if needed
* Removed alignment from game - no more GOOD/EVIL flags or restrictions on shops
* Removed ANTI_ flags related to class restrictions on what objects they can use
* Mounts added to help with long trips, and ability to use them as pack animals
* Introduced AGENTS.md file to ensure code quality
* Migration away from OLC with new commands "rcreate" and "rset" for builders to modify rooms
* Migration away from OLC with new commands "ocreate", "oset", and "osave" for builders to modify objects
* Migration away from OLC with new command "mcreate" and for builders to modify NPC's
* Fixed issue with msave not saving items in containers on NPC's
Features that would have been implemented in the next few releases:
* Height and weight normalized to species
* Stables allow for purchasing of mounts
* Stables will take mounts and provide tickets to get them out
* Updated door code so that it can be closed/locked/saved with rsave code
* SECTOR/ROOM type changes to make terrain movement easier or more difficult
* Subclass selection to personalize character further
* Combat is slowed down so it isn't over in < 15 seconds (unless you're far outmatched)
* Wagons added to help with caravans
* BUILDING object type created to allow enter/leave
* Updated BUILDING object type so that it can be damaged and no longer enterable (but someone can leave at cost to health)
* Plantlife introduced, allowing a plant object to produce fruit or herbs every few hours/days
* Plantlife can be refreshed to spawn fruits/herbs more frequently by watering it
* Updated lockpicking skill
* Trap as a skill - one focused on city and one focused on desert
* Poisons and antidotes
* New alcohol and drugs/stimulants
* Skimmers/ships to traverse difficult terrain
* New elemental classes
* Introduction of gathering mana/magic for sorceror class
* Highly modified magic system
* Ranged weapons and ammo
* Components for some magical spells
* Reading/writing limited to specific castes of society
* Haggling and bartering system
* New calendar and moon cycles
* Heat based on time of day increases/decreases, changing hunger/thirst levels
* Sandstorms
* Shaded rooms
* Weather updates and sandstorms limiting visibility
* Shaded rooms providing bonuses to regeneration
* Criminal system for cities and jails
* Basic Psionics
* Basic crafting system
* Continued skill and spell improvements
* Apartment rentals for storing your loot
* Enhanced quest system
* Dialogue trees with NPC's
* Additional zones/cities based on Miranthas world map
* Resources on the world map can be claimed by different city-states or independent factions
* Claimed resources improve quality of armor/weapons/food/prices available
* Death from old age if you roll badly on your birthday after the expected lifespan of a species
* Attacks hit different parts of the body and have different damage effects
* Armor degradation based on damage taken per body part
* Weapon degradation based on damage dealt - potentially shattering weapons
...and down the road:

View file

@ -226,7 +226,7 @@ sell objects to players.
A single zone typically contains up to than 100 rooms, 100 monster definitions
and 100 object definitions, but a large region can be subdivided into several
zones at the authors discretion. For example, the City of Midgaard is divided
zones at the authors discretion. For example, a large city can be divided
into two zones, one for the main city and one for the southern residential area.
In addition to this, with the new zone system describing top and bottom rooms
of a zone, zones can contain very few rooms and indeed can overlap with other
@ -242,7 +242,7 @@ independent, so there can be both a room number 3001 and an object number
refers to each entity by its Vnum and never by name. Vnums are normally not
seen by players. Each zone itself also has a Vnum. A common convention is to
number the zone with the Vnums of its component rooms, divided by 100. For
example, Midgaard is zone 30, consisting of rooms 3000 to 3099. Mobile and
example, the main city is zone 30, consisting of rooms 3000 to 3099. Mobile and
object numbering follows the same convention.
The author of the zone can define aspects of each room such as the terrain type,
@ -632,7 +632,7 @@ The format of a mobile is:
~
<detailed description>
~
<mob flags> <affect flags> <alignment> <type flag>
<mob flags> <affect flags> <reserved> <type flag>
{type-specific information; see below for details}
Virtual Number This number is critical; it is the identity of the mobile within
@ -687,9 +687,9 @@ L) NPC Flags : ISNPC
its hit points. If the WIMPY bit is set in conjunction with
any of the forms of the AGGRESSIVE bit, the mob will only
attack mobs that are unconscious (sleeping or incapacitated).
9) AGGR_EVIL Mob will attack players that are evil-aligned.
10) AGGR_GOOD Mob will attack players that are good-aligned.
11) AGGR_NEU Mob will attack players that are neutrally aligned.
9) RESERVED Not used.
10) RESERVED Not used.
11) RESERVED Not used.
12) MEMORY Mob will remember players that initiate attacks on it, and will
attack that player if it ever runs into him again.
13) HELPER The mob will attack any player it sees in the room that is
@ -710,7 +710,7 @@ following values:
M) AFF Flags : NOBITS
1) BLIND Mob is blind.
2) INVIS Mob is invisible.
3) DET-ALIGN NOT USED.
3) RESERVED NOT USED.
4) DET-INVIS Mob can see invisible characters and objects.
5) DET-MAGIC Mob is sensitive to magical presence.
6) SENSE-LIFE Mob can sense hidden life.
@ -721,23 +721,17 @@ M) AFF Flags : NOBITS
10) CURSE Mob is cursed.
11) INFRA Mob can see in dark.
12) POISON Reserved for internal use. Do not set.
13) PROT-EVIL Mob is protected from evil characters.
14) PROT-GOOD Mob is protected from good characters.
13) RESERVED NOT USED.
14) RESERVED NOT USED.
15) SLEEP Reserved for internal use. Do not set.
16) NO_TRACK Mob cannot be tracked.
17) UNUSED Unused (room for future expansion).
18) UNUSED Unused (room for future expansion).
17) FLY Mob is flying.
18) SCUBA Mob can breathe underwater.
19) SNEAK Mob can move quietly without room being informed.
20) HIDE Mob is hidden; can only be seen with sense life.
21) UNUSED Unused (room for future expansion).
21) SCAN Actively scanning for hidden threats.
22) CHARM Reserved for internal use. Do not set.
Alignment A number from -1000 to 1000 representing the mobs initial alignment.
-1000.....-350 Evil
-349......349 Neutral
350.....1000 Good
Type Flag This flag is a single letter which indicates what type of mobile is
currently being defined, and controls what information tbaMUD expects to find
next (i.e., in the file from the current point to the end of the current
@ -748,7 +742,7 @@ Standard tbaMUD supports two types of mobiles: S (for Simple), and E (for Enhanc
4.2 Type S Mobiles
For type S mobs, the type-specific information should be in the following format:
<mob flags> <affect flags> <alignment> S
<mob flags> <affect flags> <reserved> S
<level> <thaco> <armor class> <hitpoints> <damage>
<coins> <experience points>
<load position> <default position> <sex>
@ -840,13 +834,13 @@ A beastly fido is mucking through the garbage looking for food here.
The fido is a small dog that has a foul smell and pieces of rotted meat
hanging around his teeth.
~
65738 0 0 0 65536 0 0 0 -200 S
65738 0 0 0 65536 0 0 0 0 S
1 20 9 0d0+10 1d2+0
10 100
8 8 1
This is mobile vnum 3062. The Fido has mob and affect flags set and has an
initial alignment of -200. After the S flag we see that the Fido is level 1,
This is mobile vnum 3062. The Fido has mob and affect flags set and has a
reserved value of 0. After the S flag we see that the Fido is level 1,
has a THAC0 of 20, an Armor Class of 9, 0d0+10 hit points (10 hitpoints), and
will do 1d2 hit points of bare hand damage per round. The Fido has 10 coins
and 100 experience points, has a load position and default position of
@ -865,7 +859,7 @@ literal letter Emust then come after all E-specs to signal the end of the mob.
The format of an E mobile is as follows:
<mob flags> <affect flags> <alignment> E
<mob flags> <affect flags> <reserved> E
<level> <hitroll> <armor class> <hitpoints> <damage>
<coins> <experience points>
<load position> <default position> <sex>
@ -886,7 +880,7 @@ A beastly fido is mucking through the garbage looking for food here.
The fido is a small dog that has a foul smell and pieces of rotted meat
hanging around his teeth.
~
65738 0 0 0 65536 0 0 0 -200 E
65738 0 0 0 65536 0 0 0 0 E
1 20 9 0d0+10 1d2+0
10 100
8 8 1
@ -1033,14 +1027,10 @@ but has no substantive effect otherwise. The flags have the following values:
7) MAGIC Item has a magical aura and cannot be enchanted.
8) NODROP Item is cursed and cannot be removed or dropped.
9) BLESS Item is blessed (cosmetic).
10) ANTI_GOOD Item cannot be used by good-aligned characters.
11) ANTI_EVIL Item cannot be used by evil-aligned characters.
12) ANTI_NEUTRAL Item cannot be used by neutral align characters.
13) ANTI_MAGIC_USER Item cannot be used by the Mage class.
14) ANTI_CLERIC Item cannot be used by the Cleric class.
15) ANTI_THIEF Item cannot be used by the Thief class.
16) ANTI_WARRIOR Item cannot be used by the Warrior class.
17) NOSELL Shopkeepers will not buy or sell the item.
10) NOSELL Shopkeepers will not buy or sell the item.
11) QUEST_ITEM Shopkeepers will only accept questpoints for this item.
12) HOOD_UP WORN item hood is currently up.
13) SKINNED Item/corpse can be skinned.
Wear Bitvector A bitvector (see section 2.4 on Using Bitvectors) with
the following values:
@ -1311,7 +1301,7 @@ terminated by the literal letter S.
Virtual Number An arbitrary number used to identify the zone. Zone numbers are
traditionally the room numbers of the zone divided by 100; for example,
Midgaard, which consists of rooms 3000 through 3099, is zone 30.
The main city, which consists of rooms 3000 through 3099, is zone 30.
Zone Name A label given to the zone so that it can be identified in system
logs.
@ -1444,7 +1434,7 @@ Zone File Example
A sample zone file annotated with comments follows.
#30 * This is zone number 30
Northern Midgaard Main City~ * The name of the zone
Northern Main City~ * The name of the zone
3099 15 2 * Top of zone is room #3099; it resets every 15 minutes.
* * Resets regardless of people.
*
@ -1611,16 +1601,16 @@ monty-haul campaigns.
Shop Keeper Mobile Number Virtual number of the shopkeeper mobile.
With Who Bitvector A bitvector (see section 2.4 on Using Bitvectors) used to
designate certain alignments or classes that the shop will not trade with, with
designate certain classes that the shop will not trade with, with
the following values:
E) No Trade With : NOBITS
Designates certain alignments or classes that the shop will not trade with:
Designates certain classes that the shop will not trade with:
1) Good 2) Evil
3) Neutral 4) Magic User
5) Cleric 6) Thief
7) Warrior
1) Sorceror 2) Cleric
3) Rogue 4) Fighter
5) Barbarian 6) Ranger
7) Bard 8) Druid
Shop Room 1...Shop Room n The virtual numbers the mobile must be in for the
shop to be effective. (So transferred shopkeepers cant sell in the desert).
@ -1737,15 +1727,16 @@ Shop Keeper Mobile Number Virtual number of the shopkeeper mobile.
With Who Bitvector
A bitvector (see section Using Bitvectors) used to designate certain
alignments or classes that the shop will not trade with, with the following
classes that the shop will not trade with, with the following
values:
1 a NOGOOD Keeper wont trade with positively-aligned players.
2 b NOEVIL Keeper wont trade with evilly-aligned players.
4 c NONEUTRAL Keeper wont trade with neutrally-aligned players.
8 d NOMAGIC_USER Keeper wont trade with the Mage class.
8 d NOSORCEROR Keeper wont trade with the Sorceror class.
16 e NOCLERIC Keeper wont trade with the Cleric class.
32 f NOTHIEF Keeper wont trade with the Thief class.
64 g NOWARRIOR Keeper wont trade with the Warrior class.
32 f NOROGUE Keeper wont trade with the Rogue class.
64 g NOFIGHTER Keeper wont trade with the Fighter class.
128 h NOBARBARIAN Keeper wont trade with the Barbarian class.
256 i NORANGER Keeper wont trade with the Ranger class.
512 j NOBARD Keeper wont trade with the Bard class.
1024 k NODRUID Keeper wont trade with the Druid class.
Shop Room Number
The virtual number the mobile must be in for the shop to be effective. (So
@ -1823,7 +1814,7 @@ interactions you want it to perform. See the help files in game for details.
8.2 Trigger File Example
#100
Obj Command 100 - portal to Midgaard~
Obj Command 100 - portal to the capital~
1 c 7
en~
if %cmd.mudcommand% == enter && %arg% /= portal
@ -1848,18 +1839,18 @@ particular MUD. Check with your implementor for details.
7) charm person 8) chill touch 9) clone
10) color spray 11) control weather 12) create food
13) create water 14) cure blind 15) cure critic
16) cure light 17) curse 18) detect alignment
16) cure light 17) curse 18) reserved
19) detect invisibility 20) detect magic 21) detect poison
22) dispel evil 23) earthquake 24) enchant weapon
22) reserved 23) earthquake 24) enchant weapon
25) energy drain 26) fireball 27) harm
28) heal 29) invisibility 30) lightning bolt
31) locate object 32) magic missile 33) poison
34) protection from evil 35) remove curse 36) sanctuary
34) reserved 35) remove curse 36) sanctuary
37) shocking grasp 38) sleep 39) strength
40) summon 41) !UNUSED! 42) word of recall
40) summon 41) !RESERVED! 42) word of recall
43) remove poison 44) sense life 45) animate dead
46) dispel good 47) group armor 48) group heal
49) !UNUSED! 50) infravision 51) waterwalk
46) reserved 47) group armor 48) group heal
49) !RESERVED! 50) infravision 51) waterwalk
B Item Values for Drink Containers

View file

@ -813,7 +813,7 @@ equips it to the character for the specified location. unequip_char() does the
opposite; it removes the object from the characters equipment list and returns it
as a free-floating object. The object being unequipped must be placed elsewhere or
destroyed. Note that some objects may not be equipped by characters of certain
classes and/or alignments.
classes.
void obj_to_char (struct obj_data *object, struct char_data *ch)
@ -1418,18 +1418,9 @@ search for CLASS_UNDEFINEDand add a new CLASS_xxxdefinition for your class name
with the next available number. Remember to bump the value of NUM_CLASSES, just
below, by 1.
Then search structs.hfor "Extra object flags" so you can add an "ITEM_ANTI_xxx"
flag for your new class. As before, use the next available number in the sequence
for ITEM_xxx flags.
The "ITEM_xxx" extra flags have a corresponding text description in constants.c, so
search it for "ITEM_x (extra bits)". Add a string giving a short name for the new
ITEM_ANTI_xxx flag, in the appropriate order, before the "\n" entry near the
bottom.
The shops have a similar "dont trade" setup, so search shop.hfor "TRADE_NOGOOD"
The shops have a similar "dont trade" setup, so search shop.h for "TRADE_NOSORCEROR"
to add a new TRADE_NOxxx item to the list for the class to be added. Below that
(near "NOTRADE_GOOD"), a line will need to be added for each new class so the no
(near "NOTRADE_SORCEROR"), a line will need to be added for each new class so the no
trade status of a shop can be tested for the class.
With the definitions in shop.h, the shop code in shop.c can then be modified to
@ -1559,7 +1550,7 @@ foreground colors.
Basic special procedures have largely been replaced by trigedit. Thus shifting the
workload from the coder to the builder to add life and creativity into their zones.
While it is easier, and less time consuming, to make triggers than special
procedures they are still used for such functions as banks and guildmasters.
procedures they are still used for such functions as banks.
Using special procedures, your virtual world is not just a bunch of monsters,
objects and rooms, reduced to a number of statistics. Just like good descriptions
@ -1570,7 +1561,7 @@ Several special procedures are provided with stock tbaMUD which you can use to
create your own and get used to the mechanics of special procedures. These special
procedures can be found in castle.c and spec_procs.c. They range from very simple
procedures, like puff (pulsed special procedure) or bank (command-driven special
procedure), to very complex procedures like the guild master.
procedure), to very complex procedures.
In this chapter, FALSE refers to the value 0 and TRUE to any non-zero value.

View file

@ -16,11 +16,11 @@ Message List:
0 ) [5 ] 1, burning hands 30) [314] 2, Unknown
1 ) [6 ] 1, call lightning 31) [399] 2, Unknown
2 ) [8 ] 1, chill touch 32) [33 ] 1, poison
3 ) [10 ] 1, color spray 33) [202] 1, !UNUSED!
4 ) [22 ] 1, dispel evil 34) [203] 1, !UNUSED!
5 ) [23 ] 1, earthquake 35) [204] 1, !UNUSED!
6 ) [25 ] 1, energy drain 36) [205] 1, !UNUSED!
7 ) [26 ] 1, fireball 37) [206] 1, !UNUSED!
3 ) [10 ] 1, color spray 33) [202] 1, !RESERVED!
4 ) [22 ] 1, dispel 34) [203] 1, !RESERVED!
5 ) [23 ] 1, earthquake 35) [204] 1, !RESERVED!
6 ) [25 ] 1, energy drain 36) [205] 1, !RESERVED!
7 ) [26 ] 1, fireball 37) [206] 1, !RESERVED!
=========================================

View file

@ -9,17 +9,16 @@ rumble@tbamud.com -- Rumble
1. Errant Rooms (show error command)
2. SYSERR: Object # (oedit-s-desc) doesn't have drink type as last alias.
3. SYSERR: Mob # both Aggressive and Aggressive_to_Alignment.
4. SYSERR: Object # (oedit-s-desc) has out of range level #.
5. SYSERR: Object # (oedit-s-desc) has negative weight (-2147483644).
3. SYSERR: Object # (oedit-s-desc) has out of range level #.
4. SYSERR: Object # (oedit-s-desc) has negative weight (-2147483644).
SYSERR: Object # (oedit-s-desc) has out of range level #2147483647.
6. SYSERR: Object # (oedit-s-desc) uses 'UNDEFINED' spell #.
7. SYSERR: Object # (oedit-s-desc) contains (5) more than maximum (3).
8. Char is already equipped: (medit-s-desc), (oedit-s-desc)
9. SYSERR: Attempt to assign spec to non-existant mob #
10. No associated object exists when attempting to create a board [vnum #].
11. SYSERR: Mob using >'((ch)-)player_specials....
12: SYSERR: Object # (keyword) is type NOTE and has extra description with
5. SYSERR: Object # (oedit-s-desc) uses 'UNDEFINED' spell #.
6. SYSERR: Object # (oedit-s-desc) contains (5) more than maximum (3).
7. Char is already equipped: (medit-s-desc), (oedit-s-desc)
8. SYSERR: Attempt to assign spec to non-existant mob #
9. No associated object exists when attempting to create a board [vnum #].
10. SYSERR: Mob using >'((ch)-)player_specials....
11. SYSERR: Object # (keyword) is type NOTE and has extra description with
same name (keyword)
1: Errant Rooms
@ -44,30 +43,23 @@ the drink aliases).
i.e. a shot of whisky should have the namelist: whisky shot whisky
a cup of slime mold juice namelist should be: juice cup slime juice
3: SYSERR: Mob # both Aggressive and Aggressive_to_Alignment.
Another harmless error. If your mob is aggressive there is no need to also
make it aggressive to certain alignments since AGGR means it will attack any
player it can see. Choose to make it aggressive to an alignment or aggressive
to all.
4: SYSERR: Object # (oedit-s-desc) has out of range level #.
3: SYSERR: Object # (oedit-s-desc) has out of range level #.
A simple one. Limit spell levels to the levels available, on tbaMUD that
would be 1-34.
5: SYSERR: Object # (oedit-s-desc) has negative weight (-2147483644).
4: SYSERR: Object # (oedit-s-desc) has negative weight (-2147483644).
SYSERR: Object # (oedit-s-desc) has out of range level #2147483647.
These are really annoying. This happens on older versions of CircleMUD when
you use numbers larger than necessary. This will actually crash many older
versions of CircleMUD. Just do not do it. Use realistic numbers.
6: SYSERR: Object # (oedit-s-desc) uses 'UNDEFINED' spell #.
5: SYSERR: Object # (oedit-s-desc) uses 'UNDEFINED' spell #.
There is no spell zero. Either select a spell or put -1 for none.
7: SYSERR: Object # (oedit-s-desc) contains (#) more than maximum (#).
6: SYSERR: Object # (oedit-s-desc) contains (#) more than maximum (#).
When making a drink container you will set how much it initially contains
on creation and the max it can hold if a player were to fill it. Common sense
@ -75,24 +67,24 @@ tells us that you can not create a container that initially holds more than
the max you set. To simplify the max must always be greater than or equal to
the initial amount.
8: Char is already equipped: (medit-s-desc), (oedit-s-desc)
7: Char is already equipped: (medit-s-desc), (oedit-s-desc)
This happens when someone tries to equip a mob with one or more object in
a single location. HELP ZEDIT-EQUIP for all the possible object equipping
locations. All you have to do to fix this is pick a different equip location
that is not used.
9: SYSERR: Attempt to assign spec to non-existant mob #
8: SYSERR: Attempt to assign spec to non-existant mob #
To get rid of this "grep # spec_assign.c" and remove this assignment.
10: No associated object exists when attempting to create a board [vnum #].
9: No associated object exists when attempting to create a board [vnum #].
You need to delete this board from lib/etc/boards/ and modify boards.c and
boards.h. Again "grep #" *.[ch] to search for this vnum in all of your .c and
.h files to remove the reference.
11: SYSERR: Mob using >'((ch)-)player_specials....
10: SYSERR: Mob using >'((ch)-)player_specials....
Players and mobs (NPC's) share many of the same data fields, but not all.
So when a mob tries to access player data it gives a SYSERR like this:
@ -110,7 +102,7 @@ know this is confusing, but just copy the example below.
The changed line now will not just check for a flag, instead it will check if
it is a player (not an NPC) and it is flagged then continue.
12: SYSERR: Object # (keyword) is type NOTE and has extra description with
11: SYSERR: Object # (keyword) is type NOTE and has extra description with
same name (keyword)
Object type NOTE is meant to be written on using the action-description. So if

View file

@ -308,7 +308,7 @@ You receive a punch in your kidneys which ends your worldly misery -- R.I.P.!
$n punches $N in the kidneys causing immediate death!
You wildly punch at the air, missing $N.
$n misses a wild punch at you.
$n practices shadow-boxing while $N takes a break.
$n shadow-boxes while $N takes a break.
#
#
#
@ -861,4 +861,3 @@ $N is hit by $n's lightning breath.
$N is immortal and immune to lightning breath.
$n tries to breath lightning upon you.
$n tries to breath lightning upon $N.

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@
[Note: Further information can be gained on the capitalized words by using the
HELP-command]
Classes, skills, and guilds:
Classes and skills:
When you enter the game the first time, you are asked to select a class; either
THIEF, WARRIOR, CLERIC, or MAGIC user. Characters belonging to each of these
@ -16,10 +16,7 @@ keys, creating food supplies out of nothing, and destroying an enemy in
several different ways. It is not advisable for a new player to offend the
members of such a group!
In order to learn the tricks of your specific trade, you need to find your
GUILD, which may take a little exploring. However, you can usually find a
guild in any reasonably populated city. Once you've found it, you need to
look up your guildmaster and PRACTICE.
You improve your skills by using them in play.
Equipment:

View file

@ -8,20 +8,13 @@ A tall, burly human soldier stands here at the gate.
has a thick, haggard looking brown beard that is flecked with dust and sand.
His eyes have a bluish-tint to them with a small amount of green. One might
call his stature bulky, as he has quite a bit of muscle.
~
B
~
6218 0 0 0 0 0 0 0 0 E
1 3d20+40
8 8 1
Str: 16
Class: 3
SaveStr: 3
Species: 0
Skill 132 5
Skill 134 5
Skill 137 5
@ -33,17 +26,17 @@ Skill 145 5
Skill 146 5
Skill 147 5
E
L 17 127 1
L 16 117 1
L 15 117 1
L 11 111 1
L 10 107 1
L 9 124 1
L 8 115 1
L 7 108 1
L 6 110 1
L 5 131 1
L 3 118 1
L 5 131 1
L 6 110 1
L 7 108 1
L 8 115 1
L 9 124 1
L 10 107 1
L 11 111 1
L 15 117 1
L 16 117 1
L 17 127 1
#101
Sally~
slim lanky human soldier guard~
@ -55,27 +48,25 @@ tightly. What muscle she does have is accentuated, though there is not much.
Her arms and legs are lanky, seeming to be longer than normal. A ponytail of
dark hair has been pulled back behind her head, intensifying the sharpness of
her nose.
~
B
~
6218 0 0 0 0 0 0 0 0 E
1 3d20+40
8 8 2
Class: 3
Species: 0
Skill 132 5
Skill 134 5
Skill 137 5
Skill 140 5
Skill 141 5
Skill 142 5
Skill 143 5
Skill 144 5
Skill 145 5
Skill 146 5
Skill 147 5
Skill 152 5
Skill 156 5
Skill 163 5
E
L 3 118 1
L 5 131 1
@ -99,20 +90,13 @@ has taken hold, yet he has a thick and wiry beard that covers most of his face.
Pale blue eyes look bloodshot, and bluish-purple bags hang under them. His
hands are marred with minor cuts and scars from years of working in service to
others.
~
B
~
10 0 0 0 0 0 0 0 0 E
1 3d12+60
8 8 1
E
L 9 112 1
L 14 113 1
L 9 112 1
#103
Lanky~
woman lanky scarred~
@ -125,13 +109,6 @@ dehydration. Tanned dark from the harsh rays of the sun, her upper torso is
covered in scars from a lifetime of labor. Unkempt brown hair covers her head,
descending to just above her shoulders. Her eyes are a light blue color and
appear slightly bloodshot.
~
B
~
10 0 0 0 0 0 0 0 0 E
1 3d8+60
@ -161,17 +138,56 @@ appear thick and ready to propel the animal if it feels threatened.
B
It's a rat.
~
8 0 0 0 0 0 0 0 0 E
1 0d0+10
8 8 2
Str: 6
Dex: 10
Int: 4
Wis: 4
Con: 6
Str: 3
Dex: 7
Int: 3
Wis: 8
Con: 8
Cha: 3
Species: 25
Age: 42
AtkT 4
E
#105
Kank~
kank sandy brown~
a sandy brown kank~
A sandy brown kank is here, clacking its pincers.
~
It looks unfinished.
~
B
No background has been recorded.
~
1048584 0 0 0 0 0 0 0 0 E
1 0d0+10
8 8 0
Str: 18
Dex: 5
Int: 3
Wis: 7
Con: 18
Cha: 3
Species: 21
AtkT 4
E
$

View file

@ -642,4 +642,12 @@ hide, reinforcing the foot from the ground below.
11 0 0 0 0 ah 0 0 0 0 0 0 0
0 0 0 0
1 40 0 0 0
#168
unfinished object~
unfinished object made by Kinther~
This is an unfinished object created by Kinther on Wed Dec 31 14:19:06 2025~
~
0 0 0 0 0 a 0 0 0 0 0 0 0
0 0 0 0
0 0 0 0 0
$~

View file

@ -43,59 +43,6 @@ V 5 0
V 6 0
V 7 0
.
#R 131 1759603279
O 144 0 20 0 0
X 0 0
X 1 0
X 2 0
X 3 0
W 0 0
W 1 0
W 2 0
W 3 0
V 0 100
V 1 100
V 2 0
V 3 0
V 4 0
V 5 0
V 6 0
V 7 0
O 145 0 40 0 0
X 0 0
X 1 0
X 2 0
X 3 0
W 0 0
W 1 0
W 2 0
W 3 0
V 0 4
V 1 0
V 2 2
V 3 0
V 4 0
V 5 0
V 6 0
V 7 0
O 146 0 80 0 0
X 0 0
X 1 0
X 2 0
X 3 0
W 0 0
W 1 0
W 2 0
W 3 0
V 0 6
V 1 0
V 2 2
V 3 0
V 4 0
V 5 0
V 6 0
V 7 0
.
#R 136 1759603517
O 147 0 100 0 0
X 0 0
@ -197,3 +144,58 @@ V 5 0
V 6 0
V 7 0
.
#R 131 1767296856
O 144 0 20 0 0
X 0 0
X 1 0
X 2 0
X 3 0
W 0 0
W 1 0
W 2 0
W 3 0
V 0 100
V 1 100
V 2 0
V 3 0
V 4 0
V 5 0
V 6 0
V 7 0
O 145 0 40 0 0
X 0 0
X 1 0
X 2 0
X 3 0
W 0 0
W 1 0
W 2 0
W 3 0
V 0 4
V 1 0
V 2 2
V 3 0
V 4 0
V 5 0
V 6 0
V 7 0
O 146 0 80 0 0
X 0 0
X 1 0
X 2 0
X 3 0
W 0 0
W 1 0
W 2 0
W 3 0
V 0 6
V 1 0
V 2 2
V 3 0
V 4 0
V 5 0
V 6 0
V 7 0
.
#R 101 1767296996
.

View file

@ -1 +1,7 @@
#1
Test~
0 q 1
~
* No Script
~
$~

View file

@ -1,596 +0,0 @@
#3000
Mage Guildguard - 3024~
0 q 100
~
* Check the direction the player must go to enter the guild.
if %direction% == south
* Stop them if they are not the appropriate class.
if %actor.class% != Magic User
return 0
%send% %actor% The guard humiliates you, and blocks your way.
%echoaround% %actor% The guard humiliates %actor.name%, and blocks %actor.hisher% way.
end
end
~
#3001
Cleric Guildguard - 3025~
0 q 100
~
* Check the direction the player must go to enter the guild.
if %direction% == north
* Stop them if they are not the appropriate class.
if %actor.class% != Cleric
return 0
%send% %actor% The guard humiliates you, and blocks your way.
%echoaround% %actor% The guard humiliates %actor.name%, and blocks %actor.hisher% way.
end
end
~
#3002
Thief Guildguard - 3026~
0 q 100
~
* Check the direction the player must go to enter the guild.
if %direction% == east
* Stop them if they are not the appropriate class.
if %actor.class% != Thief
return 0
%send% %actor% The guard humiliates you, and blocks your way.
%echoaround% %actor% The guard humiliates %actor.name%, and blocks %actor.hisher% way.
end
end
~
#3003
Warrior Guildguard - 3027~
0 q 100
~
* Check the direction the player must go to enter the guild.
if %direction% == east
* Stop them if they are not the appropriate class.
if %actor.class% != Warrior
return 0
%send% %actor% The guard humiliates you, and blocks your way.
%echoaround% %actor% The guard humiliates %actor.name%, and blocks %actor.hisher% way.
end
end
~
#3004
Dump - 3030~
2 h 100
~
%send% %actor% You are awarded for outstanding performance.
%echoaround% %actor% %actor.name% has been awarded for being a good citizen.
eval value %object.cost% / 10
if %value% > 50
set value 50
elseif %value% < 1
set value 1
end
if %actor.level% < 3
nop %actor.exp(%value%)%
else
nop %actor.coins(%value%)%
end
%purge% %object%
~
#3005
Stock Thief~
0 b 10
~
set actor %random.char%
if %actor%
if %actor.is_pc% && %actor.coins%
%send% %actor% You discover that %self.name% has %self.hisher% hands in your wallet.
%echoaround% %actor% %self.name% tries to steal coins from %actor.name%.
eval coins %actor.coins% * %random.10% / 100
nop %actor.coins(-%coins%)%
nop %self.coins(%coins%)%
end
end
~
#3006
Stock Snake~
0 k 10
~
%send% %actor% %self.name% bites you!
%echoaround% %actor% %self.name% bites %actor.name%.
dg_cast 'poison' %actor%
~
#3007
Stock Magic User~
0 k 10
~
switch %actor.level%
case 1
case 2
case 3
break
case 4
dg_cast 'magic missile' %actor%
break
case 5
dg_cast 'chill touch' %actor%
break
case 6
dg_cast 'burning hands' %actor%
break
case 7
case 8
dg_cast 'shocking grasp' %actor%
break
case 9
case 10
case 11
dg_cast 'lightning bolt' %actor%
break
case 12
dg_cast 'color spray' %actor%
break
case 13
dg_cast 'energy drain' %actor%
break
case 14
dg_cast 'curse' %actor%
break
case 15
dg_cast 'poison' %actor%
break
case 16
if %actor.align% > 0
dg_cast 'dispel good' %actor%
else
dg_cast 'dispel evil' %actor%
end
break
case 17
case 18
dg_cast 'call lightning' %actor%
break
case 19
case 20
case 21
case 22
dg_cast 'harm' %actor%
break
default
dg_cast 'fireball' %actor%
break
done
~
#3008
Near Death Trap~
2 g 100
~
* By Rumble of The Builder Academy tbamud.com 9091
* Near Death Trap stuns actor
set stunned %actor.hitp%
%damage% %actor% %stunned%
%send% %actor% You are on the brink of life and death.
%send% %actor% The Gods must favor you this day.
~
#3009
Stock Cityguard - 3059, 60, 67~
0 b 50
~
if !%self.fighting%
set actor %random.char%
if %actor%
if %actor.is_killer%
emote screams 'HEY!!! You're one of those PLAYER KILLERS!!!!!!'
kill %actor.name%
elseif %actor.is_thief%
emote screams 'HEY!!! You're one of those PLAYER THIEVES!!!!!!'
kill %actor.name%
elseif %actor.cha% < 6
%send% %actor% %self.name% spits in your face.
%echoaround% %actor% %self.name% spits in %actor.name%'s face.
end
if %actor.fighting%
eval victim %actor.fighting%
if %actor.align% < %victim.align% && %victim.align% >= 0
emote screams 'PROTECT THE INNOCENT! BANZAI! CHARGE! ARARARAGGGHH!'
kill %actor.name%
end
end
end
end
~
#3010
Stock Fido - 3062, 3066~
0 b 100
~
set inroom %self.room%
set item %inroom.contents%
while %item%
* Target the next item in room. In case it is devoured.
set next_item %item.next_in_list%
* Check for a corpse. Corpse on TBA is vnum 65535. Stock is -1.
if %item.vnum(65535)%
emote savagely devours a corpse.
%purge% %item%
halt
end
set item %next_item%
* Loop back
done
~
#3011
Stock Janitor - 3061, 3068~
0 b 100
~
eval inroom %self.room%
eval item %inroom.contents%
while %item%
* Target the next item in room. In case it is picked up.
set next_item %item.next_in_list%
* TODO: if %item.wearflag(take)%
* Check for fountains and expensive items.
if %item.type% != FOUNTAIN && %item.cost% <= 15
take %item.name%
end
set item %next_item%
* Loop back
done
~
#3012
Newbie Tour Guide~
0 e 0
has entered the game.~
%echo% This trigger commandlist is not complete!
~
#3013
Newbie Tour Guide Loader~
0 e 0
has entered the game.~
* By Rumble of The Builder Academy tbamud.com 9091
* Num Arg 0 means the argument has to match exactly. So trig will only fire off:
* "has entered game." and not "has" or "entered" etc. (that would be num arg 1).
* Figure out what vnum the mob is in so we can use zoneecho.
eval inroom %self.room%
%zoneecho% %inroom.vnum% %self.name% shouts, 'Welcome, %actor.name%!'
~
#3014
Teleporter~
1 c 3
teleport~
* By Rumble and Jamie Nelson of The Builder Academy tbamud.com 9091
%send% %actor% You attempt to manipulate space and time.
%echoaround% %actor% %actor.name% attempts to manipulate space and time.
wait 1 sec
set sanctus 100
set jade 400
set newbie 500
set sea 600
set camelot 775
set nuclear 1800
set spider 1999
set arena 2000
set tower 2200
set memlin 2798
set mudschool 2800
set midgaard 3001
set capital 3702
set haven 3998
set chasm 4200
set arctic 4396
set Orc 4401
set monastery 4512
set ant 4600
set zodiac 5701
set grave 7401
set zamba 7500
set gidean 7801
set glumcoins 8301
set duke 8660
set oasis 9000
set domiae 9603
set northern 10004
set south 10101
set dbz 10301
set orchan 10401
set elcardo 10604
set iuel 10701
set omega 11501
set torres 11701
set dollhouse 11899
set hannah 12500
set maze 13001
set wyvern 14000
set caves 16999
set cardinal 17501
set circus 18700
set western 20001
set sapphire 20101
set kitchen 22001
set terringham 23200
set dragon 23300
set school 23400
set mines 23500
set aldin 23601
set crystal 23875
set pass 23901
set maura 24000
set enterprise 24100
set new 24200
set valley 24300
set prison 24457
set nether 24500
set yard 24700
set elven 24801
set jedi 24901
set dragonspyre 25000
set ape 25100
set vampyre 25200
set windmill 25300
set village 25400
set shipwreck 25516
set keep 25645
set jareth 25705
set light 25800
set mansion 25907
set grasslands 26000
set igor's 26100
set forest 26201
set farmlands 26300
set banshide 26400
set beach 26500
set ankou 26600
set vice 26728
set desert 26900
set wasteland 27001
set sundhaven 27119
set station 27300
set smurfville 27400
set sparta 27501
set shire 27700
set oceania 27800
set notre 27900
set motherboard 28000
set khanjar 28100
set kerjim 28200
set haunted 28300
set ghenna 28400
set hell 28601
set goblin 28700
set galaxy 28801
set werith's 28900
set lizard 29000
set black 29100
set kerofk 29202
set trade 29400
set jungle 29500
set froboz 29600
set desire 29801
set cathedral 29900
set ancalador 30000
set campus 30100
set bull 30401
set chessboard 30537
set tree 30600
set castle 30700
set baron 30800
set westlawn 30900
set graye 31003
set teeth 31100
set leper 31200
set altar 31400
set mcgintey 31500
set wharf 31700
set dock 31801
set yllnthad 31900
set bay 32200
set pale 32300
set army 32400
set revelry 32500
set perimeter 32600
set asylum 34501
set ultima 55685
set tarot 21101
if !%arg%
*they didnt type a location
set fail 1
else
*take the first word they type after the teleport command
*compare it to a variable above
eval loc %%%arg.car%%%
if !%loc%
*they typed an invalid location
set fail 1
end
end
if %fail%
%send% %actor% You fail.
%echoaround% %actor% %actor.name% fails.
halt
end
%echoaround% %actor% %actor.name% seems successful as %actor.heshe% steps into another realm.
%teleport% %actor% %loc%
%force% %actor% look
%echoaround% %actor% %actor.name% steps out of space and time.
~
#3015
Teleporter Recall and Return~
1 c 7
re~
* By Rumble of The Builder Academy tbamud.com 9091
if %cmd% == recall
eval teleporter_return_room %actor.room.vnum%
remote teleporter_return_room %actor.id%
%send% %actor% You recall to safety.
%echoaround% %actor% %actor.name% recalls.
%teleport% %actor% 3001
%force% %actor% look
%echoaround% %actor% %actor.name% appears in the room.
elseif %cmd% == return
%send% %actor% You return to your previous location.
%echoaround% %actor% %actor.name% teleports out of the room.
%teleport% %actor% %actor.teleporter_return_room%
%force% %actor% look
%echoaround% %actor% %actor.name% appears in the room.
else
return 0
end
~
#3016
Kind Soul Gives Newbie Equipment~
0 g 100
~
* By Rumble of The Builder Academy tbamud.com 9091
* If a player is < level 5 and naked it fully equips them. If < 5 and missing
* some equipment it will equip one spot.
if %actor.is_pc% && %actor.level% < 5
wait 2 sec
if !%actor.eq(*)%
say get some clothes on! Here, I will help.
%load% obj 3037 %actor% light
%load% obj 3083 %actor% rfinger
%load% obj 3083 %actor% lfinger
%load% obj 3082 %actor% neck1
%load% obj 3082 %actor% neck2
%load% obj 3040 %actor% body
%load% obj 3076 %actor% head
%load% obj 3080 %actor% legs
%load% obj 3084 %actor% feet
%load% obj 3071 %actor% hands
%load% obj 3086 %actor% arms
%load% obj 3042 %actor% shield
%load% obj 3087 %actor% about
%load% obj 3088 %actor% waist
%load% obj 3089 %actor% rwrist
%load% obj 3089 %actor% lwrist
%load% obj 3021 %actor% wield
%load% obj 3055 %actor% hold
halt
end
if !%actor.eq(light)%
say you really shouldn't be wandering these parts without a light source %actor.name%.
shake
%load% obj 3037
give candle %actor.name%
halt
end
if !%actor.eq(rfinger)% || !%actor.eq(lfinger)%
say did you lose one of your rings?
sigh
%load% obj 3083
give ring %actor.name%
halt
end
if !%actor.eq(neck1)% || !%actor.eq(neck2)%
say you lose everything don't you?
roll
%load% obj 3082
give neck %actor.name%
halt
end
if !%actor.eq(body)%
say you won't get far without some body armor %actor.name%.
%load% obj 3040
give plate %actor.name%
halt
end
if !%actor.eq(head)%
say protect that noggin of yours, %actor.name%.
%load% obj 3076
give cap %actor.name%
halt
end
if !%actor.eq(legs)%
say why do you always lose your pants %actor.name%?
%load% obj 3080
give leggings %actor.name%
halt
end
if !%actor.eq(feet)%
say you can't go around barefoot %actor.name%.
%load% obj 3084
give boots %actor.name%
halt
end
if !%actor.eq(hands)%
say need some gloves %actor.name%?
%load% obj 3071
give gloves %actor.name%
halt
end
if !%actor.eq(arms)%
say you must be freezing %actor.name%.
%load% obj 3086
give sleeve %actor.name%
halt
end
if !%actor.eq(shield)%
say you need one of these to protect yourself %actor.name%.
%load% obj 3042
give shield %actor.name%
halt
end
if !%actor.eq(about)%
say you are going to catch a cold %actor.name%.
%load% obj 3087
give cape %actor.name%
halt
end
if !%actor.eq(waist)%
say better use this to hold your pants up %actor.name%.
%load% obj 3088
give belt %actor.name%
halt
end
if !%actor.eq(rwrist)% || !%actor.eq(lwrist)%
say misplace something?
smile
%load% obj 3089
give wristguard %actor.name%
halt
end
if !%actor.eq(wield)%
say without a weapon you will be Fido food %actor.name%.
%load% obj 3021
give sword %actor.name%
halt
end
end
~
#3017
Mortal Greet~
2 s 100
~
* By Rumble of The Builder Academy tbamud.com 9091
* TBA mortal greet and equip. New players start at level 0.
wait 1 sec
if %actor.level% == 0
if !%actor.eq(*)%
%load% obj 3037 %actor% light
%load% obj 3083 %actor% rfinger
%load% obj 3083 %actor% lfinger
%load% obj 3082 %actor% neck1
%load% obj 3082 %actor% neck2
%load% obj 3040 %actor% body
%load% obj 3076 %actor% head
%load% obj 3080 %actor% legs
%load% obj 3084 %actor% feet
%load% obj 3071 %actor% hands
%load% obj 3086 %actor% arms
%load% obj 3042 %actor% shield
%load% obj 3087 %actor% about
%load% obj 3088 %actor% waist
%load% obj 3089 %actor% rwrist
%load% obj 3089 %actor% lwrist
%load% obj 3021 %actor% wield
%load% obj 3055 %actor% hold
end
if !%actor.has_item(3006)%
%load% obj 3006 %actor% inv
end
end
wait 3 sec
%zoneecho% 3001 A booming voice announces, 'Welcome %actor.name% to the realm!'
~
#3099
Test~
2 b 1
~
%zoneecho% 3001 You hear a loud --=BOOM=--,
~
$~

View file

@ -1,5 +1,5 @@
0.trg
1.trg
2.trg
30.trg
$

View file

@ -1,2 +1 @@
30.trg
$

View file

@ -726,7 +726,7 @@ map of the city can be seen upon one wall, giving the impression this would be a
good place to learn more about where certain landmarks are.
To the south lies Caravan Way, while to the east Wall Road can be seen.
~
1 131072 0 0 0 0
1 131080 0 0 0 0
D1
~
~

View file

@ -20,7 +20,7 @@ PROFILE =
CFLAGS = $(MYFLAGS) $(PROFILE)
OBJFILES = comm.o act.comm.o act.informative.o act.movement.o act.item.o \
act.offensive.o act.other.o act.social.o act.wizard.o ban.o boards.o \
act.offensive.o act.other.o act.social.o act.wizard.o set.o ban.o boards.o \
castle.o class.o config.o constants.o db.o fight.o graph.o handler.o \
house.o interpreter.o limits.o magic.o mail.o mobact.o modify.o \
objsave.o shop.o spec_assign.o spec_procs.o spell_parser.o \
@ -115,6 +115,10 @@ act.social.o: act.social.c conf.h sysdep.h structs.h utils.h comm.h \
act.wizard.o: act.wizard.c conf.h sysdep.h structs.h utils.h comm.h \
interpreter.h handler.h db.h spells.h house.h screen.h constants.h
$(CC) -c $(CFLAGS) act.wizard.c
set.o: set.c conf.h sysdep.h structs.h utils.h comm.h \
interpreter.h handler.h db.h constants.h genolc.h genwld.h genzon.h \
oasis.h improved-edit.h modify.h genobj.h dg_scripts.h set.h
$(CC) -c $(CFLAGS) set.c
ban.o: ban.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h handler.h db.h
$(CC) -c $(CFLAGS) ban.c
boards.o: boards.c conf.h sysdep.h structs.h utils.h comm.h db.h boards.h \

View file

@ -22,7 +22,7 @@ BINDIR = ^.bin
CFLAGS = $(MYFLAGS) $(PROFILE)
OBJFILES = o.comm act.o.comm act.o.informative act.o.movement act.o.item \
act.o.offensive act.o.other act.o.social act.o.wizard o.ban o.boards \
act.o.offensive act.o.other act.o.social act.o.wizard o.set o.ban o.boards \
o.castle o.class o.config o.constants o.db o.fight o.graph o.handler \
o.house o.interpreter o.limits o.magic o.mail o.mobact o.modify \
o.objsave o.random o.shop o.spec_assign o.spec_procs \
@ -73,6 +73,11 @@ act.o.wizard: act.c.wizard h.conf h.sysdep h.structs \
h.utils h.comm h.interpreter h.handler \
h.db h.spells h.house h.screen h.constants
$(CC) -c $(CFLAGS) act.c.wizard -o act.o.wizard
o.set: c.set h.conf h.sysdep h.structs \
h.utils h.comm h.interpreter h.handler \
h.db h.constants h.genobj h.genolc h.genwld h.genzon h.oasis \
h.improved-edit h.modify h.dg_scripts h.set
$(CC) -c $(CFLAGS) c.set -o o.set
o.ban: c.ban h.conf h.sysdep h.structs h.utils h.comm h.interpreter h.handler h.db
$(CC) -c $(CFLAGS) c.ban
o.boards: c.boards h.conf h.sysdep h.structs h.utils h.comm h.db h.boards \

View file

@ -49,6 +49,7 @@ Dep_circledexe = \
act.informative.obj\
act.offensive.obj\
act.other.obj\
set.obj\
boards.obj\
ban.obj\
act.wizard.obj\
@ -93,6 +94,7 @@ act.item.obj+
act.informative.obj+
act.offensive.obj+
act.other.obj+
set.obj+
boards.obj+
ban.obj+
act.wizard.obj+
@ -163,6 +165,11 @@ act.other.obj : act.other.c
$(CompOptsAt_circledexe) $(CompInheritOptsAt_circledexe) -o$@ act.other.c
|
set.obj : set.c
$(BCC32) -P- -c @&&|
$(CompOptsAt_circledexe) $(CompInheritOptsAt_circledexe) -o$@ set.c
|
boards.obj : boards.c
$(BCC32) -P- -c @&&|
$(CompOptsAt_circledexe) $(CompInheritOptsAt_circledexe) -o$@ boards.c
@ -339,5 +346,3 @@ BccW32.cfg :
-WC
-g0
| $@

View file

@ -50,6 +50,7 @@ Dep_circledexe = \
act.informative.obj\
act.offensive.obj\
act.other.obj\
set.obj\
boards.obj\
ban.obj\
act.wizard.obj\
@ -94,6 +95,7 @@ act.item.obj+
act.informative.obj+
act.offensive.obj+
act.other.obj+
set.obj+
boards.obj+
ban.obj+
act.wizard.obj+
@ -162,6 +164,11 @@ act.other.obj : act.other.c
$(CompOptsAt_circledexe) $(CompInheritOptsAt_circledexe) -o$@ act.other.c
|
set.obj : set.c
$(BCC32) -P- -c @&&|
$(CompOptsAt_circledexe) $(CompInheritOptsAt_circledexe) -o$@ set.c
|
boards.obj : boards.c
$(BCC32) -P- -c @&&|
$(CompOptsAt_circledexe) $(CompInheritOptsAt_circledexe) -o$@ boards.c
@ -339,5 +346,3 @@ BccW32.cfg :
-WC
-g0
| $@

View file

@ -48,6 +48,7 @@ OBJS=\
boards.obj \
ban.obj \
act.wizard.obj \
set.obj \
act.social.obj \
act.other.obj \
act.offensive.obj \
@ -494,6 +495,29 @@ ACT_WIZARD_C=\
act.wizard.obj: $(ACT_WIZARD_C) $(DISTDIR)\src\act.wizard.c
$(CC) $(CFLAGS) $(DISTDIR)\src\act.wizard.c
# Build SET.C
SET_C=\
$(DISTDIR)\src\sysdep.h\
$(DISTDIR)\src\structs.h\
$(DISTDIR)\src\utils.h\
$(DISTDIR)\src\comm.h\
$(DISTDIR)\src\interpreter.h\
$(DISTDIR)\src\handler.h\
$(DISTDIR)\src\db.h\
$(DISTDIR)\src\constants.h\
$(DISTDIR)\src\genobj.h\
$(DISTDIR)\src\genolc.h\
$(DISTDIR)\src\genwld.h\
$(DISTDIR)\src\genzon.h\
$(DISTDIR)\src\oasis.h\
$(DISTDIR)\src\improved-edit.h\
$(DISTDIR)\src\modify.h\
$(DISTDIR)\src\dg_scripts.h\
$(DISTDIR)\src\set.h\
set.obj: $(SET_C) $(DISTDIR)\src\set.c
$(CC) $(CFLAGS) $(DISTDIR)\src\set.c
# Build ACT.SOCIAL.C
ACT_SOCIAL_C=\
$(DISTDIR)\src\sysdep.h\
@ -598,4 +622,3 @@ ACT_COMM_C=\
act.comm.obj: $(ACT_COMM_C) $(DISTDIR)\src\act.comm.c
$(CC) $(CFLAGS) $(DISTDIR)\src\act.comm.c

View file

@ -37,6 +37,7 @@ MAKE = $(MAKE) /NOLOGO /S
OBJFILES = comm.obj act.comm.obj act.informative.obj act.movement.obj act.item.obj \
asciimap.obj act.offensive.obj act.other.obj act.social.obj act.wizard.obj \
set.obj \
ban.obj boards.obj castle.obj class.obj config.obj constants.obj db.obj \
dg_event.obj dg_scripts.obj dg_triggers.obj fight.obj genolc.obj graph.obj \
handler.obj house.obj ibt.obj interpreter.obj limits.obj lists.obj magic.obj \
@ -83,6 +84,10 @@ act.social.obj: act.social.c conf.h sysdep.h structs.h utils.h comm.h \
act.wizard.obj: act.wizard.c conf.h sysdep.h structs.h utils.h comm.h \
interpreter.h handler.h db.h spells.h house.h screen.h constants.h
$(CC) -c $(CFLAGS) act.wizard.c
set.obj: set.c conf.h sysdep.h structs.h utils.h comm.h \
interpreter.h handler.h db.h constants.h genolc.h genwld.h genzon.h \
oasis.h improved-edit.h modify.h genobj.h dg_scripts.h set.h
$(CC) -c $(CFLAGS) set.c
ban.obj: ban.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h handler.h db.h
$(CC) -c $(CFLAGS) ban.c
boards.obj: boards.c conf.h sysdep.h structs.h utils.h comm.h db.h boards.h \

View file

@ -22,7 +22,7 @@ LIB = -lsocket
CFLAGS = $(MYFLAGS) $(PROFILE)
OBJFILES = comm.o act.comm.o act.informative.o act.movement.o act.item.o \
act.offensive.o act.other.o act.social.o act.wizard.o ban.o boards.o \
act.offensive.o act.other.o act.social.o act.wizard.o set.o ban.o boards.o \
castle.o class.o config.o constants.o db.o fight.o graph.o handler.o \
house.o interpreter.o limits.o magic.o mail.o mobact.o modify.o \
objsave.o shop.o spec_assign.o spec_procs.o spell_parser.o \
@ -117,6 +117,10 @@ act.social.o: act.social.c conf.h sysdep.h structs.h utils.h comm.h \
act.wizard.o: act.wizard.c conf.h sysdep.h structs.h utils.h comm.h \
interpreter.h handler.h db.h spells.h house.h screen.h constants.h
$(CC) -c $(CFLAGS) act.wizard.c
set.o: set.c conf.h sysdep.h structs.h utils.h comm.h \
interpreter.h handler.h db.h constants.h genolc.h genwld.h genzon.h \
oasis.h improved-edit.h modify.h genobj.h dg_scripts.h set.h
$(CC) -c $(CFLAGS) set.c
ban.o: ban.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h handler.h db.h
$(CC) -c $(CFLAGS) ban.c
boards.o: boards.c conf.h sysdep.h structs.h utils.h comm.h db.h boards.h \

View file

@ -18,7 +18,7 @@ CFLAGS = NOLINK $(MYFLAGS) $(PROFILE)
MAKE = SMAKE
OBJFILES = comm.o act.comm.o act.informative.o act.movement.o act.item.o \
act.offensive.o act.other.o act.social.o act.wizard.o ban.o boards.o \
act.offensive.o act.other.o act.social.o act.wizard.o set.o ban.o boards.o \
castle.o class.o config.o constants.o db.o fight.o graph.o handler.o \
house.o interpreter.o limits.o magic.o mail.o mobact.o modify.o \
objsave.o olc.o shop.o spec_assign.o spec_procs.o spell_parser.o \
@ -109,6 +109,10 @@ act.social.o: act.social.c conf.h sysdep.h structs.h utils.h comm.h \
act.wizard.o: act.wizard.c conf.h sysdep.h structs.h utils.h comm.h \
interpreter.h handler.h db.h spells.h house.h screen.h
$(CC) $(CFLAGS) act.wizard.c
set.o: set.c conf.h sysdep.h structs.h utils.h comm.h \
interpreter.h handler.h db.h constants.h genolc.h genwld.h genzon.h \
oasis.h improved-edit.h modify.h genobj.h dg_scripts.h set.h
$(CC) $(CFLAGS) set.c
ban.o: ban.c conf.h sysdep.h structs.h utils.h comm.h interpreter.h handler.h db.h
$(CC) $(CFLAGS) ban.c
boards.o: boards.c conf.h sysdep.h structs.h utils.h comm.h db.h boards.h \
@ -189,4 +193,3 @@ utils.o: utils.c conf.h sysdep.h structs.h utils.h comm.h screen.h spells.h \
weather.o: weather.c conf.h sysdep.h structs.h utils.h comm.h handler.h \
interpreter.h db.h
$(CC) $(CFLAGS) weather.c

View file

@ -19,6 +19,7 @@
#define _ACT_H_
#include "utils.h" /* for the ACMD macro */
#include "set.h"
#ifndef MAX_EMOTE_TOKENS
#define MAX_EMOTE_TOKENS 16
@ -175,12 +176,18 @@ ACMD(do_gen_door);
/* Functions without subcommands */
ACMD(do_enter);
ACMD(do_follow);
ACMD(do_hitch);
ACMD(do_leave);
ACMD(do_mount);
ACMD(do_move);
ACMD(do_pack);
ACMD(do_rest);
ACMD(do_dismount);
ACMD(do_sit);
ACMD(do_sleep);
ACMD(do_stand);
ACMD(do_unhitch);
ACMD(do_unpack);
ACMD(do_unfollow);
ACMD(do_wake);
/* Global variables from act.movement.c */
@ -257,6 +264,8 @@ ACMD(do_group);
ACMD(do_hide);
ACMD(do_listen);
ACMD(do_not_here);
ACMD(do_change);
ACMD(do_reroll);
ACMD(do_report);
ACMD(do_save);
ACMD(do_skills);
@ -342,7 +351,7 @@ ACMD(do_wizutil);
#define SCMD_THAW 4
#define SCMD_UNAFFECT 5
/* Functions without subcommands */
ACMD(do_acaudit);
ACMD(do_audit);
ACMD(do_advance);
ACMD(do_at);
ACMD(do_checkloadstatus);

View file

@ -41,6 +41,8 @@ static void look_at_target(struct char_data *ch, char *arg);
static void look_in_direction(struct char_data *ch, int dir);
static void look_in_obj(struct char_data *ch, char *arg);
static void look_at_tables(struct char_data *ch);
static bool look_can_spot_hidden(struct char_data *ch, struct char_data *tch, room_rnum room);
static bool look_list_direction_chars(struct char_data *ch, room_rnum room);
/* do_look, do_inventory utility functions */
static void list_obj_to_char(struct obj_data *list, struct char_data *ch, int mode, int show);
/* do_look, do_equipment, do_examine, do_inventory */
@ -101,8 +103,11 @@ static void show_obj_to_char(struct obj_data *obj, struct char_data *ch, int mod
found++;
}
if (found) {
send_to_char(ch, "You are %s upon %s.", GET_POS(ch) == POS_SITTING ? "sitting" :
"resting", obj->short_description);
const char *pos = (GET_POS(ch) == POS_STANDING ? "standing" :
(GET_POS(ch) == POS_SITTING ? "sitting" :
(GET_POS(ch) == POS_SLEEPING ? "sleeping" : "resting")));
const char *prep = (GET_POS(ch) == POS_STANDING || GET_POS(ch) == POS_SITTING) ? "at" : "upon";
send_to_char(ch, "You are %s %s %s.", pos, prep, obj->short_description);
goto end;
}
}
@ -234,9 +239,6 @@ static void show_obj_modifiers(struct obj_data *obj, struct char_data *ch)
if (OBJ_FLAGGED(obj, ITEM_INVISIBLE))
send_to_char(ch, " (invisible)");
if (OBJ_FLAGGED(obj, ITEM_BLESS) && AFF_FLAGGED(ch, AFF_DETECT_ALIGN))
send_to_char(ch, " ..It glows blue!");
if (OBJ_FLAGGED(obj, ITEM_MAGIC) && AFF_FLAGGED(ch, AFF_DETECT_MAGIC))
send_to_char(ch, " ..It glows yellow!");
@ -567,17 +569,62 @@ static void list_one_char(struct char_data *i, struct char_data *ch)
CCNRM(ch, C_NRM));
}
/* NPCs with a full long description at default position: print that and bail. */
if (IS_NPC(i) && i->player.long_descr && GET_POS(i) == GET_DEFAULT_POS(i)) {
if (AFF_FLAGGED(i, AFF_MOUNTED) && MOUNT(i) &&
IN_ROOM(MOUNT(i)) == IN_ROOM(i) &&
MOB_FLAGGED(MOUNT(i), MOB_MOUNT)) {
const char *rdesc = get_char_sdesc(i);
const char *mdesc = get_char_sdesc(MOUNT(i));
if (rdesc && *rdesc)
send_to_char(ch, "%c%s", UPPER(*rdesc), rdesc + 1);
else
send_to_char(ch, "Someone");
if (mdesc && *mdesc)
send_to_char(ch, " is riding %s here.", mdesc);
else
send_to_char(ch, " is riding someone here.");
if (AFF_FLAGGED(i, AFF_INVISIBLE))
send_to_char(ch, " (invisible)");
if (AFF_FLAGGED(i, AFF_HIDE))
send_to_char(ch, " (hidden)");
if (!IS_NPC(i) && !i->desc)
send_to_char(ch, " (linkless)");
if (!IS_NPC(i) && PLR_FLAGGED(i, PLR_WRITING))
send_to_char(ch, " (writing)");
if (!IS_NPC(i) && PRF_FLAGGED(i, PRF_BUILDWALK))
send_to_char(ch, " (buildwalk)");
if (!IS_NPC(i) && PRF_FLAGGED(i, PRF_AFK))
send_to_char(ch, " (AFK)");
send_to_char(ch, "\r\n");
if (AFF_FLAGGED(i, AFF_SANCTUARY))
act("...$e glows with a bright light!", FALSE, i, 0, ch, TO_VICT);
return;
}
/* Custom ldesc overrides position-based output. */
if (i->char_specials.custom_ldesc && i->player.long_descr) {
if (AFF_FLAGGED(i, AFF_INVISIBLE))
send_to_char(ch, "*");
if (AFF_FLAGGED(ch, AFF_DETECT_ALIGN)) {
if (IS_EVIL(i))
send_to_char(ch, "(Red Aura) ");
else if (IS_GOOD(i))
send_to_char(ch, "(Blue Aura) ");
}
send_to_char(ch, "%s", i->player.long_descr);
if (AFF_FLAGGED(i, AFF_SANCTUARY))
act("...$e glows with a bright light!", FALSE, i, 0, ch, TO_VICT);
if (AFF_FLAGGED(i, AFF_BLIND) && GET_LEVEL(i) < LVL_IMMORT)
act("...$e is groping around blindly!", FALSE, i, 0, ch, TO_VICT);
return;
}
/* Characters with a full long description at default position: print that and bail. */
if (i->player.long_descr && GET_POS(i) == GET_DEFAULT_POS(i)) {
if (AFF_FLAGGED(i, AFF_INVISIBLE))
send_to_char(ch, "*");
send_to_char(ch, "%s", i->player.long_descr);
@ -618,9 +665,13 @@ static void list_one_char(struct char_data *i, struct char_data *ch)
send_to_char(ch, "%s", positions[(int) GET_POS(i)]);
else {
furniture = SITTING(i);
send_to_char(ch, " is %s upon %s.", (GET_POS(i) == POS_SLEEPING ?
"sleeping" : (GET_POS(i) == POS_RESTING ? "resting" : "sitting")),
OBJS(furniture, ch));
{
const char *pos = (GET_POS(i) == POS_STANDING ? "standing" :
(GET_POS(i) == POS_SITTING ? "sitting" :
(GET_POS(i) == POS_SLEEPING ? "sleeping" : "resting")));
const char *prep = (GET_POS(i) == POS_STANDING || GET_POS(i) == POS_SITTING) ? "at" : "upon";
send_to_char(ch, " is %s %s %s.", pos, prep, OBJS(furniture, ch));
}
}
} else {
if (FIGHTING(i)) {
@ -637,19 +688,115 @@ static void list_one_char(struct char_data *i, struct char_data *ch)
send_to_char(ch, " is here struggling with thin air.");
}
if (AFF_FLAGGED(ch, AFF_DETECT_ALIGN)) {
if (IS_EVIL(i))
send_to_char(ch, " (Red Aura)");
else if (IS_GOOD(i))
send_to_char(ch, " (Blue Aura)");
}
send_to_char(ch, "\r\n");
if (AFF_FLAGGED(i, AFF_SANCTUARY))
act("...$e glows with a bright light!", FALSE, i, 0, ch, TO_VICT);
}
static void strip_trailing_crlf(char *str)
{
size_t len;
if (!str)
return;
len = strlen(str);
while (len > 0 && (str[len - 1] == '\n' || str[len - 1] == '\r'))
str[--len] = '\0';
}
static void build_current_ldesc(const struct char_data *ch, char *out, size_t outsz)
{
struct obj_data *furniture;
const char *positions[] = {
" is lying here, dead.",
" is lying here, mortally wounded.",
" is lying here, incapacitated.",
" is lying here, stunned.",
" is sleeping here.",
" is resting here.",
" is sitting here.",
" is here, fighting someone.",
" is standing here."
};
const char *sdesc;
char base[MAX_INPUT_LENGTH];
if (!out || outsz == 0)
return;
*out = '\0';
if (!ch) {
strlcpy(out, "Someone is here.", outsz);
return;
}
if (AFF_FLAGGED(ch, AFF_MOUNTED) && MOUNT(ch) &&
IN_ROOM(MOUNT(ch)) == IN_ROOM(ch) &&
MOB_FLAGGED(MOUNT(ch), MOB_MOUNT)) {
const char *rdesc = get_char_sdesc(ch);
const char *mdesc = get_char_sdesc(MOUNT(ch));
if (!rdesc || !*rdesc)
rdesc = "someone";
if (!mdesc || !*mdesc)
mdesc = "someone";
snprintf(out, outsz, "%c%s is riding %s here.",
UPPER(*rdesc), rdesc + 1, mdesc);
strip_trailing_crlf(out);
return;
}
if (ch->char_specials.custom_ldesc && ch->player.long_descr) {
strlcpy(out, ch->player.long_descr, outsz);
strip_trailing_crlf(out);
return;
}
if (ch->player.long_descr && GET_POS(ch) == GET_DEFAULT_POS(ch)) {
strlcpy(out, ch->player.long_descr, outsz);
strip_trailing_crlf(out);
return;
}
sdesc = get_char_sdesc(ch);
if (!sdesc || !*sdesc)
sdesc = "someone";
strlcpy(base, sdesc, sizeof(base));
if (*base)
base[0] = UPPER(*base);
if (GET_POS(ch) != POS_FIGHTING) {
if (!SITTING(ch)) {
snprintf(out, outsz, "%s%s", base, positions[(int) GET_POS(ch)]);
} else {
furniture = SITTING(ch);
{
const char *pos = (GET_POS(ch) == POS_STANDING ? "standing" :
(GET_POS(ch) == POS_SITTING ? "sitting" :
(GET_POS(ch) == POS_SLEEPING ? "sleeping" : "resting")));
const char *prep = (GET_POS(ch) == POS_STANDING || GET_POS(ch) == POS_SITTING) ? "at" : "upon";
snprintf(out, outsz, "%s is %s %s %s.", base, pos, prep, OBJS(furniture, ch));
}
}
} else {
if (FIGHTING(ch)) {
if (IN_ROOM(ch) == IN_ROOM(FIGHTING(ch)))
snprintf(out, outsz, "%s is here, fighting %s!", base, PERS(FIGHTING(ch), ch));
else
snprintf(out, outsz, "%s is here, fighting someone who has already left!", base);
} else {
snprintf(out, outsz, "%s is here, fighting someone who has already left!", base);
}
}
strip_trailing_crlf(out);
}
static void list_char_to_char(struct char_data *list, struct char_data *ch)
{
struct char_data *i;
@ -660,6 +807,9 @@ static void list_char_to_char(struct char_data *list, struct char_data *ch)
if (!IS_NPC(ch) && !PRF_FLAGGED(ch, PRF_HOLYLIGHT) &&
IS_NPC(i) && i->player.long_descr && *i->player.long_descr == '.')
continue;
if (RIDDEN_BY(i) && IN_ROOM(RIDDEN_BY(i)) == IN_ROOM(i) &&
CAN_SEE(ch, RIDDEN_BY(i)))
continue;
send_to_char(ch, "%s", CCYEL(ch, C_NRM));
if (CAN_SEE(ch, i))
list_one_char(i, ch);
@ -809,18 +959,123 @@ void look_at_room(struct char_data *ch, int ignore_brief)
static void look_in_direction(struct char_data *ch, int dir)
{
if (EXIT(ch, dir)) {
if (EXIT(ch, dir)->general_description)
send_to_char(ch, "%s", EXIT(ch, dir)->general_description);
else
send_to_char(ch, "You see nothing special.\r\n");
static const char *range_labels[] = { "[near]", "[far]", "[very far]" };
room_rnum room = IN_ROOM(ch);
struct room_direction_data *start_exit = W_EXIT(room, dir);
const char *door_name = NULL;
int distance;
bool blocked = FALSE;
if (EXIT_FLAGGED(EXIT(ch, dir), EX_CLOSED) && EXIT(ch, dir)->keyword)
send_to_char(ch, "The %s is closed.\r\n", fname(EXIT(ch, dir)->keyword));
else if (EXIT_FLAGGED(EXIT(ch, dir), EX_ISDOOR) && EXIT(ch, dir)->keyword)
send_to_char(ch, "The %s is open.\r\n", fname(EXIT(ch, dir)->keyword));
} else
send_to_char(ch, "Nothing special there...\r\n");
if (start_exit && start_exit->to_room != NOWHERE &&
EXIT_FLAGGED(start_exit, EX_ISDOOR) &&
(!EXIT_FLAGGED(start_exit, EX_HIDDEN) || PRF_FLAGGED(ch, PRF_HOLYLIGHT))) {
door_name = start_exit->keyword ? fname(start_exit->keyword) : "door";
if (EXIT_FLAGGED(start_exit, EX_CLOSED)) {
send_to_char(ch, "You look to the %s and see the %s is closed.\r\n", dirs[dir], door_name);
return;
}
send_to_char(ch, "You look to the %s and see the %s is open:\r\n", dirs[dir], door_name);
} else {
send_to_char(ch, "You look to the %s and see:\r\n", dirs[dir]);
}
for (distance = 0; distance < 3; distance++) {
struct room_direction_data *exit = NULL;
if (!blocked) {
exit = W_EXIT(room, dir);
if (!exit || exit->to_room == NOWHERE)
blocked = TRUE;
else if (EXIT_FLAGGED(exit, EX_HIDDEN) && !PRF_FLAGGED(ch, PRF_HOLYLIGHT))
blocked = TRUE;
else if (EXIT_FLAGGED(exit, EX_CLOSED))
blocked = TRUE;
else
room = exit->to_room;
}
if (blocked || !VALID_ROOM_RNUM(room)) {
if (distance == 0)
send_to_char(ch, "nothing\r\n");
break;
}
send_to_char(ch, "%s\r\n", range_labels[distance]);
if (!look_list_direction_chars(ch, room))
send_to_char(ch, "nothing\r\n");
}
}
static bool look_can_spot_hidden(struct char_data *ch, struct char_data *tch, room_rnum room)
{
int total;
if (!ch || !tch)
return FALSE;
if (!AFF_FLAGGED(tch, AFF_HIDE))
return FALSE;
if (!GET_SKILL(ch, SKILL_PERCEPTION))
return FALSE;
if (AFF_FLAGGED(ch, AFF_BLIND))
return FALSE;
if (IS_DARK(room) && !CAN_SEE_IN_DARK(ch))
return FALSE;
total = roll_scan_perception(ch);
if (AFF_FLAGGED(ch, AFF_SCAN))
total += 5; /* Match scan bonus from other detection checks. */
if (GET_STEALTH_CHECK(tch) <= 0)
SET_STEALTH_CHECK(tch, 5);
return (total >= (GET_STEALTH_CHECK(tch) + 2));
}
static bool look_list_direction_chars(struct char_data *ch, room_rnum room)
{
struct char_data *tch;
bool found = FALSE;
if (!VALID_ROOM_RNUM(room))
return FALSE;
if (IS_DARK(room) && !CAN_SEE_IN_DARK(ch))
return FALSE;
for (tch = world[room].people; tch; tch = tch->next_in_room) {
if (tch == ch)
continue;
if (!IS_NPC(ch) && !PRF_FLAGGED(ch, PRF_HOLYLIGHT) &&
IS_NPC(tch) && tch->player.long_descr && *tch->player.long_descr == '.')
continue;
if (RIDDEN_BY(tch) && IN_ROOM(RIDDEN_BY(tch)) == room &&
CAN_SEE(ch, RIDDEN_BY(tch)))
continue;
if (AFF_FLAGGED(tch, AFF_HIDE)) {
if (CAN_SEE(ch, tch) || look_can_spot_hidden(ch, tch, room)) {
char hidden_ldesc[MAX_STRING_LENGTH];
if (build_hidden_ldesc(tch, hidden_ldesc, sizeof(hidden_ldesc)))
send_to_char(ch, "%s", hidden_ldesc);
else
send_to_char(ch, "a shadowy figure\r\n");
found = TRUE;
}
continue;
}
if (CAN_SEE(ch, tch)) {
char ldesc[MAX_STRING_LENGTH];
build_current_ldesc(tch, ldesc, sizeof(ldesc));
send_to_char(ch, "%s\r\n", ldesc);
found = TRUE;
}
}
return found;
}
static void look_in_obj(struct char_data *ch, char *arg)
@ -961,6 +1216,42 @@ static void look_at_target(struct char_data *ch, char *arg)
return;
}
if (!found_char) {
struct char_data *tch;
char tmp[MAX_INPUT_LENGTH];
char *name = tmp;
int matchnum;
strlcpy(tmp, arg, sizeof(tmp));
matchnum = get_number(&name);
if (matchnum > 0) {
for (tch = world[IN_ROOM(ch)].people; tch && matchnum; tch = tch->next_in_room) {
if (tch == ch)
continue;
if (!AFF_FLAGGED(tch, AFF_MOUNTED))
continue;
if (!CAN_SEE(ch, tch))
continue;
if (isname(name, (char *)get_char_sdesc(tch))) {
if (--matchnum == 0) {
found_char = tch;
break;
}
}
}
}
}
if (found_char != NULL) {
look_at_char(found_char, ch);
if (ch != found_char) {
if (CAN_SEE(found_char, ch))
act("$n looks at you.", TRUE, ch, 0, found_char, TO_VICT);
act("$n looks at $N.", TRUE, ch, 0, found_char, TO_NOTVICT);
}
return;
}
/* Strip off "number." from 2.foo and friends. */
if (!(fnum = get_number(&arg))) {
send_to_char(ch, "Look at what?\r\n");
@ -1144,9 +1435,14 @@ ACMD(do_coins)
ACMD(do_score)
{
struct time_info_data playing_time;
time_t played_seconds;
int played_minutes;
int played_hours;
int played_days;
struct ac_breakdown acb;
bool ismob = IS_NPC(ch);
char ldesc[MAX_STRING_LENGTH];
const char *sdesc = get_char_sdesc(ch);
compute_ac_breakdown(ch, &acb);
@ -1154,11 +1450,22 @@ ACMD(do_score)
"\r\n"
"====================[ Score ]====================\r\n");
build_current_ldesc(ch, ldesc, sizeof(ldesc));
send_to_char(ch, "Name: %s\r\n", GET_NAME(ch) ? GET_NAME(ch) : "someone");
send_to_char(ch, "Sdesc: %s\r\n", sdesc && *sdesc ? sdesc : "someone");
send_to_char(ch, "Ldesc: %s\r\n", *ldesc ? ldesc : "None");
send_to_char(ch, "Weight: %d Height: %d\r\n",
GET_WEIGHT(ch), GET_HEIGHT(ch));
send_to_char(ch,
"HP: %d/%d Mana: %d/%d Move: %d/%d\r\n",
"HP: %d/%d Mana: %d/%d Stamina: %d/%d\r\n",
GET_HIT(ch), GET_MAX_HIT(ch),
GET_MANA(ch), GET_MAX_MANA(ch),
GET_MOVE(ch), GET_MAX_MOVE(ch));
GET_STAMINA(ch), GET_MAX_STAMINA(ch));
send_to_char(ch, "Carrying Weight: %d/%d\r\n",
IS_CARRYING_W(ch), CAN_CARRY_W(ch));
/* Abilities and 5e modifiers */
send_to_char(ch,
@ -1189,36 +1496,31 @@ ACMD(do_score)
send_to_char(ch, "Stealth Disadvantage: %s\r\n",
has_stealth_disadv(ch) ? "Yes" : "No");
send_to_char(ch, "You are %d years old.", GET_AGE(ch));
send_to_char(ch, "You are %d years old.", GET_ROLEPLAY_AGE(ch));
if (age(ch)->month == 0 && age(ch)->day == 0)
send_to_char(ch, " It's your birthday today.\r\n");
else
send_to_char(ch, "\r\n");
send_to_char(ch, "\r\n");
/* Only players have quest data */
if (!ismob) {
send_to_char(ch, "You have %d questpoints.\r\n", GET_QUESTPOINTS(ch));
if (GET_QUEST(ch) == NOTHING)
send_to_char(ch, "You are not on a quest at the moment.\r\n");
else {
send_to_char(ch, "Your current quest is: %s",
QST_NAME(real_quest(GET_QUEST(ch))));
if (PRF_FLAGGED(ch, PRF_SHOWVNUMS))
send_to_char(ch, " [%d]\r\n", GET_QUEST(ch));
else
send_to_char(ch, "\r\n");
}
if (!ismob && GET_QUEST(ch) != NOTHING) {
send_to_char(ch, "Your current quest is: %s",
QST_NAME(real_quest(GET_QUEST(ch))));
if (PRF_FLAGGED(ch, PRF_SHOWVNUMS))
send_to_char(ch, " [%d]\r\n", GET_QUEST(ch));
else
send_to_char(ch, "\r\n");
}
/* Only players have valid playtime data */
if (!ismob) {
playing_time = *real_time_passed((time(0) - ch->player.time.logon) +
ch->player.time.played, 0);
send_to_char(ch, "You have been playing for %d day%s and %d hour%s.\r\n",
playing_time.day, playing_time.day == 1 ? "" : "s",
playing_time.hours, playing_time.hours == 1 ? "" : "s");
played_seconds = get_total_played_seconds(ch);
played_minutes = (played_seconds / SECS_PER_REAL_MIN) % 60;
played_hours = (played_seconds / SECS_PER_REAL_HOUR) % 24;
played_days = played_seconds / SECS_PER_REAL_DAY;
send_to_char(ch,
"You have been playing for %d minute%s, %d hour%s, and %d day%s.\r\n",
played_minutes, played_minutes == 1 ? "" : "s",
played_hours, played_hours == 1 ? "" : "s",
played_days, played_days == 1 ? "" : "s");
}
/* Position */
@ -1246,7 +1548,7 @@ ACMD(do_score)
send_to_char(ch, "You are sitting.\r\n");
else {
struct obj_data *furniture = SITTING(ch);
send_to_char(ch, "You are sitting upon %s.\r\n", furniture->short_description);
send_to_char(ch, "You are sitting at %s.\r\n", furniture->short_description);
}
break;
case POS_FIGHTING:
@ -1254,7 +1556,13 @@ ACMD(do_score)
FIGHTING(ch) ? PERS(FIGHTING(ch), ch) : "thin air");
break;
case POS_STANDING:
send_to_char(ch, "You are standing.\r\n");
if (AFF_FLAGGED(ch, AFF_MOUNTED) && MOUNT(ch) &&
IN_ROOM(MOUNT(ch)) == IN_ROOM(ch) &&
MOB_FLAGGED(MOUNT(ch), MOB_MOUNT)) {
send_to_char(ch, "You are riding %s.\r\n", get_char_sdesc(MOUNT(ch)));
} else {
send_to_char(ch, "You are standing.\r\n");
}
break;
default:
send_to_char(ch, "You are floating.\r\n");
@ -1589,8 +1897,6 @@ ACMD(do_who)
continue;
if (!CAN_SEE(ch, tch) || GET_LEVEL(tch) < low || GET_LEVEL(tch) > high)
continue;
if (outlaws && !PLR_FLAGGED(tch, PLR_KILLER) && !PLR_FLAGGED(tch, PLR_THIEF))
continue;
if (questwho && !PRF_FLAGGED(tch, PRF_QUEST))
continue;
if (localwho && world[IN_ROOM(ch)].zone != world[IN_ROOM(tch)].zone)
@ -1635,8 +1941,6 @@ ACMD(do_who)
continue;
if (!CAN_SEE(ch, tch) || GET_LEVEL(tch) < low || GET_LEVEL(tch) > high)
continue;
if (outlaws && !PLR_FLAGGED(tch, PLR_KILLER) && !PLR_FLAGGED(tch, PLR_THIEF))
continue;
if (questwho && !PRF_FLAGGED(tch, PRF_QUEST))
continue;
if (localwho && world[IN_ROOM(ch)].zone != world[IN_ROOM(tch)].zone)
@ -1709,10 +2013,6 @@ ACMD(do_who)
send_to_char(ch, " (noshout)");
if (PRF_FLAGGED(tch, PRF_QUEST))
send_to_char(ch, " (quest)");
if (PLR_FLAGGED(tch, PLR_THIEF))
send_to_char(ch, " (THIEF)");
if (PLR_FLAGGED(tch, PLR_KILLER))
send_to_char(ch, " (KILLER)");
send_to_char(ch, "\r\n");
}
}
@ -1820,9 +2120,6 @@ ACMD(do_users)
continue;
if (!CAN_SEE(ch, tch) || GET_LEVEL(tch) < low || GET_LEVEL(tch) > high)
continue;
if (outlaws && !PLR_FLAGGED(tch, PLR_KILLER) &&
!PLR_FLAGGED(tch, PLR_THIEF))
continue;
if (showclass && !(showclass & (1 << GET_CLASS(tch))))
continue;
if (GET_INVIS_LEV(tch) > GET_LEVEL(ch))
@ -2340,7 +2637,7 @@ ACMD(do_toggle)
" Brief: %-3s "
" Summonable: %-3s\r\n"
" Move Display: %-3s "
" Stamina Display: %-3s "
" Compact: %-3s "
" Quest: %-3s\r\n"
@ -2369,7 +2666,7 @@ ACMD(do_toggle)
ONOFF(PRF_FLAGGED(ch, PRF_BRIEF)),
ONOFF(PRF_FLAGGED(ch, PRF_SUMMONABLE)),
ONOFF(PRF_FLAGGED(ch, PRF_DISPMOVE)),
ONOFF(PRF_FLAGGED(ch, PRF_DISPSTAMINA)),
ONOFF(PRF_FLAGGED(ch, PRF_COMPACT)),
ONOFF(PRF_FLAGGED(ch, PRF_QUEST)),

View file

@ -920,6 +920,9 @@ void weight_change_object(struct obj_data *obj, int weight)
obj_from_char(obj);
GET_OBJ_WEIGHT(obj) += weight;
obj_to_char(obj, tmp_ch);
} else if ((tmp_ch = obj->worn_by)) {
IS_CARRYING_W(tmp_ch) += weight;
GET_OBJ_WEIGHT(obj) += weight;
} else if ((tmp_obj = obj->in_obj)) {
obj_from_obj(obj);
GET_OBJ_WEIGHT(obj) += weight;
@ -2162,7 +2165,7 @@ ACMD(do_forage)
prof_bonus = GET_PROFICIENCY(GET_SKILL(ch, SKILL_SURVIVAL));
cost = MAX(1, 10 - prof_bonus);
if (!IS_NPC(ch) && GET_MOVE(ch) < cost) {
if (!IS_NPC(ch) && GET_STAMINA(ch) < cost) {
send_to_char(ch, "You are too exhausted to forage.\r\n");
return;
}
@ -2171,7 +2174,7 @@ ACMD(do_forage)
WAIT_STATE(ch, delay_seconds * PASSES_PER_SEC);
if (!IS_NPC(ch))
GET_MOVE(ch) = MAX(0, GET_MOVE(ch) - cost);
GET_STAMINA(ch) = MAX(0, GET_STAMINA(ch) - cost);
total = roll_skill_check(ch, SKILL_SURVIVAL, 0, NULL);

View file

@ -44,6 +44,13 @@ static bool validate_furniture_use(struct char_data *ch, struct obj_data *furnit
bool already_there);
static void attach_char_to_furniture(struct char_data *ch, struct obj_data *furniture);
static const char *position_gerund(int pos);
static void clear_mount_state(struct char_data *ch);
static bool mount_skill_check(struct char_data *ch, int dc);
static bool resolve_mounted_move(struct char_data *ch, struct char_data **mount);
static int count_hitched_mounts(struct char_data *ch);
static int max_hitched_mounts(struct char_data *ch);
static struct char_data *first_hitched_mount_in_room(struct char_data *ch);
static struct char_data *find_hitched_mount_for_pack(struct char_data *ch, struct obj_data *obj);
/* simple function to determine if char can walk on water */
@ -263,6 +270,138 @@ static const char *position_gerund(int pos)
return "using";
}
}
static void clear_mount_state(struct char_data *ch)
{
struct char_data *mount;
if (!ch)
return;
mount = MOUNT(ch);
if (mount && RIDDEN_BY(mount) == ch) {
int rider_weight = GET_WEIGHT(ch) + IS_CARRYING_W(ch);
IS_CARRYING_W(mount) = MAX(0, IS_CARRYING_W(mount) - rider_weight);
RIDDEN_BY(mount) = NULL;
}
MOUNT(ch) = NULL;
REMOVE_BIT_AR(AFF_FLAGS(ch), AFF_MOUNTED);
}
static bool mount_skill_check(struct char_data *ch, int dc)
{
int total = roll_skill_check(ch, SKILL_ANIMAL_HANDLING, 0, NULL);
bool success = (total >= dc);
gain_skill(ch, "animal handling", success);
return success;
}
static bool resolve_mounted_move(struct char_data *ch, struct char_data **mount)
{
struct char_data *mount_ch;
if (!AFF_FLAGGED(ch, AFF_MOUNTED))
return FALSE;
mount_ch = MOUNT(ch);
if (!mount_ch || IN_ROOM(mount_ch) != IN_ROOM(ch)) {
clear_mount_state(ch);
send_to_char(ch, "You aren't mounted on anything.\r\n");
return FALSE;
}
if (!mount_skill_check(ch, 5)) {
send_to_char(ch, "%s refuses to move.\r\n", get_char_sdesc(mount_ch));
act("$n tries to urge $N forward, but $N refuses to move.",
TRUE, ch, 0, mount_ch, TO_ROOM);
return FALSE;
}
if (!mount_skill_check(ch, 3)) {
int dam = dice(1, 8);
send_to_char(ch, "You are thrown from %s!\r\n", get_char_sdesc(mount_ch));
act("$n is thrown from $N!", TRUE, ch, 0, mount_ch, TO_ROOM);
clear_mount_state(ch);
GET_POS(ch) = POS_RESTING;
WAIT_STATE(ch, PULSE_VIOLENCE);
damage(ch, ch, dam, TYPE_SUFFERING);
return FALSE;
}
if (mount)
*mount = mount_ch;
return TRUE;
}
static int count_hitched_mounts(struct char_data *ch)
{
struct follow_type *follow;
int count = 0;
if (!ch)
return 0;
for (follow = ch->followers; follow; follow = follow->next) {
if (HITCHED_TO(follow->follower) == ch &&
MOB_FLAGGED(follow->follower, MOB_MOUNT))
count++;
}
return count;
}
static int max_hitched_mounts(struct char_data *ch)
{
int skill = GET_SKILL(ch, SKILL_ANIMAL_HANDLING);
if (skill > 59)
return 2;
return 1;
}
static struct char_data *first_hitched_mount_in_room(struct char_data *ch)
{
struct follow_type *follow;
if (!ch)
return NULL;
for (follow = ch->followers; follow; follow = follow->next) {
if (HITCHED_TO(follow->follower) == ch &&
MOB_FLAGGED(follow->follower, MOB_MOUNT) &&
IN_ROOM(follow->follower) == IN_ROOM(ch))
return follow->follower;
}
return NULL;
}
static struct char_data *find_hitched_mount_for_pack(struct char_data *ch, struct obj_data *obj)
{
struct follow_type *follow;
if (!ch || !obj)
return NULL;
for (follow = ch->followers; follow; follow = follow->next) {
struct char_data *mount = follow->follower;
if (HITCHED_TO(mount) != ch || !MOB_FLAGGED(mount, MOB_MOUNT))
continue;
if (IN_ROOM(mount) != IN_ROOM(ch))
continue;
if ((IS_CARRYING_N(mount) + 1) > CAN_CARRY_N(mount))
continue;
if ((IS_CARRYING_W(mount) + GET_OBJ_WEIGHT(obj)) > CAN_CARRY_W(mount))
continue;
return mount;
}
return NULL;
}
/** Move a PC/NPC character from their current location to a new location. This
* is the standard movement locomotion function that all normal walking
* movement by characters should be sent through. This function also defines
@ -290,9 +429,12 @@ int do_simple_move(struct char_data *ch, int dir, int need_specials_check)
room_rnum was_in = IN_ROOM(ch);
/* ... and the room the character will move into. */
room_rnum going_to = EXIT(ch, dir)->to_room;
/* How many movement points are required to travel from was_in to going_to.
/* How many stamina points are required to travel from was_in to going_to.
* We redefine this later when we need it. */
int need_movement = 0;
/* Character whose stamina is used for movement (mounts override). */
struct char_data *stamina_ch = ch;
bool mounted_move = FALSE;
/* Contains the "leave" message to display to the was_in room. */
char leave_message[SMALL_BUFSIZE];
/*---------------------------------------------------------------------*/
@ -400,16 +542,24 @@ int do_simple_move(struct char_data *ch, int dir, int need_specials_check)
}
/* All checks passed, nothing will prevent movement now other than lack of
* move points. */
/* move points needed is avg. move loss for src and destination sect type */
* stamina points. */
/* stamina points needed is avg. move loss for src and destination sect type */
need_movement = (movement_loss[SECT(was_in)] +
movement_loss[SECT(going_to)]) / 2;
/* Move Point Requirement Check */
if (GET_MOVE(ch) < need_movement && !IS_NPC(ch))
if (AFF_FLAGGED(ch, AFF_MOUNTED) && MOUNT(ch) && IN_ROOM(MOUNT(ch)) == was_in)
{
stamina_ch = MOUNT(ch);
mounted_move = TRUE;
}
if (GET_STAMINA(stamina_ch) < need_movement && (mounted_move || !IS_NPC(ch)))
{
if (need_specials_check && ch->master)
send_to_char(ch, "You are too exhausted to follow.\r\n");
else if (mounted_move)
send_to_char(ch, "Your mount is too exhausted.\r\n");
else
send_to_char(ch, "You are too exhausted.\r\n");
@ -423,8 +573,8 @@ int do_simple_move(struct char_data *ch, int dir, int need_specials_check)
/* Begin: the leave operation. */
/*---------------------------------------------------------------------*/
/* If applicable, subtract movement cost. */
if (GET_LEVEL(ch) < LVL_IMMORT && !IS_NPC(ch))
GET_MOVE(ch) -= need_movement;
if (GET_LEVEL(ch) < LVL_IMMORT && (mounted_move || !IS_NPC(ch)))
GET_STAMINA(stamina_ch) -= need_movement;
/* Generate the leave message and display to others in the was_in room. */
if (AFF_FLAGGED(ch, AFF_SNEAK)) {
@ -492,6 +642,7 @@ int do_simple_move(struct char_data *ch, int dir, int need_specials_check)
/* Only here is the move successful *and* complete. Return success for
* calling functions to handle post move operations. */
clear_custom_ldesc(ch);
return (1);
}
@ -499,6 +650,7 @@ int perform_move(struct char_data *ch, int dir, int need_specials_check)
{
room_rnum was_in;
struct follow_type *k, *next;
struct char_data *mount = NULL;
if (ch == NULL || dir < 0 || dir >= NUM_OF_DIRS || FIGHTING(ch))
return (0);
@ -512,13 +664,22 @@ int perform_move(struct char_data *ch, int dir, int need_specials_check)
else
send_to_char(ch, "It seems to be closed.\r\n");
} else {
if (!ch->followers)
return (do_simple_move(ch, dir, need_specials_check));
was_in = IN_ROOM(ch);
if (AFF_FLAGGED(ch, AFF_MOUNTED) &&
!resolve_mounted_move(ch, &mount))
return (0);
if (!do_simple_move(ch, dir, need_specials_check))
return (0);
if (mount && IN_ROOM(mount) == was_in) {
char_from_room(mount);
char_to_room(mount, IN_ROOM(ch));
}
if (!ch->followers)
return (1);
for (k = ch->followers; k; k = next) {
next = k->next;
if ((IN_ROOM(k->follower) == was_in) &&
@ -936,10 +1097,69 @@ ACMD(do_stand)
{
char token[MAX_INPUT_LENGTH];
struct obj_data *furniture = NULL, *current_furniture = SITTING(ch);
struct char_data *mount = NULL;
char arg[MAX_INPUT_LENGTH];
bool has_target = FALSE, used_number = FALSE;
int ordinal = 0;
int orig_pos = GET_POS(ch);
if (*argument) {
one_argument(argument, arg);
if (*arg && !is_abbrev(arg, "at"))
mount = get_char_vis(ch, arg, NULL, FIND_CHAR_ROOM);
if (mount && MOB_FLAGGED(mount, MOB_MOUNT) && HITCHED_TO(mount) != ch) {
act("You don't have $N hitched.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
if (mount && HITCHED_TO(mount) == ch && MOB_FLAGGED(mount, MOB_MOUNT)) {
if (FIGHTING(mount)) {
act("$N is fighting right now.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
if (GET_POS(mount) == POS_RESTING) {
act("You get $N to stand up.", FALSE, ch, 0, mount, TO_CHAR);
act("$n gets $N to stand up.", TRUE, ch, 0, mount, TO_ROOM);
GET_POS(mount) = POS_STANDING;
clear_custom_ldesc(mount);
return;
}
if (GET_POS(mount) == POS_SLEEPING) {
act("$N has to wake up first.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
act("$N is already standing.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
}
if (!*argument && AFF_FLAGGED(ch, AFF_MOUNTED) && MOUNT(ch)) {
mount = MOUNT(ch);
if (IN_ROOM(mount) != IN_ROOM(ch) || RIDDEN_BY(mount) != ch) {
clear_mount_state(ch);
send_to_char(ch, "You aren't mounted on anything.\r\n");
return;
}
if (FIGHTING(mount)) {
send_to_char(ch, "Your mount is fighting right now.\r\n");
return;
}
if (GET_POS(mount) != POS_SLEEPING) {
act("You get $N to rest and dismount.", FALSE, ch, 0, mount, TO_CHAR);
act("$n gets $N to rest and dismounts.", TRUE, ch, 0, mount, TO_ROOM);
GET_POS(mount) = POS_RESTING;
clear_custom_ldesc(mount);
} else {
act("$N is already asleep.", FALSE, ch, 0, mount, TO_CHAR);
}
clear_mount_state(ch);
return;
}
if (AFF_FLAGGED(ch, AFF_MOUNTED)) {
send_to_char(ch, "You must dismount first.\r\n");
return;
}
if (*argument) {
if (!extract_furniture_token(ch, argument, token, sizeof(token), "Stand"))
return;
@ -1000,6 +1220,7 @@ ACMD(do_stand)
act(self_msg, TRUE, ch, furniture, 0, TO_CHAR);
act(room_msg, TRUE, ch, furniture, 0, TO_ROOM);
GET_POS(ch) = FIGHTING(ch) ? POS_FIGHTING : POS_STANDING;
clear_custom_ldesc(ch);
return;
}
@ -1020,6 +1241,7 @@ ACMD(do_stand)
char_from_furniture(ch);
/* Will be standing after a successful bash and may still be fighting. */
GET_POS(ch) = FIGHTING(ch) ? POS_FIGHTING : POS_STANDING;
clear_custom_ldesc(ch);
break;
case POS_RESTING:
@ -1033,6 +1255,7 @@ ACMD(do_stand)
GET_POS(ch) = POS_STANDING;
/* Were they sitting in something. */
char_from_furniture(ch);
clear_custom_ldesc(ch);
break;
case POS_SLEEPING:
@ -1050,6 +1273,7 @@ ACMD(do_stand)
else
act("$n stops floating around, and puts $s feet on the ground.", TRUE, ch, 0, 0, TO_ROOM);
GET_POS(ch) = POS_STANDING;
clear_custom_ldesc(ch);
break;
}
}
@ -1062,6 +1286,11 @@ ACMD(do_sit)
int ordinal = 0;
int orig_pos = GET_POS(ch);
if (AFF_FLAGGED(ch, AFF_MOUNTED)) {
send_to_char(ch, "You must dismount first.\r\n");
return;
}
if (*argument) {
if (!extract_furniture_token(ch, argument, token, sizeof(token), "Sit"))
return;
@ -1122,6 +1351,7 @@ ACMD(do_sit)
act(self_msg, TRUE, ch, furniture, 0, TO_CHAR);
act(room_msg, TRUE, ch, furniture, 0, TO_ROOM);
GET_POS(ch) = POS_SITTING;
clear_custom_ldesc(ch);
return;
}
@ -1130,6 +1360,7 @@ ACMD(do_sit)
send_to_char(ch, "You sit down.\r\n");
act("$n sits down.", FALSE, ch, 0, 0, TO_ROOM);
GET_POS(ch) = POS_SITTING;
clear_custom_ldesc(ch);
break;
case POS_SITTING:
send_to_char(ch, "You're sitting already.\r\n");
@ -1141,6 +1372,7 @@ ACMD(do_sit)
else
act("$n stops resting and sits up.", TRUE, ch, 0, 0, TO_ROOM);
GET_POS(ch) = POS_SITTING;
clear_custom_ldesc(ch);
break;
case POS_SLEEPING:
send_to_char(ch, "You have to wake up first.\r\n");
@ -1155,6 +1387,7 @@ ACMD(do_sit)
else
act("$n stops floating around, and sits down.", TRUE, ch, 0, 0, TO_ROOM);
GET_POS(ch) = POS_SITTING;
clear_custom_ldesc(ch);
break;
}
}
@ -1163,9 +1396,69 @@ ACMD(do_rest)
{
char token[MAX_INPUT_LENGTH];
struct obj_data *furniture = NULL, *current_furniture = SITTING(ch);
struct char_data *mount = NULL;
char arg[MAX_INPUT_LENGTH];
bool has_target = FALSE, used_number = FALSE;
int ordinal = 0;
if (*argument) {
one_argument(argument, arg);
if (*arg && !is_abbrev(arg, "at"))
mount = get_char_vis(ch, arg, NULL, FIND_CHAR_ROOM);
if (mount && MOB_FLAGGED(mount, MOB_MOUNT) && HITCHED_TO(mount) != ch) {
act("You don't have $N hitched.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
if (mount && HITCHED_TO(mount) == ch && MOB_FLAGGED(mount, MOB_MOUNT)) {
if (FIGHTING(mount)) {
act("$N is fighting right now.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
if (GET_POS(mount) == POS_RESTING) {
act("$N is already resting.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
if (GET_POS(mount) == POS_SLEEPING) {
act("$N is already asleep.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
act("You pull on $N's reins, forcing it to rest.", FALSE, ch, 0, mount, TO_CHAR);
act("$n pulls on $N's reins, forcing it to rest.", TRUE, ch, 0, mount, TO_ROOM);
GET_POS(mount) = POS_RESTING;
clear_custom_ldesc(mount);
return;
}
}
if (!*argument && AFF_FLAGGED(ch, AFF_MOUNTED) && MOUNT(ch)) {
mount = MOUNT(ch);
if (IN_ROOM(mount) != IN_ROOM(ch) || RIDDEN_BY(mount) != ch) {
clear_mount_state(ch);
send_to_char(ch, "You aren't mounted on anything.\r\n");
return;
}
if (FIGHTING(mount)) {
send_to_char(ch, "Your mount is fighting right now.\r\n");
return;
}
if (GET_POS(mount) == POS_SLEEPING) {
act("$N is already asleep.", FALSE, ch, 0, mount, TO_CHAR);
clear_mount_state(ch);
return;
}
act("You pull on $N's reins, forcing it to rest.", FALSE, ch, 0, mount, TO_CHAR);
act("$n pulls on $N's reins, forcing it to rest and dismounts.", TRUE, ch, 0, mount, TO_ROOM);
GET_POS(mount) = POS_RESTING;
clear_custom_ldesc(mount);
clear_mount_state(ch);
return;
}
if (AFF_FLAGGED(ch, AFF_MOUNTED)) {
send_to_char(ch, "You must dismount first.\r\n");
return;
}
if (*argument) {
if (!extract_furniture_token(ch, argument, token, sizeof(token), "Rest"))
return;
@ -1227,6 +1520,7 @@ ACMD(do_rest)
act(self_msg, TRUE, ch, furniture, 0, TO_CHAR);
act(room_msg, TRUE, ch, furniture, 0, TO_ROOM);
GET_POS(ch) = POS_RESTING;
clear_custom_ldesc(ch);
return;
}
@ -1240,6 +1534,7 @@ ACMD(do_rest)
act("$n sits down and rests.", TRUE, ch, 0, 0, TO_ROOM);
}
GET_POS(ch) = POS_RESTING;
clear_custom_ldesc(ch);
break;
case POS_SITTING:
@ -1258,6 +1553,7 @@ ACMD(do_rest)
act("$n rests.", TRUE, ch, 0, 0, TO_ROOM);
}
GET_POS(ch) = POS_RESTING;
clear_custom_ldesc(ch);
break;
case POS_RESTING:
@ -1279,6 +1575,7 @@ ACMD(do_rest)
else
act("$n stops floating around, and rests.", FALSE, ch, 0, 0, TO_ROOM);
GET_POS(ch) = POS_RESTING;
clear_custom_ldesc(ch);
break;
}
}
@ -1290,6 +1587,11 @@ ACMD(do_sleep)
bool has_target = FALSE, used_number = FALSE;
int ordinal = 0;
if (AFF_FLAGGED(ch, AFF_MOUNTED)) {
send_to_char(ch, "You must dismount first.\r\n");
return;
}
if (*argument) {
if (!extract_furniture_token(ch, argument, token, sizeof(token), "Sleep"))
return;
@ -1332,6 +1634,7 @@ ACMD(do_sleep)
act("You go to sleep on $p.", TRUE, ch, furniture, 0, TO_CHAR);
act("$n lies down and falls asleep on $p.", TRUE, ch, furniture, 0, TO_ROOM);
GET_POS(ch) = POS_SLEEPING;
clear_custom_ldesc(ch);
return;
}
@ -1354,6 +1657,7 @@ ACMD(do_sleep)
act("$n lies down and falls asleep.", TRUE, ch, 0, 0, TO_ROOM);
}
GET_POS(ch) = POS_SLEEPING;
clear_custom_ldesc(ch);
break;
case POS_SLEEPING:
@ -1371,6 +1675,7 @@ ACMD(do_sleep)
else
act("$n stops floating around, and lies down to sleep.", TRUE, ch, 0, 0, TO_ROOM);
GET_POS(ch) = POS_SLEEPING;
clear_custom_ldesc(ch);
break;
}
}
@ -1468,6 +1773,285 @@ ACMD(do_follow)
}
}
ACMD(do_mount)
{
char arg[MAX_INPUT_LENGTH];
struct char_data *mount;
one_argument(argument, arg);
if (!*arg) {
send_to_char(ch, "Mount what?\r\n");
return;
}
if (AFF_FLAGGED(ch, AFF_MOUNTED)) {
send_to_char(ch, "You are already mounted.\r\n");
return;
}
if (!(mount = get_char_vis(ch, arg, NULL, FIND_CHAR_ROOM))) {
send_to_char(ch, "%s", CONFIG_NOPERSON);
return;
}
if (mount == ch) {
send_to_char(ch, "You can't mount yourself.\r\n");
return;
}
if (!IS_NPC(mount) || !MOB_FLAGGED(mount, MOB_MOUNT)) {
send_to_char(ch, "You can't mount %s!\r\n", get_char_sdesc(mount));
return;
}
if (HITCHED_TO(mount) && HITCHED_TO(mount) != ch) {
act("$N is already hitched to someone else.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
if (RIDDEN_BY(mount)) {
act("$N is already being ridden.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
if ((GET_WEIGHT(ch) + IS_CARRYING_W(ch) + IS_CARRYING_W(mount)) > CAN_CARRY_W(mount)) {
act("$N can't carry that much weight.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
SET_BIT_AR(AFF_FLAGS(ch), AFF_MOUNTED);
MOUNT(ch) = mount;
RIDDEN_BY(mount) = ch;
IS_CARRYING_W(mount) += GET_WEIGHT(ch) + IS_CARRYING_W(ch);
act("You mount $N.", FALSE, ch, 0, mount, TO_CHAR);
act("$n mounts $N.", TRUE, ch, 0, mount, TO_ROOM);
}
ACMD(do_dismount)
{
struct char_data *mount = MOUNT(ch);
if (!AFF_FLAGGED(ch, AFF_MOUNTED) || !mount) {
clear_mount_state(ch);
send_to_char(ch, "You aren't mounted on anything.\r\n");
return;
}
act("You dismount $N.", FALSE, ch, 0, mount, TO_CHAR);
act("$n dismounts $N.", TRUE, ch, 0, mount, TO_ROOM);
clear_mount_state(ch);
}
ACMD(do_hitch)
{
char arg[MAX_INPUT_LENGTH];
struct char_data *mount;
int max_hitched;
one_argument(argument, arg);
if (!*arg) {
send_to_char(ch, "Hitch what?\r\n");
return;
}
if (!(mount = get_char_vis(ch, arg, NULL, FIND_CHAR_ROOM))) {
send_to_char(ch, "%s", CONFIG_NOPERSON);
return;
}
if (mount == ch) {
send_to_char(ch, "You can't hitch yourself.\r\n");
return;
}
if (!IS_NPC(mount) || !MOB_FLAGGED(mount, MOB_MOUNT)) {
send_to_char(ch, "You can't hitch %s!\r\n", get_char_sdesc(mount));
return;
}
if (RIDDEN_BY(mount) && RIDDEN_BY(mount) != ch) {
act("$N is already being ridden.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
if (HITCHED_TO(mount) == ch) {
act("$N is already hitched to you.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
if (HITCHED_TO(mount) && HITCHED_TO(mount) != ch) {
act("$N is already hitched to someone else.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
if (mount->master && mount->master != ch) {
act("$N is already following someone else.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
max_hitched = max_hitched_mounts(ch);
if (count_hitched_mounts(ch) >= max_hitched) {
send_to_char(ch, "You can't hitch any more mounts.\r\n");
return;
}
if (!mount->master)
add_follower(mount, ch);
HITCHED_TO(mount) = ch;
act("You hitch $N to you.", FALSE, ch, 0, mount, TO_CHAR);
act("$n hitches $N to $m.", TRUE, ch, 0, mount, TO_ROOM);
}
ACMD(do_unhitch)
{
char arg[MAX_INPUT_LENGTH];
struct char_data *mount;
one_argument(argument, arg);
if (!*arg) {
send_to_char(ch, "Unhitch what?\r\n");
return;
}
if (!(mount = get_char_vis(ch, arg, NULL, FIND_CHAR_ROOM))) {
send_to_char(ch, "%s", CONFIG_NOPERSON);
return;
}
if (HITCHED_TO(mount) != ch || !MOB_FLAGGED(mount, MOB_MOUNT)) {
act("You don't have $N hitched.", FALSE, ch, 0, mount, TO_CHAR);
return;
}
if (mount->master == ch)
stop_follower(mount);
else
HITCHED_TO(mount) = NULL;
act("You unhitch $N.", FALSE, ch, 0, mount, TO_CHAR);
act("$n unhitches $N.", TRUE, ch, 0, mount, TO_ROOM);
}
ACMD(do_pack)
{
char arg[MAX_INPUT_LENGTH];
struct char_data *mount;
struct obj_data *obj;
struct follow_type *follow;
bool found_mount = FALSE;
one_argument(argument, arg);
if (!*arg) {
for (follow = ch->followers; follow; follow = follow->next) {
bool found_item = FALSE;
mount = follow->follower;
if (HITCHED_TO(mount) != ch || !MOB_FLAGGED(mount, MOB_MOUNT))
continue;
if (IN_ROOM(mount) != IN_ROOM(ch))
continue;
found_mount = TRUE;
send_to_char(ch, "Packed on %s:\r\n", get_char_sdesc(mount));
for (obj = mount->carrying; obj; obj = obj->next_content) {
if (GET_OBJ_TYPE(obj) != ITEM_CONTAINER)
continue;
if (!CAN_SEE_OBJ(ch, obj))
continue;
send_to_char(ch, " %s\r\n", obj->short_description ? obj->short_description : "something");
found_item = TRUE;
}
if (!found_item)
send_to_char(ch, " Nothing.\r\n");
}
if (!found_mount)
send_to_char(ch, "You don't have any mounts hitched.\r\n");
return;
}
if (!(obj = get_obj_in_list_vis(ch, arg, NULL, ch->carrying))) {
send_to_char(ch, "You don't seem to have that.\r\n");
return;
}
if (GET_OBJ_TYPE(obj) != ITEM_CONTAINER) {
send_to_char(ch, "You can only pack containers onto a mount.\r\n");
return;
}
mount = find_hitched_mount_for_pack(ch, obj);
if (!mount) {
if (!first_hitched_mount_in_room(ch))
send_to_char(ch, "You don't have any mounts hitched here.\r\n");
else
send_to_char(ch, "Your mount can't carry that much.\r\n");
return;
}
obj_from_char(obj);
obj_to_char(obj, mount);
act("You pack $p onto $N.", FALSE, ch, obj, mount, TO_CHAR);
act("$n packs $p onto $N.", TRUE, ch, obj, mount, TO_ROOM);
}
ACMD(do_unpack)
{
char arg[MAX_INPUT_LENGTH];
struct char_data *mount;
struct obj_data *obj;
struct follow_type *follow;
one_argument(argument, arg);
if (!*arg) {
send_to_char(ch, "Unpack what?\r\n");
return;
}
for (follow = ch->followers; follow; follow = follow->next) {
mount = follow->follower;
if (HITCHED_TO(mount) != ch || !MOB_FLAGGED(mount, MOB_MOUNT))
continue;
if (IN_ROOM(mount) != IN_ROOM(ch))
continue;
obj = get_obj_in_list_vis(ch, arg, NULL, mount->carrying);
if (!obj)
continue;
if (GET_OBJ_TYPE(obj) != ITEM_CONTAINER)
continue;
if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch)) {
send_to_char(ch, "You can't carry any more items.\r\n");
return;
}
if ((IS_CARRYING_W(ch) + GET_OBJ_WEIGHT(obj)) > CAN_CARRY_W(ch)) {
send_to_char(ch, "You can't carry that much weight.\r\n");
return;
}
obj_from_char(obj);
obj_to_char(obj, ch);
act("You unpack $p from $N.", FALSE, ch, obj, mount, TO_CHAR);
act("$n unpacks $p from $N.", TRUE, ch, obj, mount, TO_ROOM);
return;
}
send_to_char(ch, "You don't have that packed on a hitched mount.\r\n");
}
ACMD(do_unfollow)
{
if (ch->master) {

View file

@ -54,8 +54,6 @@ ACMD(do_assist)
else if (!CAN_SEE(ch, opponent))
act("You can't see who is fighting $M!", FALSE, ch, 0, helpee, TO_CHAR);
/* prevent accidental pkill */
else if (!CONFIG_PK_ALLOWED && !IS_NPC(opponent))
send_to_char(ch, "You cannot kill other players.\r\n");
else {
send_to_char(ch, "You join the fight!\r\n");
act("$N assists you!", 0, helpee, 0, ch, TO_CHAR);
@ -82,9 +80,6 @@ ACMD(do_hit)
} else if (AFF_FLAGGED(ch, AFF_CHARM) && (ch->master == vict))
act("$N is just such a good friend, you simply can't hit $M.", FALSE, ch, 0, vict, TO_CHAR);
else {
if (!CONFIG_PK_ALLOWED && !IS_NPC(vict) && !IS_NPC(ch))
check_killer(ch, vict);
if ((GET_POS(ch) == POS_STANDING) && (vict != FIGHTING(ch))) {
if (GET_DEX(ch) > GET_DEX(vict) || (GET_DEX(ch) == GET_DEX(vict) && rand_number(1, 2) == 1)) /* if faster */
hit(ch, vict, TYPE_UNDEFINED); /* first */
@ -150,7 +145,7 @@ ACMD(do_backstab)
return;
}
/* Only piercing weapons allowed */
if (GET_OBJ_VAL(weap, 3) != TYPE_PIERCE - TYPE_HIT) {
if (GET_OBJ_VAL(weap, 2) != TYPE_PIERCE - TYPE_HIT) {
send_to_char(ch, "Only piercing weapons can be used for backstabbing.\r\n");
return;
}
@ -187,14 +182,14 @@ ACMD(do_backstab)
/* Keeping this logic really simple so it can be adjusted later if need be */
if (crit_success) {
/* Simple crit = 2x damage */
int base = dice(GET_OBJ_VAL(weap, 1), GET_OBJ_VAL(weap, 2));
int base = dice(GET_OBJ_VAL(weap, 0), GET_OBJ_VAL(weap, 1));
int dmg = base + GET_ABILITY_MOD(GET_DEX(ch));
if (dmg < 1) dmg = 1;
dmg *= 2;
damage(ch, vict, dmg, SKILL_BACKSTAB);
} else {
/* Hit but not crit = 1.5x damage */
int base = dice(GET_OBJ_VAL(weap, 1), GET_OBJ_VAL(weap, 2));
int base = dice(GET_OBJ_VAL(weap, 0), GET_OBJ_VAL(weap, 1));
int dmg = base + GET_ABILITY_MOD(GET_DEX(ch));
if (dmg < 1) dmg = 1;
dmg *= 1.5;

View file

@ -29,13 +29,50 @@
#include "shop.h"
#include "quest.h"
#include "modify.h"
#include "roomsave.h"
/* Local defined utility functions */
/* do_group utility functions */
static void print_group(struct char_data *ch);
static void display_group_list(struct char_data * ch);
static bool change_has_emote_tokens(const char *text)
{
for (; text && *text; text++) {
switch (*text) {
case '~': case '!': case '%': case '^':
case '#': case '&': case '=': case '+':
case '@':
return TRUE;
}
}
return FALSE;
}
static bool change_ends_with_punct(const char *text)
{
size_t len = text ? strlen(text) : 0;
if (len == 0)
return FALSE;
return (text[len - 1] == '.' || text[len - 1] == '!' || text[len - 1] == '?');
}
static void change_trim_trailing_spaces(char *text)
{
size_t len = text ? strlen(text) : 0;
while (len > 0 && isspace((unsigned char)text[len - 1])) {
text[len - 1] = '\0';
len--;
}
}
#define REROLL_REVIEW_SECONDS (2 * SECS_PER_REAL_HOUR)
static void reroll_clear_saved(struct char_data *ch)
{
GET_REROLL_EXPIRES(ch) = 0;
memset(&GET_REROLL_OLD_ABILS(ch), 0, sizeof(struct char_ability_data));
}
ACMD(do_quit)
{
char first[MAX_INPUT_LENGTH];
@ -182,6 +219,168 @@ ACMD(do_save)
Crash_crashsave(ch);
}
ACMD(do_change)
{
char option[MAX_INPUT_LENGTH];
char suffix[MAX_INPUT_LENGTH];
char base_buf[MAX_INPUT_LENGTH];
char ldesc[MAX_STRING_LENGTH];
char *rest = argument;
const char *base;
rest = one_argument(rest, option);
if (!*option) {
send_to_char(ch, "Usage: change ldesc <string>\r\n");
return;
}
if (!is_abbrev(option, "ldesc")) {
send_to_char(ch, "Unknown change option. Available: ldesc\r\n");
return;
}
skip_spaces(&rest);
if (!*rest) {
send_to_char(ch, "Usage: change ldesc <string>\r\n");
return;
}
if (change_has_emote_tokens(rest)) {
send_to_char(ch, "You can't use emote tokens in your ldesc.\r\n");
return;
}
strlcpy(suffix, rest, sizeof(suffix));
change_trim_trailing_spaces(suffix);
if (!*suffix) {
send_to_char(ch, "Usage: change ldesc <string>\r\n");
return;
}
if (!change_ends_with_punct(suffix))
strlcat(suffix, ".", sizeof(suffix));
base = (GET_SHORT_DESC(ch) && *GET_SHORT_DESC(ch)) ? GET_SHORT_DESC(ch) : GET_NAME(ch);
if (!base || !*base)
base = "someone";
strlcpy(base_buf, base, sizeof(base_buf));
if (*base_buf)
base_buf[0] = UPPER(*base_buf);
snprintf(ldesc, sizeof(ldesc), "%s %s\r\n", base_buf, suffix);
if (ch->player.long_descr) {
if (!IS_NPC(ch) || GET_MOB_RNUM(ch) == NOBODY ||
ch->player.long_descr != mob_proto[GET_MOB_RNUM(ch)].player.long_descr) {
free(ch->player.long_descr);
}
}
ch->player.long_descr = strdup(ldesc);
ch->char_specials.custom_ldesc = TRUE;
send_to_char(ch, "Long description updated.\r\n");
}
ACMD(do_reroll)
{
char arg[MAX_INPUT_LENGTH];
struct char_data *vict;
time_t now;
time_t remaining;
bool expired = FALSE;
one_argument(argument, arg);
if (IS_NPC(ch)) {
send_to_char(ch, "You can't reroll stats.\r\n");
return;
}
now = time(0);
if (GET_REROLL_EXPIRES(ch) > 0 && now >= GET_REROLL_EXPIRES(ch)) {
reroll_clear_saved(ch);
save_char(ch);
expired = TRUE;
}
if (*arg && is_abbrev(arg, "undo")) {
if (!GET_REROLL_USED(ch)) {
send_to_char(ch, "You haven't rerolled yet.\r\n");
return;
}
if (GET_REROLL_EXPIRES(ch) <= 0 || expired) {
send_to_char(ch, "You no longer have a reroll pending; your stats are permanent.\r\n");
return;
}
ch->real_abils = GET_REROLL_OLD_ABILS(ch);
affect_total(ch);
reroll_clear_saved(ch);
save_char(ch);
send_to_char(ch, "Your original stats have been restored. You cannot reroll again.\r\n");
send_to_char(ch, "Stats: Str %d, Int %d, Wis %d, Dex %d, Con %d, Cha %d\r\n",
GET_STR(ch), GET_INT(ch), GET_WIS(ch),
GET_DEX(ch), GET_CON(ch), GET_CHA(ch));
return;
}
if (*arg && GET_LEVEL(ch) >= LVL_GRGOD) {
if (!(vict = get_char_vis(ch, arg, NULL, FIND_CHAR_WORLD)))
send_to_char(ch, "There is no such player.\r\n");
else if (IS_NPC(vict))
send_to_char(ch, "You can't do that to a mob!\r\n");
else {
roll_real_abils(vict);
affect_total(vict);
send_to_char(ch, "Rerolled...\r\n");
log("(GC) %s has rerolled %s.", GET_NAME(ch), GET_NAME(vict));
send_to_char(ch, "New stats: Str %d, Int %d, Wis %d, Dex %d, Con %d, Cha %d\r\n",
GET_STR(vict), GET_INT(vict), GET_WIS(vict),
GET_DEX(vict), GET_CON(vict), GET_CHA(vict));
save_char(vict);
}
return;
}
if (*arg) {
send_to_char(ch, "Usage: reroll | reroll undo\r\n");
return;
}
if (GET_REROLL_USED(ch)) {
if (GET_REROLL_EXPIRES(ch) > 0 && now < GET_REROLL_EXPIRES(ch)) {
remaining = GET_REROLL_EXPIRES(ch) - now;
int hours = remaining / SECS_PER_REAL_HOUR;
int mins = (remaining % SECS_PER_REAL_HOUR) / SECS_PER_REAL_MIN;
if (hours > 0)
send_to_char(ch, "You have already rerolled. You can 'reroll undo' within %d hour%s %d minute%s.\r\n",
hours, hours == 1 ? "" : "s", mins, mins == 1 ? "" : "s");
else
send_to_char(ch, "You have already rerolled. You can 'reroll undo' within %d minute%s.\r\n",
mins, mins == 1 ? "" : "s");
} else {
send_to_char(ch, "You have already rerolled and cannot reroll again.\r\n");
}
return;
}
GET_REROLL_OLD_ABILS(ch) = ch->real_abils;
roll_real_abils(ch);
affect_total(ch);
GET_REROLL_USED(ch) = TRUE;
GET_REROLL_EXPIRES(ch) = now + REROLL_REVIEW_SECONDS;
save_char(ch);
send_to_char(ch, "Your stats have been rerolled. You have 2 hours to review them.\r\n");
send_to_char(ch, "New stats: Str %d, Int %d, Wis %d, Dex %d, Con %d, Cha %d\r\n",
GET_STR(ch), GET_INT(ch), GET_WIS(ch),
GET_DEX(ch), GET_CON(ch), GET_CHA(ch));
send_to_char(ch, "Use 'reroll undo' to restore your original stats before the timer expires.\r\n");
}
/* Generic function for commands which are normally overridden by special
* procedures - i.e., shop commands, mail commands, etc. */
ACMD(do_not_here)
@ -557,7 +756,7 @@ ACMD(do_sneak)
if (total < dc) {
gain_skill(ch, "stealth", FALSE);
WAIT_STATE(ch, PULSE_VIOLENCE / 2);
GET_MOVE(ch) -= 10;
GET_STAMINA(ch) -= 10;
return;
}
@ -576,7 +775,7 @@ ACMD(do_sneak)
SET_STEALTH_CHECK(ch, MAX(GET_STEALTH_CHECK(ch), total));
gain_skill(ch, "stealth", TRUE);
GET_MOVE(ch) -= 10;
GET_STAMINA(ch) -= 10;
}
ACMD(do_hide)
@ -612,7 +811,7 @@ ACMD(do_hide)
/* Failure */
gain_skill(ch, "stealth", FALSE);
WAIT_STATE(ch, PULSE_VIOLENCE / 2);
GET_MOVE(ch) -= 10;
GET_STAMINA(ch) -= 10;
return;
}
@ -623,7 +822,7 @@ ACMD(do_hide)
send_to_char(ch, "You hide yourself as best you can.\r\n");
gain_skill(ch, "stealth", TRUE);
WAIT_STATE(ch, PULSE_VIOLENCE / 2);
GET_MOVE(ch) -= 10;
GET_STAMINA(ch) -= 10;
}
static void remember_scan_target(struct char_data *ch, struct char_data *tch)
@ -886,7 +1085,11 @@ bool perform_scan_sweep(struct char_data *ch)
continue;
if (total >= scan_target_dc(tch)) {
send_to_char(ch, "A shadowy figure.\r\n");
char hidden_ldesc[MAX_STRING_LENGTH];
if (build_hidden_ldesc(tch, hidden_ldesc, sizeof(hidden_ldesc)))
send_to_char(ch, "%s", hidden_ldesc);
else
send_to_char(ch, "A shadowy figure.\r\n");
remember_scan_target(ch, tch);
found_any = TRUE;
} else {
@ -929,7 +1132,7 @@ ACMD(do_scan)
act("$n studies $s surroundings with a wary gaze.", TRUE, ch, 0, 0, TO_ROOM);
WAIT_STATE(ch, PULSE_VIOLENCE / 2);
GET_MOVE(ch) -= 10;
GET_STAMINA(ch) -= 10;
}
ACMD(do_listen)
@ -959,7 +1162,7 @@ ACMD(do_listen)
send_to_char(ch, "You focus entirely on every whisper and distant sound.\r\n");
WAIT_STATE(ch, PULSE_VIOLENCE / 2);
GET_MOVE(ch) -= 10;
GET_STAMINA(ch) -= 10;
}
ACMD(do_palm)
@ -1307,7 +1510,7 @@ static void print_group(struct char_data *ch)
GROUP_LEADER(GROUP(ch)) == k ? CBGRN(ch, C_NRM) : CCGRN(ch, C_NRM),
GET_HIT(k), GET_MAX_HIT(k),
GET_MANA(k), GET_MAX_MANA(k),
GET_MOVE(k), GET_MAX_MOVE(k),
GET_STAMINA(k), GET_MAX_STAMINA(k),
CCNRM(ch, C_NRM));
}
@ -1464,7 +1667,7 @@ ACMD(do_report)
GET_NAME(ch),
GET_HIT(ch), GET_MAX_HIT(ch),
GET_MANA(ch), GET_MAX_MANA(ch),
GET_MOVE(ch), GET_MAX_MOVE(ch));
GET_STAMINA(ch), GET_MAX_STAMINA(ch));
}
ACMD(do_split)
@ -1600,7 +1803,7 @@ ACMD(do_display)
skip_spaces(&argument);
if (!*argument) {
send_to_char(ch, "Usage: prompt { { H | M | V } | all | auto | none }\r\n");
send_to_char(ch, "Usage: prompt { { H | M | S } | all | auto | none }\r\n");
return;
}
@ -1613,15 +1816,15 @@ ACMD(do_display)
if (!str_cmp(argument, "on") || !str_cmp(argument, "all")) {
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPHP);
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPMANA);
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPMOVE);
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPSTAMINA);
} else if (!str_cmp(argument, "off") || !str_cmp(argument, "none")) {
REMOVE_BIT_AR(PRF_FLAGS(ch), PRF_DISPHP);
REMOVE_BIT_AR(PRF_FLAGS(ch), PRF_DISPMANA);
REMOVE_BIT_AR(PRF_FLAGS(ch), PRF_DISPMOVE);
REMOVE_BIT_AR(PRF_FLAGS(ch), PRF_DISPSTAMINA);
} else {
REMOVE_BIT_AR(PRF_FLAGS(ch), PRF_DISPHP);
REMOVE_BIT_AR(PRF_FLAGS(ch), PRF_DISPMANA);
REMOVE_BIT_AR(PRF_FLAGS(ch), PRF_DISPMOVE);
REMOVE_BIT_AR(PRF_FLAGS(ch), PRF_DISPSTAMINA);
for (i = 0; i < strlen(argument); i++) {
switch (LOWER(argument[i])) {
@ -1631,11 +1834,12 @@ ACMD(do_display)
case 'm':
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPMANA);
break;
case 's':
case 'v':
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPMOVE);
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPSTAMINA);
break;
default:
send_to_char(ch, "Usage: prompt { { H | M | V } | all | auto | none }\r\n");
send_to_char(ch, "Usage: prompt { { H | M | S } | all | auto | none }\r\n");
return;
}
}

File diff suppressed because it is too large Load diff

View file

@ -79,7 +79,6 @@ static void cedit_setup(struct descriptor_data *d)
/* Copy the current configuration from the config_info to this one and copy
* the game play options from the configuration info struct. */
OLC_CONFIG(d)->play.pk_allowed = CONFIG_PK_ALLOWED;
OLC_CONFIG(d)->play.pt_allowed = CONFIG_PT_ALLOWED;
OLC_CONFIG(d)->play.level_can_shout = CONFIG_LEVEL_CAN_SHOUT;
OLC_CONFIG(d)->play.tunnel_size = CONFIG_TUNNEL_SIZE;
@ -177,7 +176,6 @@ static void cedit_save_internally(struct descriptor_data *d)
/* see if we need to reassign spec procs on rooms */
int reassign = (CONFIG_DTS_ARE_DUMPS != OLC_CONFIG(d)->play.dts_are_dumps);
/* Copy the data back from the descriptor to the config_info structure. */
CONFIG_PK_ALLOWED = OLC_CONFIG(d)->play.pk_allowed;
CONFIG_PT_ALLOWED = OLC_CONFIG(d)->play.pt_allowed;
CONFIG_LEVEL_CAN_SHOUT = OLC_CONFIG(d)->play.level_can_shout;
CONFIG_TUNNEL_SIZE = OLC_CONFIG(d)->play.tunnel_size;
@ -326,8 +324,6 @@ int save_config( IDXTYPE nowhere )
"* [ Game Play Options ]\n"
);
fprintf(fl, "* Is player killing allowed on the mud?\n"
"pk_allowed = %d\n\n", CONFIG_PK_ALLOWED);
fprintf(fl, "* Is player thieving allowed on the mud?\n"
"pt_allowed = %d\n\n", CONFIG_PT_ALLOWED);
fprintf(fl, "* What is the minimum level a player can shout/gossip/etc?\n"
@ -591,7 +587,6 @@ static void cedit_disp_game_play_options(struct descriptor_data *d)
write_to_output(d, "\r\n\r\n"
"%sA%s) Player Killing Allowed : %s%s\r\n"
"%sB%s) Player Thieving Allowed : %s%s\r\n"
"%sC%s) Minimum Level To Shout : %s%d\r\n"
"%sE%s) Tunnel Size : %s%d\r\n"
@ -617,7 +612,6 @@ static void cedit_disp_game_play_options(struct descriptor_data *d)
"%s8%s) Scripts on PC's : %s%s\r\n"
"%sQ%s) Exit To The Main Menu\r\n"
"Enter your choice : ",
grn, nrm, cyn, CHECK_VAR(OLC_CONFIG(d)->play.pk_allowed),
grn, nrm, cyn, CHECK_VAR(OLC_CONFIG(d)->play.pt_allowed),
grn, nrm, cyn, OLC_CONFIG(d)->play.level_can_shout,
grn, nrm, cyn, OLC_CONFIG(d)->play.tunnel_size,
@ -844,11 +838,6 @@ void cedit_parse(struct descriptor_data *d, char *arg)
case CEDIT_GAME_OPTIONS_MENU:
switch (*arg) {
case 'a':
case 'A':
TOGGLE_VAR(OLC_CONFIG(d)->play.pk_allowed);
break;
case 'b':
case 'B':
TOGGLE_VAR(OLC_CONFIG(d)->play.pt_allowed);

View file

@ -23,6 +23,7 @@
#include "constants.h"
#include "act.h"
#include "class.h"
#include "species.h"
/* Names first */
const char *class_abbrevs[] = {
@ -95,52 +96,18 @@ bitvector_t find_class_bitvector(const char *arg)
return (ret);
}
/* These are definitions which control the guildmasters for each class.
* The first field (top line) controls the highest percentage skill level a
* character of the class is allowed to attain in any skill. (After this
* level, attempts to practice will say "You are already learned in this area."
*
* The second line controls the maximum percent gain in learnedness a character
* is allowed per practice -- in other words, if the random die throw comes out
* higher than this number, the gain will only be this number instead.
*
* The third line controls the minimu percent gain in learnedness a character
* is allowed per practice -- in other words, if the random die throw comes
* out below this number, the gain will be set up to this number.
*
* The fourth line simply sets whether the character knows 'spells' or 'skills'.
* This does not affect anything except the message given to the character when
* trying to practice (i.e. "You know of the following spells" vs. "You know of
* the following skills" */
#define SPELL 0
#define SKILL 1
/* #define LEARNED_LEVEL 0 % known which is considered "learned" */
/* #define MAX_PER_PRAC 1 max percent gain in skill per practice */
/* #define MIN_PER_PRAC 2 min percent gain in skill per practice */
/* #define PRAC_TYPE 3 should it say 'spell' or 'skill'? */
int prac_params[4][NUM_CLASSES] = {
/* SOR CLE THE FIG BAR RAN BARD DRU */
{ 95, 95, 85, 80, 75, 85, 85, 95 }, /* learned level */
{ 100, 100, 12, 12, 11, 12, 13, 90 }, /* max per practice */
{ 25, 25, 0, 0, 0, 0, 25, 25 }, /* min per practice */
{ SPELL, SPELL, SKILL, SKILL, SKILL, SKILL, SKILL, SKILL }, /* prac name */
};
/* The appropriate rooms for each guildmaster/guildguard; controls which types
* of people the various guildguards let through. i.e., the first line shows
/* The appropriate rooms for each class gatekeeper; controls which types
* of people the various guards let through. i.e., the first line shows
* that from room 3017, only SORCERORS are allowed to go south. Don't forget
* to visit spec_assign.c if you create any new mobiles that should be a guild
* to visit spec_assign.c if you create any new mobiles that should be a
* master or guard so they can act appropriately. If you "recycle" the
* existing mobs that are used in other guilds for your new guild, then you
* don't have to change that file, only here. Guildguards are now implemented
* existing mobs that are used in other areas for your new one, then you
* don't have to change that file, only here. Guards are now implemented
* via triggers. This code remains as an example. */
/* TO-DO: Is this necessary anymore now that there are no official guild rooms? */
struct guild_info_type guild_info[] = {
/* Midgaard */
/* Main City */
{ CLASS_SORCEROR, 3017, SOUTH },
{ CLASS_CLERIC, 3004, NORTH },
{ CLASS_ROGUE, 3027, EAST },
@ -172,10 +139,103 @@ bool has_save_proficiency(int class_num, int ability) {
}
}
static void set_real_ability(struct char_data *ch, int ability, int value)
{
switch (ability) {
case ABIL_STR: ch->real_abils.str = value; break;
case ABIL_DEX: ch->real_abils.dex = value; break;
case ABIL_CON: ch->real_abils.con = value; break;
case ABIL_INT: ch->real_abils.intel = value; break;
case ABIL_WIS: ch->real_abils.wis = value; break;
case ABIL_CHA: ch->real_abils.cha = value; break;
}
}
static void roll_real_abils_preference(struct char_data *ch)
{
int i, j, temp;
int rolls[NUM_ABILITIES];
int sorted[NUM_ABILITIES];
bool used[NUM_ABILITIES] = { FALSE };
bool assigned[NUM_ABILITIES] = { FALSE };
static const int default_order[NUM_ABILITIES] = {
ABIL_STR, ABIL_DEX, ABIL_CON, ABIL_INT, ABIL_WIS, ABIL_CHA
};
for (i = 0; i < NUM_ABILITIES; i++) {
int die[4];
for (j = 0; j < 4; j++)
die[j] = rand_number(1, 6);
temp = die[0] + die[1] + die[2] + die[3] -
MIN(die[0], MIN(die[1], MIN(die[2], die[3])));
rolls[i] = temp;
sorted[i] = temp;
}
for (i = 0; i < NUM_ABILITIES - 1; i++) {
for (j = i + 1; j < NUM_ABILITIES; j++) {
if (sorted[j] > sorted[i]) {
temp = sorted[i];
sorted[i] = sorted[j];
sorted[j] = temp;
}
}
}
int pref_count = ch->stat_pref_count;
if (pref_count > NUM_ABILITIES)
pref_count = NUM_ABILITIES;
for (i = 0; i < pref_count; i++) {
int ability = ch->stat_pref_order[i];
if (ability < 0 || ability >= NUM_ABILITIES)
continue;
set_real_ability(ch, ability, sorted[i]);
assigned[ability] = TRUE;
for (j = 0; j < NUM_ABILITIES; j++) {
if (!used[j] && rolls[j] == sorted[i]) {
used[j] = TRUE;
break;
}
}
}
int fifo_idx = 0;
for (i = 0; i < NUM_ABILITIES; i++) {
int ability = default_order[i];
if (assigned[ability])
continue;
while (fifo_idx < NUM_ABILITIES && used[fifo_idx])
fifo_idx++;
if (fifo_idx >= NUM_ABILITIES)
break;
set_real_ability(ch, ability, rolls[fifo_idx]);
used[fifo_idx] = TRUE;
assigned[ability] = TRUE;
fifo_idx++;
}
if (HAS_VALID_SPECIES(ch))
apply_species_bonuses(ch);
else
ch->aff_abils = ch->real_abils;
ch->stat_pref_use = FALSE;
}
/* Roll the 6 stats for a character... each stat is made of the sum of the best
* 3 out of 4 rolls of a 6-sided die. Each class then decides which priority
* will be given for the best to worst stats. */
void roll_real_abils(struct char_data *ch)
static void roll_real_abils_classic(struct char_data *ch)
{
int i, j, k, temp;
ubyte table[6];
@ -265,7 +325,240 @@ void roll_real_abils(struct char_data *ch)
ch->real_abils.cha = table[5];
break;
}
ch->aff_abils = ch->real_abils;
if (HAS_VALID_SPECIES(ch))
apply_species_bonuses(ch);
else
ch->aff_abils = ch->real_abils;
}
void roll_real_abils(struct char_data *ch)
{
if (ch && ch->stat_pref_use)
roll_real_abils_preference(ch);
else
roll_real_abils_classic(ch);
}
/* Per-class skill caps */
#define DEFAULT_CLASS_SKILL_MAX 90
static int class_skill_maxes[NUM_CLASSES][MAX_SKILLS + 1];
static bool class_skill_caps_ready = FALSE;
static int clamp_skill_max(int max)
{
if (max < 0)
return 0;
if (max > 100)
return 100;
return max;
}
static void apply_class_skill(int chclass, struct char_data *ch, int skill, int start, int max)
{
if (ch)
SET_SKILL(ch, skill, start);
if (chclass >= 0 && chclass < NUM_CLASSES && skill >= 0 && skill <= MAX_SKILLS)
class_skill_maxes[chclass][skill] = clamp_skill_max(max);
}
static void apply_class_skills(int chclass, struct char_data *ch)
{
switch (chclass) {
case CLASS_SORCEROR:
apply_class_skill(chclass, ch, SPELL_MAGIC_MISSILE, 5, 90);
apply_class_skill(chclass, ch, SPELL_DETECT_INVIS, 5, 90);
apply_class_skill(chclass, ch, SPELL_DETECT_MAGIC, 5, 90);
apply_class_skill(chclass, ch, SPELL_CHILL_TOUCH, 5, 90);
apply_class_skill(chclass, ch, SPELL_INFRAVISION, 5, 90);
apply_class_skill(chclass, ch, SPELL_INVISIBLE, 5, 90);
apply_class_skill(chclass, ch, SPELL_ARMOR, 5, 90);
apply_class_skill(chclass, ch, SPELL_BURNING_HANDS, 5, 90);
apply_class_skill(chclass, ch, SPELL_LOCATE_OBJECT, 5, 90);
apply_class_skill(chclass, ch, SPELL_STRENGTH, 5, 90);
apply_class_skill(chclass, ch, SPELL_SHOCKING_GRASP, 5, 90);
apply_class_skill(chclass, ch, SPELL_SLEEP, 5, 90);
apply_class_skill(chclass, ch, SPELL_LIGHTNING_BOLT, 5, 90);
apply_class_skill(chclass, ch, SPELL_BLINDNESS, 5, 90);
apply_class_skill(chclass, ch, SPELL_DETECT_POISON, 5, 90);
apply_class_skill(chclass, ch, SPELL_COLOR_SPRAY, 5, 90);
apply_class_skill(chclass, ch, SPELL_ENERGY_DRAIN, 5, 90);
apply_class_skill(chclass, ch, SPELL_CURSE, 5, 90);
apply_class_skill(chclass, ch, SPELL_POISON, 5, 90);
apply_class_skill(chclass, ch, SPELL_FIREBALL, 5, 90);
apply_class_skill(chclass, ch, SPELL_CHARM, 5, 90);
apply_class_skill(chclass, ch, SPELL_IDENTIFY, 5, 90);
apply_class_skill(chclass, ch, SPELL_FLY, 5, 90);
apply_class_skill(chclass, ch, SPELL_ENCHANT_WEAPON, 5, 90);
apply_class_skill(chclass, ch, SPELL_CLONE, 5, 90);
apply_class_skill(chclass, ch, SKILL_UNARMED, 5, 90);
apply_class_skill(chclass, ch, SKILL_ARCANA, 5, 90);
apply_class_skill(chclass, ch, SKILL_HISTORY, 5, 90);
apply_class_skill(chclass, ch, SKILL_INSIGHT, 5, 90);
break;
case CLASS_CLERIC:
apply_class_skill(chclass, ch, SPELL_CURE_LIGHT, 5, 90);
apply_class_skill(chclass, ch, SPELL_ARMOR, 5, 90);
apply_class_skill(chclass, ch, SPELL_CREATE_FOOD, 5, 90);
apply_class_skill(chclass, ch, SPELL_CREATE_WATER, 5, 90);
apply_class_skill(chclass, ch, SPELL_DETECT_POISON, 5, 90);
apply_class_skill(chclass, ch, SPELL_CURE_BLIND, 5, 90);
apply_class_skill(chclass, ch, SPELL_BLESS, 5, 90);
apply_class_skill(chclass, ch, SPELL_DETECT_INVIS, 5, 90);
apply_class_skill(chclass, ch, SPELL_BLINDNESS, 5, 90);
apply_class_skill(chclass, ch, SPELL_INFRAVISION, 5, 90);
apply_class_skill(chclass, ch, SPELL_PROT_FROM_EVIL, 5, 90);
apply_class_skill(chclass, ch, SPELL_POISON, 5, 90);
apply_class_skill(chclass, ch, SPELL_GROUP_ARMOR, 5, 90);
apply_class_skill(chclass, ch, SPELL_CURE_CRITIC, 5, 90);
apply_class_skill(chclass, ch, SPELL_SUMMON, 5, 90);
apply_class_skill(chclass, ch, SPELL_REMOVE_POISON, 5, 90);
apply_class_skill(chclass, ch, SPELL_IDENTIFY, 5, 90);
apply_class_skill(chclass, ch, SPELL_WORD_OF_RECALL, 5, 90);
apply_class_skill(chclass, ch, SPELL_DARKNESS, 5, 90);
apply_class_skill(chclass, ch, SPELL_EARTHQUAKE, 5, 90);
apply_class_skill(chclass, ch, SPELL_SANCTUARY, 5, 90);
apply_class_skill(chclass, ch, SPELL_CALL_LIGHTNING, 5, 90);
apply_class_skill(chclass, ch, SPELL_HEAL, 5, 90);
apply_class_skill(chclass, ch, SPELL_CONTROL_WEATHER, 5, 90);
apply_class_skill(chclass, ch, SPELL_SENSE_LIFE, 5, 90);
apply_class_skill(chclass, ch, SPELL_HARM, 5, 90);
apply_class_skill(chclass, ch, SPELL_GROUP_HEAL, 5, 90);
apply_class_skill(chclass, ch, SPELL_REMOVE_CURSE, 5, 90);
apply_class_skill(chclass, ch, SKILL_SHIELD_USE, 5, 90);
apply_class_skill(chclass, ch, SKILL_ACROBATICS, 5, 90);
apply_class_skill(chclass, ch, SKILL_ARCANA, 5, 90);
apply_class_skill(chclass, ch, SKILL_RELIGION, 5, 90);
break;
case CLASS_ROGUE:
apply_class_skill(chclass, ch, SKILL_STEALTH, 5, 90);
apply_class_skill(chclass, ch, SKILL_TRACK, 5, 90);
apply_class_skill(chclass, ch, SKILL_BACKSTAB, 5, 90);
apply_class_skill(chclass, ch, SKILL_PICK_LOCK, 5, 90);
apply_class_skill(chclass, ch, SKILL_SLEIGHT_OF_HAND, 5, 90);
apply_class_skill(chclass, ch, SKILL_UNARMED, 5, 90);
apply_class_skill(chclass, ch, SKILL_SHIELD_USE, 5, 90);
apply_class_skill(chclass, ch, SKILL_PIERCING_WEAPONS, 5, 90);
apply_class_skill(chclass, ch, SKILL_PERCEPTION, 5, 90);
apply_class_skill(chclass, ch, SKILL_ACROBATICS, 5, 90);
apply_class_skill(chclass, ch, SKILL_DECEPTION, 5, 90);
apply_class_skill(chclass, ch, SKILL_INVESTIGATION, 5, 90);
break;
case CLASS_FIGHTER:
apply_class_skill(chclass, ch, SKILL_KICK, 5, 90);
apply_class_skill(chclass, ch, SKILL_RESCUE, 5, 90);
apply_class_skill(chclass, ch, SKILL_BANDAGE, 5, 90);
apply_class_skill(chclass, ch, SKILL_BASH, 5, 90);
apply_class_skill(chclass, ch, SKILL_UNARMED, 5, 90);
apply_class_skill(chclass, ch, SKILL_SLASHING_WEAPONS, 5, 90);
apply_class_skill(chclass, ch, SKILL_PIERCING_WEAPONS, 5, 90);
apply_class_skill(chclass, ch, SKILL_BLUDGEONING_WEAPONS, 5, 90);
apply_class_skill(chclass, ch, SKILL_SHIELD_USE, 5, 90);
apply_class_skill(chclass, ch, SKILL_PERCEPTION, 5, 90);
apply_class_skill(chclass, ch, SKILL_ATHLETICS, 5, 90);
apply_class_skill(chclass, ch, SKILL_INTIMIDATION, 5, 90);
apply_class_skill(chclass, ch, SKILL_SURVIVAL, 5, 90);
break;
case CLASS_BARBARIAN:
apply_class_skill(chclass, ch, SKILL_KICK, 5, 90);
apply_class_skill(chclass, ch, SKILL_RESCUE, 5, 90);
apply_class_skill(chclass, ch, SKILL_BANDAGE, 5, 90);
apply_class_skill(chclass, ch, SKILL_WHIRLWIND, 5, 90);
apply_class_skill(chclass, ch, SKILL_UNARMED, 5, 90);
apply_class_skill(chclass, ch, SKILL_PIERCING_WEAPONS, 5, 90);
apply_class_skill(chclass, ch, SKILL_BLUDGEONING_WEAPONS, 5, 90);
apply_class_skill(chclass, ch, SKILL_PERCEPTION, 5, 90);
apply_class_skill(chclass, ch, SKILL_ATHLETICS, 5, 90);
apply_class_skill(chclass, ch, SKILL_INTIMIDATION, 5, 90);
break;
case CLASS_RANGER:
apply_class_skill(chclass, ch, SKILL_BANDAGE, 5, 90);
apply_class_skill(chclass, ch, SKILL_TRACK, 5, 90);
apply_class_skill(chclass, ch, SKILL_BASH, 5, 90);
apply_class_skill(chclass, ch, SKILL_SLEIGHT_OF_HAND, 5, 90);
apply_class_skill(chclass, ch, SKILL_UNARMED, 5, 90);
apply_class_skill(chclass, ch, SKILL_SLASHING_WEAPONS, 5, 90);
apply_class_skill(chclass, ch, SKILL_PIERCING_WEAPONS, 5, 90);
apply_class_skill(chclass, ch, SKILL_SHIELD_USE, 5, 90);
apply_class_skill(chclass, ch, SKILL_PERCEPTION, 5, 90);
apply_class_skill(chclass, ch, SKILL_NATURE, 5, 90);
apply_class_skill(chclass, ch, SKILL_ANIMAL_HANDLING, 5, 90);
apply_class_skill(chclass, ch, SKILL_SURVIVAL, 5, 90);
apply_class_skill(chclass, ch, SKILL_ATHLETICS, 5, 90);
apply_class_skill(chclass, ch, SKILL_PERSUASION, 5, 90);
apply_class_skill(chclass, ch, SKILL_STEALTH, 5, 90);
break;
case CLASS_BARD:
apply_class_skill(chclass, ch, SPELL_ARMOR, 5, 90);
apply_class_skill(chclass, ch, SPELL_IDENTIFY, 5, 90);
apply_class_skill(chclass, ch, SKILL_BANDAGE, 5, 90);
apply_class_skill(chclass, ch, SKILL_TRACK, 5, 90);
apply_class_skill(chclass, ch, SKILL_PICK_LOCK, 5, 90);
apply_class_skill(chclass, ch, SKILL_SLEIGHT_OF_HAND, 5, 90);
apply_class_skill(chclass, ch, SKILL_UNARMED, 5, 90);
apply_class_skill(chclass, ch, SKILL_PIERCING_WEAPONS, 5, 90);
apply_class_skill(chclass, ch, SKILL_SHIELD_USE, 5, 90);
apply_class_skill(chclass, ch, SKILL_PERCEPTION, 5, 90);
apply_class_skill(chclass, ch, SKILL_ACROBATICS, 5, 90);
apply_class_skill(chclass, ch, SKILL_HISTORY, 5, 90);
apply_class_skill(chclass, ch, SKILL_INVESTIGATION, 5, 90);
apply_class_skill(chclass, ch, SKILL_SURVIVAL, 5, 90);
apply_class_skill(chclass, ch, SKILL_STEALTH, 5, 90);
break;
case CLASS_DRUID:
apply_class_skill(chclass, ch, SPELL_DETECT_INVIS, 5, 90);
apply_class_skill(chclass, ch, SPELL_DETECT_MAGIC, 5, 90);
apply_class_skill(chclass, ch, SPELL_LOCATE_OBJECT, 5, 90);
apply_class_skill(chclass, ch, SKILL_BANDAGE, 5, 90);
apply_class_skill(chclass, ch, SKILL_TRACK, 5, 90);
apply_class_skill(chclass, ch, SKILL_UNARMED, 5, 90);
apply_class_skill(chclass, ch, SKILL_PIERCING_WEAPONS, 5, 90);
apply_class_skill(chclass, ch, SKILL_SHIELD_USE, 5, 90);
apply_class_skill(chclass, ch, SKILL_PERCEPTION, 5, 90);
apply_class_skill(chclass, ch, SKILL_ACROBATICS, 5, 90);
apply_class_skill(chclass, ch, SKILL_ARCANA, 5, 90);
apply_class_skill(chclass, ch, SKILL_HISTORY, 5, 90);
apply_class_skill(chclass, ch, SKILL_INSIGHT, 5, 90);
apply_class_skill(chclass, ch, SKILL_INVESTIGATION, 5, 90);
apply_class_skill(chclass, ch, SKILL_PERSUASION, 5, 90);
apply_class_skill(chclass, ch, SKILL_RELIGION, 5, 90);
apply_class_skill(chclass, ch, SKILL_SURVIVAL, 5, 90);
break;
}
}
void init_class_skill_caps(void)
{
int chclass, skill;
for (chclass = 0; chclass < NUM_CLASSES; chclass++) {
for (skill = 0; skill <= MAX_SKILLS; skill++)
class_skill_maxes[chclass][skill] = DEFAULT_CLASS_SKILL_MAX;
}
for (chclass = 0; chclass < NUM_CLASSES; chclass++)
apply_class_skills(chclass, NULL);
class_skill_caps_ready = TRUE;
}
int class_skill_max(int chclass, int skillnum)
{
if (!class_skill_caps_ready)
return DEFAULT_CLASS_SKILL_MAX;
if (chclass < 0 || chclass >= NUM_CLASSES)
return DEFAULT_CLASS_SKILL_MAX;
if (skillnum < 0 || skillnum > MAX_SKILLS)
return DEFAULT_CLASS_SKILL_MAX;
return class_skill_maxes[chclass][skillnum];
}
void grant_class_skills(struct char_data *ch, bool reset)
@ -282,200 +575,39 @@ void grant_class_skills(struct char_data *ch, bool reset)
if (GET_CLASS(ch) < CLASS_SORCEROR || GET_CLASS(ch) >= NUM_CLASSES)
return;
switch (GET_CLASS(ch)) {
case CLASS_SORCEROR:
SET_SKILL(ch, SPELL_MAGIC_MISSILE, 5);
SET_SKILL(ch, SPELL_DETECT_INVIS, 5);
SET_SKILL(ch, SPELL_DETECT_MAGIC, 5);
SET_SKILL(ch, SPELL_CHILL_TOUCH, 5);
SET_SKILL(ch, SPELL_INFRAVISION, 5);
SET_SKILL(ch, SPELL_INVISIBLE, 5);
SET_SKILL(ch, SPELL_ARMOR, 5);
SET_SKILL(ch, SPELL_BURNING_HANDS, 5);
SET_SKILL(ch, SPELL_LOCATE_OBJECT, 5);
SET_SKILL(ch, SPELL_STRENGTH, 5);
SET_SKILL(ch, SPELL_SHOCKING_GRASP, 5);
SET_SKILL(ch, SPELL_SLEEP, 5);
SET_SKILL(ch, SPELL_LIGHTNING_BOLT, 5);
SET_SKILL(ch, SPELL_BLINDNESS, 5);
SET_SKILL(ch, SPELL_DETECT_POISON, 5);
SET_SKILL(ch, SPELL_COLOR_SPRAY, 5);
SET_SKILL(ch, SPELL_ENERGY_DRAIN, 5);
SET_SKILL(ch, SPELL_CURSE, 5);
SET_SKILL(ch, SPELL_POISON, 5);
SET_SKILL(ch, SPELL_FIREBALL, 5);
SET_SKILL(ch, SPELL_CHARM, 5);
SET_SKILL(ch, SPELL_IDENTIFY, 5);
SET_SKILL(ch, SPELL_FLY, 5);
SET_SKILL(ch, SPELL_ENCHANT_WEAPON, 5);
SET_SKILL(ch, SPELL_CLONE, 5);
SET_SKILL(ch, SKILL_UNARMED, 5);
SET_SKILL(ch, SKILL_ARCANA, 5);
SET_SKILL(ch, SKILL_HISTORY, 5);
SET_SKILL(ch, SKILL_INSIGHT, 5);
break;
case CLASS_CLERIC:
SET_SKILL(ch, SPELL_CURE_LIGHT, 5);
SET_SKILL(ch, SPELL_ARMOR, 5);
SET_SKILL(ch, SPELL_CREATE_FOOD, 5);
SET_SKILL(ch, SPELL_CREATE_WATER, 5);
SET_SKILL(ch, SPELL_DETECT_POISON, 5);
SET_SKILL(ch, SPELL_DETECT_ALIGN, 5);
SET_SKILL(ch, SPELL_CURE_BLIND, 5);
SET_SKILL(ch, SPELL_BLESS, 5);
SET_SKILL(ch, SPELL_DETECT_INVIS, 5);
SET_SKILL(ch, SPELL_BLINDNESS, 5);
SET_SKILL(ch, SPELL_INFRAVISION, 5);
SET_SKILL(ch, SPELL_PROT_FROM_EVIL, 5);
SET_SKILL(ch, SPELL_POISON, 5);
SET_SKILL(ch, SPELL_GROUP_ARMOR, 5);
SET_SKILL(ch, SPELL_CURE_CRITIC, 5);
SET_SKILL(ch, SPELL_SUMMON, 5);
SET_SKILL(ch, SPELL_REMOVE_POISON, 5);
SET_SKILL(ch, SPELL_IDENTIFY, 5);
SET_SKILL(ch, SPELL_WORD_OF_RECALL, 5);
SET_SKILL(ch, SPELL_DARKNESS, 5);
SET_SKILL(ch, SPELL_EARTHQUAKE, 5);
SET_SKILL(ch, SPELL_DISPEL_EVIL, 5);
SET_SKILL(ch, SPELL_DISPEL_GOOD, 5);
SET_SKILL(ch, SPELL_SANCTUARY, 5);
SET_SKILL(ch, SPELL_CALL_LIGHTNING, 5);
SET_SKILL(ch, SPELL_HEAL, 5);
SET_SKILL(ch, SPELL_CONTROL_WEATHER, 5);
SET_SKILL(ch, SPELL_SENSE_LIFE, 5);
SET_SKILL(ch, SPELL_HARM, 5);
SET_SKILL(ch, SPELL_GROUP_HEAL, 5);
SET_SKILL(ch, SPELL_REMOVE_CURSE, 5);
SET_SKILL(ch, SKILL_SHIELD_USE, 5);
SET_SKILL(ch, SKILL_ACROBATICS, 5);
SET_SKILL(ch, SKILL_ARCANA, 5);
SET_SKILL(ch, SKILL_RELIGION, 5);
break;
case CLASS_ROGUE:
SET_SKILL(ch, SKILL_STEALTH, 5);
SET_SKILL(ch, SKILL_TRACK, 5);
SET_SKILL(ch, SKILL_BACKSTAB, 5);
SET_SKILL(ch, SKILL_PICK_LOCK, 5);
SET_SKILL(ch, SKILL_SLEIGHT_OF_HAND, 5);
SET_SKILL(ch, SKILL_UNARMED, 5);
SET_SKILL(ch, SKILL_SHIELD_USE, 5);
SET_SKILL(ch, SKILL_PIERCING_WEAPONS, 5);
SET_SKILL(ch, SKILL_PERCEPTION, 5);
SET_SKILL(ch, SKILL_ACROBATICS, 5);
SET_SKILL(ch, SKILL_DECEPTION, 5);
SET_SKILL(ch, SKILL_INVESTIGATION, 5);
break;
case CLASS_FIGHTER:
SET_SKILL(ch, SKILL_KICK, 5);
SET_SKILL(ch, SKILL_RESCUE, 5);
SET_SKILL(ch, SKILL_BANDAGE, 5);
SET_SKILL(ch, SKILL_BASH, 5);
SET_SKILL(ch, SKILL_UNARMED, 5);
SET_SKILL(ch, SKILL_SLASHING_WEAPONS, 5);
SET_SKILL(ch, SKILL_PIERCING_WEAPONS, 5);
SET_SKILL(ch, SKILL_BLUDGEONING_WEAPONS, 5);
SET_SKILL(ch, SKILL_SHIELD_USE, 5);
SET_SKILL(ch, SKILL_PERCEPTION, 5);
SET_SKILL(ch, SKILL_ATHLETICS, 5);
SET_SKILL(ch, SKILL_INTIMIDATION, 5);
SET_SKILL(ch, SKILL_SURVIVAL, 5);
break;
case CLASS_BARBARIAN:
SET_SKILL(ch, SKILL_KICK, 5);
SET_SKILL(ch, SKILL_RESCUE, 5);
SET_SKILL(ch, SKILL_BANDAGE, 5);
SET_SKILL(ch, SKILL_WHIRLWIND, 5);
SET_SKILL(ch, SKILL_UNARMED, 5);
SET_SKILL(ch, SKILL_PIERCING_WEAPONS, 5);
SET_SKILL(ch, SKILL_BLUDGEONING_WEAPONS, 5);
SET_SKILL(ch, SKILL_PERCEPTION, 5);
SET_SKILL(ch, SKILL_ATHLETICS, 5);
SET_SKILL(ch, SKILL_INTIMIDATION, 5);
break;
case CLASS_RANGER:
SET_SKILL(ch, SKILL_BANDAGE, 5);
SET_SKILL(ch, SKILL_TRACK, 5);
SET_SKILL(ch, SKILL_BASH, 5);
SET_SKILL(ch, SKILL_SLEIGHT_OF_HAND, 5);
SET_SKILL(ch, SKILL_UNARMED, 5);
SET_SKILL(ch, SKILL_SLASHING_WEAPONS, 5);
SET_SKILL(ch, SKILL_PIERCING_WEAPONS, 5);
SET_SKILL(ch, SKILL_SHIELD_USE, 5);
SET_SKILL(ch, SKILL_PERCEPTION, 5);
SET_SKILL(ch, SKILL_NATURE, 5);
SET_SKILL(ch, SKILL_ANIMAL_HANDLING, 5);
SET_SKILL(ch, SKILL_SURVIVAL, 5);
SET_SKILL(ch, SKILL_ATHLETICS, 5);
SET_SKILL(ch, SKILL_PERSUASION, 5);
SET_SKILL(ch, SKILL_STEALTH, 5);
break;
case CLASS_BARD:
SET_SKILL(ch, SPELL_ARMOR, 5);
SET_SKILL(ch, SPELL_IDENTIFY, 5);
SET_SKILL(ch, SKILL_BANDAGE, 5);
SET_SKILL(ch, SKILL_TRACK, 5);
SET_SKILL(ch, SKILL_PICK_LOCK, 5);
SET_SKILL(ch, SKILL_SLEIGHT_OF_HAND, 5);
SET_SKILL(ch, SKILL_UNARMED, 5);
SET_SKILL(ch, SKILL_PIERCING_WEAPONS, 5);
SET_SKILL(ch, SKILL_SHIELD_USE, 5);
SET_SKILL(ch, SKILL_PERCEPTION, 5);
SET_SKILL(ch, SKILL_ACROBATICS, 5);
SET_SKILL(ch, SKILL_HISTORY, 5);
SET_SKILL(ch, SKILL_INVESTIGATION, 5);
SET_SKILL(ch, SKILL_SURVIVAL, 5);
SET_SKILL(ch, SKILL_STEALTH, 5);
break;
case CLASS_DRUID:
SET_SKILL(ch, SPELL_DETECT_INVIS, 5);
SET_SKILL(ch, SPELL_DETECT_MAGIC, 5);
SET_SKILL(ch, SPELL_LOCATE_OBJECT, 5);
SET_SKILL(ch, SKILL_BANDAGE, 5);
SET_SKILL(ch, SKILL_TRACK, 5);
SET_SKILL(ch, SKILL_UNARMED, 5);
SET_SKILL(ch, SKILL_PIERCING_WEAPONS, 5);
SET_SKILL(ch, SKILL_SHIELD_USE, 5);
SET_SKILL(ch, SKILL_PERCEPTION, 5);
SET_SKILL(ch, SKILL_ACROBATICS, 5);
SET_SKILL(ch, SKILL_ARCANA, 5);
SET_SKILL(ch, SKILL_HISTORY, 5);
SET_SKILL(ch, SKILL_INSIGHT, 5);
SET_SKILL(ch, SKILL_INVESTIGATION, 5);
SET_SKILL(ch, SKILL_PERSUASION, 5);
SET_SKILL(ch, SKILL_RELIGION, 5);
SET_SKILL(ch, SKILL_SURVIVAL, 5);
break;
}
apply_class_skills(GET_CLASS(ch), ch);
}
/* Some initializations for characters, including initial skills */
void do_start(struct char_data *ch)
{
int base_hit = 90;
int base_mana = 100;
int base_stamina = 90;
GET_LEVEL(ch) = 1;
GET_EXP(ch) = 1;
roll_real_abils(ch);
GET_MAX_HIT(ch) = 90;
GET_MAX_MANA(ch) = 100;
GET_MAX_MOVE(ch) = 90;
if (!get_species_base_points(GET_SPECIES(ch), &base_hit, &base_mana, &base_stamina)) {
base_hit = 90;
base_mana = 100;
base_stamina = 90;
}
GET_MAX_HIT(ch) = base_hit;
GET_MAX_MANA(ch) = base_mana;
GET_MAX_STAMINA(ch) = base_stamina;
grant_class_skills(ch, TRUE);
grant_species_skills(ch);
advance_level(ch);
GET_HIT(ch) = GET_MAX_HIT(ch);
GET_MANA(ch) = GET_MAX_MANA(ch);
GET_MOVE(ch) = GET_MAX_MOVE(ch);
GET_STAMINA(ch) = GET_MAX_STAMINA(ch);
GET_COND(ch, THIRST) = 24;
GET_COND(ch, HUNGER) = 24;
@ -485,12 +617,26 @@ void do_start(struct char_data *ch)
SET_BIT_AR(PLR_FLAGS(ch), PLR_SITEOK);
}
/* This function controls the change to maxmove, maxmana, and maxhp for each
/* This function controls the change to maxstamina, maxmana, and maxhp for each
* class every time they gain a level. */
void advance_level(struct char_data *ch)
{
int add_hp, add_mana = 0, add_move = 0, i;
if (GET_LEVEL(ch) >= LVL_IMMORT) {
GET_MAX_HIT(ch) = 999;
GET_MAX_MANA(ch) = 999;
GET_MAX_STAMINA(ch) = 999;
for (i = 0; i < 3; i++)
GET_COND(ch, i) = (char) -1;
SET_BIT_AR(PRF_FLAGS(ch), PRF_HOLYLIGHT);
snoop_check(ch);
save_char(ch);
return;
}
add_hp = GET_ABILITY_MOD(GET_CON(ch));
switch (GET_CLASS(ch)) {
@ -549,7 +695,7 @@ void advance_level(struct char_data *ch)
}
ch->points.max_hit += MAX(1, add_hp);
ch->points.max_move += MAX(1, add_move);
ch->points.max_stamina += MAX(1, add_move);
if (GET_LEVEL(ch) > 1)
ch->points.max_mana += add_mana;
@ -577,32 +723,12 @@ int backstab_mult(int level)
}
/* invalid_class is used by handler.c to determine if a piece of equipment is
* usable by a particular class, based on the ITEM_ANTI_{class} bitvectors. */
* usable by a particular class. Class-based anti item flags are no longer
* enforced. */
int invalid_class(struct char_data *ch, struct obj_data *obj)
{
if (OBJ_FLAGGED(obj, ITEM_ANTI_SORCEROR) && IS_SORCEROR(ch))
return TRUE;
if (OBJ_FLAGGED(obj, ITEM_ANTI_CLERIC) && IS_CLERIC(ch))
return TRUE;
if (OBJ_FLAGGED(obj, ITEM_ANTI_FIGHTER) && IS_FIGHTER(ch))
return TRUE;
if (OBJ_FLAGGED(obj, ITEM_ANTI_ROGUE) && IS_ROGUE(ch))
return TRUE;
if (OBJ_FLAGGED(obj, ITEM_ANTI_BARBARIAN) && IS_BARBARIAN(ch))
return TRUE;
if (OBJ_FLAGGED(obj, ITEM_ANTI_RANGER) && IS_RANGER(ch))
return TRUE;
if (OBJ_FLAGGED(obj, ITEM_ANTI_BARD) && IS_BARD(ch))
return TRUE;
if (OBJ_FLAGGED(obj, ITEM_ANTI_DRUID) && IS_DRUID(ch))
return TRUE;
(void) ch;
(void) obj;
return FALSE;
}
@ -649,7 +775,6 @@ void init_spell_levels(void)
spell_level(SPELL_CREATE_FOOD, CLASS_CLERIC, 1);
spell_level(SPELL_CREATE_WATER, CLASS_CLERIC, 1);
spell_level(SPELL_DETECT_POISON, CLASS_CLERIC, 1);
spell_level(SPELL_DETECT_ALIGN, CLASS_CLERIC, 1);
spell_level(SPELL_CURE_BLIND, CLASS_CLERIC, 1);
spell_level(SPELL_BLESS, CLASS_CLERIC, 1);
spell_level(SPELL_DETECT_INVIS, CLASS_CLERIC, 1);
@ -665,8 +790,6 @@ void init_spell_levels(void)
spell_level(SPELL_WORD_OF_RECALL, CLASS_CLERIC, 1);
spell_level(SPELL_DARKNESS, CLASS_CLERIC, 1);
spell_level(SPELL_EARTHQUAKE, CLASS_CLERIC, 1);
spell_level(SPELL_DISPEL_EVIL, CLASS_CLERIC, 1);
spell_level(SPELL_DISPEL_GOOD, CLASS_CLERIC, 1);
spell_level(SPELL_SANCTUARY, CLASS_CLERIC, 1);
spell_level(SPELL_CALL_LIGHTNING, CLASS_CLERIC, 1);
spell_level(SPELL_HEAL, CLASS_CLERIC, 1);

View file

@ -23,13 +23,14 @@ int parse_class(char arg);
void roll_real_abils(struct char_data *ch);
bool has_save_proficiency(int class_num, int ability);
void grant_class_skills(struct char_data *ch, bool reset);
void init_class_skill_caps(void);
int class_skill_max(int chclass, int skillnum);
/* Global variables */
extern const char *class_abbrevs[];
extern const char *pc_class_types[];
extern const char *class_menu;
extern int prac_params[][NUM_CLASSES];
extern struct guild_info_type guild_info[];
#endif /* _CLASS_H_*/

View file

@ -1145,8 +1145,8 @@ static char *make_prompt(struct descriptor_data *d)
if (count >= 0)
len += count;
}
if (GET_MOVE(ch) << 2 < GET_MAX_MOVE(ch) && len < sizeof(prompt)) {
count = snprintf(prompt + len, sizeof(prompt) - len, "%dV ", GET_MOVE(ch));
if (GET_STAMINA(ch) << 2 < GET_MAX_STAMINA(ch) && len < sizeof(prompt)) {
count = snprintf(prompt + len, sizeof(prompt) - len, "%dS ", GET_STAMINA(ch));
if (count >= 0)
len += count;
}
@ -1163,8 +1163,8 @@ static char *make_prompt(struct descriptor_data *d)
len += count;
}
if (PRF_FLAGGED(d->character, PRF_DISPMOVE) && len < sizeof(prompt)) {
count = snprintf(prompt + len, sizeof(prompt) - len, "%dV ", GET_MOVE(d->character));
if (PRF_FLAGGED(d->character, PRF_DISPSTAMINA) && len < sizeof(prompt)) {
count = snprintf(prompt + len, sizeof(prompt) - len, "%dS ", GET_STAMINA(d->character));
if (count >= 0)
len += count;
}
@ -2795,8 +2795,8 @@ static void msdp_update( void )
MSDPSetNumber( d, eMSDP_MANA_MAX, GET_MAX_MANA(ch) );
MSDPSetNumber( d, eMSDP_WIMPY, GET_WIMP_LEV(ch) );
MSDPSetNumber( d, eMSDP_MONEY, GET_COINS(ch) );
MSDPSetNumber( d, eMSDP_MOVEMENT, GET_MOVE(ch) );
MSDPSetNumber( d, eMSDP_MOVEMENT_MAX, GET_MAX_MOVE(ch) );
MSDPSetNumber( d, eMSDP_MOVEMENT, GET_STAMINA(ch) );
MSDPSetNumber( d, eMSDP_MOVEMENT_MAX, GET_MAX_STAMINA(ch) );
MSDPSetNumber( d, eMSDP_AC, compute_armor_class(ch) );
/* This would be better moved elsewhere */

View file

@ -39,13 +39,6 @@
/* Can Scripts be attached to players? */
int script_players = NO;
/* pk_allowed sets the tone of the entire game. If pk_allowed is set to NO,
* then players will not be allowed to kill, summon, charm, or sleep other
* players, as well as a variety of other "asshole player" protections. However,
* if you decide you want to have an all-out knock-down drag-out PK Mud, just
* set pk_allowed to YES - and anything goes. */
int pk_allowed = NO;
/* Is playerthieving allowed? */
int pt_allowed = NO;

View file

@ -14,7 +14,6 @@
#define _CONFIG_H_
/* Global variable declarations, all settable by cedit */
extern int pk_allowed;
extern int script_players;
extern int pt_allowed;
extern int level_can_shout;

View file

@ -189,8 +189,8 @@ const char *position_types[] = {
* @pre Must be in the same order as the defines.
* Must end array with a single newline. */
const char *player_bits[] = {
"KILLER",
"THIEF",
"UNUSED0",
"UNUSED1",
"FROZEN",
"DONTSET",
"WRITING",
@ -228,9 +228,9 @@ const char *action_bits[] = {
"AGGR",
"STAY-ZONE",
"WIMPY",
"AGGR_EVIL",
"AGGR_GOOD",
"AGGR_NEUTRAL",
"RESERVED",
"RESERVED",
"RESERVED",
"MEMORY",
"HELPER",
"NO_CHARM",
@ -240,6 +240,7 @@ const char *action_bits[] = {
"NO_BLIND",
"NO_KILL",
"DEAD", /* You should never see this. */
"MOUNT",
"\n"
};
@ -253,7 +254,7 @@ const char *preference_bits[] = {
"NO_TELL",
"D_HP",
"D_MANA",
"D_MOVE",
"D_STAMINA",
"AUTOEX",
"NO_HASS",
"QUEST",
@ -289,7 +290,6 @@ const char *affected_bits[] =
"\0", /* DO NOT REMOVE!! */
"BLIND",
"INVIS",
"DET-ALIGN",
"DET-INVIS",
"DET-MAGIC",
"SENSE-LIFE",
@ -299,8 +299,8 @@ const char *affected_bits[] =
"CURSE",
"INFRA",
"POISON",
"PROT-EVIL",
"PROT-GOOD",
"RESERVED",
"RESERVED",
"SLEEP",
"NO_TRACK",
"FLY",
@ -311,6 +311,7 @@ const char *affected_bits[] =
"CHARM",
"BANDAGED",
"LISTEN",
"MOUNTED",
"\n"
};
@ -326,7 +327,9 @@ const char *connected_types[] = {
"Get new PW",
"Confirm new PW",
"Select sex",
"Select species",
"Select class",
"Short description",
"Reading MOTD",
"Main Menu",
"Get descript.",
@ -350,7 +353,18 @@ const char *connected_types[] = {
"Preference edit",
"IBT edit",
"Message edit",
"Protocol Detection",
"Background/Protocol",
"Connect menu",
"Get account",
"Confirm account",
"Account password",
"New account PW",
"Confirm account PW",
"Account email",
"Account menu",
"Account list",
"Stat preference",
"Select age",
"\n"
};
@ -472,17 +486,6 @@ const char *extra_bits[] = {
"MAGIC",
"NO_DROP",
"BLESS",
"ANTI_GOOD",
"ANTI_EVIL",
"ANTI_NEUTRAL",
"ANTI_SORCEROR",
"ANTI_CLERIC",
"ANTI_ROGUE",
"ANTI_FIGHTER",
"ANTI_BARBARIAN",
"ANTI_RANGER",
"ANTI_BARD",
"ANTI_DRUID",
"NO_SELL",
"QUEST_ITEM",
"HOOD_UP",
@ -511,7 +514,7 @@ const char *apply_types[] = {
"CHAR_HEIGHT",
"MAXMANA",
"MAXHIT",
"MAXMOVE",
"MAXSTAMINA",
"COINS",
"EXP",
"ARMOR",

178
src/db.c
View file

@ -21,6 +21,7 @@
#include "house.h"
#include "constants.h"
#include "oasis.h"
#include "species.h"
#include "dg_scripts.h"
#include "dg_event.h"
#include "act.h"
@ -30,6 +31,7 @@
#include "genolc.h"
#include "genobj.h" /* for free_object_strings */
#include "config.h" /* for the default config values. */
#include "class.h"
#include "fight.h"
#include "modify.h"
#include "shop.h"
@ -38,7 +40,6 @@
#include "mud_event.h"
#include "msgedit.h"
#include "screen.h"
#include "roomsave.h"
#include <sys/stat.h>
/* declarations of most of the 'global' variables */
@ -748,6 +749,7 @@ void boot_db(void)
log("Assigning spell and skill levels.");
init_spell_levels();
init_class_skill_caps();
log("Sorting command list and spells.");
sort_commands();
@ -1600,10 +1602,10 @@ static void parse_simple_mob(FILE *mob_f, int i, int nr)
GET_MAX_HIT(mob_proto + i) = 0;
GET_HIT(mob_proto + i) = t[1];
GET_MANA(mob_proto + i) = t[2];
GET_MOVE(mob_proto + i) = t[3];
GET_STAMINA(mob_proto + i) = t[3];
GET_MAX_MANA(mob_proto + i) = 10;
GET_MAX_MOVE(mob_proto + i) = 50;
GET_MAX_STAMINA(mob_proto + i) = 50;
if (!get_line(mob_f, line)) {
log("SYSERR: Format error in last line of mob #%d\n"
@ -1622,6 +1624,7 @@ static void parse_simple_mob(FILE *mob_f, int i, int nr)
GET_SEX(mob_proto + i) = t[2];
GET_CLASS(mob_proto + i) = CLASS_UNDEFINED;
GET_SPECIES(mob_proto + i) = SPECIES_UNDEFINED;
GET_WEIGHT(mob_proto + i) = 200;
GET_HEIGHT(mob_proto + i) = 198;
@ -1705,6 +1708,14 @@ static void interpret_espec(const char *keyword, const char *value, int i, int n
RANGE(CLASS_UNDEFINED, NUM_CLASSES - 1);
mob_proto[i].player.chclass = num_arg;
}
CASE("Species") {
RANGE(SPECIES_UNDEFINED, NUM_SPECIES - 1);
mob_proto[i].player.species = num_arg;
}
CASE("Age") {
RANGE(MIN_CHAR_AGE, MAX_CHAR_AGE);
mob_proto[i].player.roleplay_age = num_arg;
}
/* --- 5e-style Saving Throws --- */
CASE("SaveStr") {
@ -1890,13 +1901,6 @@ void parse_mobile(FILE *mob_f, int nr)
REMOVE_BIT_AR(AFF_FLAGS(mob_proto + i), AFF_CHARM);
REMOVE_BIT_AR(AFF_FLAGS(mob_proto + i), AFF_POISON);
REMOVE_BIT_AR(AFF_FLAGS(mob_proto + i), AFF_SLEEP);
if (MOB_FLAGGED(mob_proto + i, MOB_AGGRESSIVE) && MOB_FLAGGED(mob_proto + i, MOB_AGGR_GOOD))
REMOVE_BIT_AR(MOB_FLAGS(mob_proto + i), MOB_AGGR_GOOD);
if (MOB_FLAGGED(mob_proto + i, MOB_AGGRESSIVE) && MOB_FLAGGED(mob_proto + i, MOB_AGGR_NEUTRAL))
REMOVE_BIT_AR(MOB_FLAGS(mob_proto + i), MOB_AGGR_NEUTRAL);
if (MOB_FLAGGED(mob_proto + i, MOB_AGGRESSIVE) && MOB_FLAGGED(mob_proto + i, MOB_AGGR_EVIL))
REMOVE_BIT_AR(MOB_FLAGS(mob_proto + i), MOB_AGGR_EVIL);
check_bitvector_names(AFF_FLAGS(mob_proto + i)[0], affected_bits_count, buf2, "mobile affect");
/* This is necessary, since if we have conventional world files, &letter is
@ -1955,6 +1959,13 @@ void parse_mobile(FILE *mob_f, int nr)
exit(1);
}
if (mob_proto[i].player.time.birth == 0)
mob_proto[i].player.time.birth = time(0);
if (mob_proto[i].player.roleplay_age == 0)
mob_proto[i].player.roleplay_age = MIN_CHAR_AGE;
if (mob_proto[i].player.roleplay_age_year == 0)
mob_proto[i].player.roleplay_age_year = time_info.year;
letter = fread_letter(mob_f);
while (letter == 'L') {
int wpos = -1, vnum = -1, qty = 1;
@ -1969,7 +1980,7 @@ void parse_mobile(FILE *mob_f, int nr)
log("SYSERR: Bad 'L' line in mob #%d: '%s' (need <wear_pos> <obj_vnum> [qty]).", nr, line);
} else {
if (qty < 1) qty = 1;
loadout_add_entry(&mob_proto[i].proto_loadout, vnum, (int)wpos, qty);
loadout_append_entry(&mob_proto[i].proto_loadout, vnum, (int)wpos, qty);
}
/* look ahead to see if there is another 'L' */
letter = fread_letter(mob_f);
@ -2062,7 +2073,7 @@ void parse_mobile(FILE *mob_f, int nr)
log("SYSERR: Bad post-trigger 'L' line in mob #%d: '%s' (need <wear_pos> <obj_vnum> [qty]).", nr, line);
} else {
if (qty < 1) qty = 1;
loadout_add_entry(&mob_proto[i].proto_loadout, vnum, (int)wpos, qty);
loadout_append_entry(&mob_proto[i].proto_loadout, vnum, (int)wpos, qty);
}
letter = fread_letter(mob_f);
}
@ -2634,6 +2645,11 @@ void equip_mob_from_loadout(struct char_data *mob)
const struct mob_loadout *e = mob_proto[rnum].proto_loadout;
if (!e) return;
struct obj_data *stack[16];
int i;
for (i = 0; i < (int)(sizeof(stack) / sizeof(stack[0])); i++)
stack[i] = NULL;
for (; e; e = e->next) {
int qty = (e->quantity > 0) ? e->quantity : 1;
@ -2645,46 +2661,73 @@ void equip_mob_from_loadout(struct char_data *mob)
continue;
}
/* Inventory-only request */
if (e->wear_pos < 0) {
obj_to_char(obj, mob);
continue;
}
if (e->wear_pos >= 0) {
for (i = 0; i < (int)(sizeof(stack) / sizeof(stack[0])); i++)
stack[i] = NULL;
/* If the intended slot is free, place it there. We trust the saved slot. */
if (e->wear_pos >= 0 && e->wear_pos < NUM_WEARS && GET_EQ(mob, e->wear_pos) == NULL) {
/* If the intended slot is free, place it there. We trust the saved slot. */
if (e->wear_pos < NUM_WEARS && GET_EQ(mob, e->wear_pos) == NULL) {
#ifdef STRICT_WEAR_CHECK
/* Optional strict flag check (may be mismatched in customized codebases). */
if (!invalid_align(mob, obj) /* example gate, add yours as needed */) {
equip_char(mob, obj, e->wear_pos);
continue;
}
/* If strict check fails, try alt or inventory below. */
#else
equip_char(mob, obj, e->wear_pos);
continue;
#endif
}
/* Try the mirrored slot for finger/neck/wrist pairs if intended is occupied. */
{
int alt = find_alt_slot_same_family(mob, e->wear_pos);
if (alt >= 0 && GET_EQ(mob, alt) == NULL) {
#ifdef STRICT_WEAR_CHECK
if (!invalid_align(mob, obj)) {
equip_char(mob, obj, alt);
continue;
/* Optional strict flag check (may be mismatched in customized codebases). */
if (!invalid_align(mob, obj) /* example gate, add yours as needed */) {
equip_char(mob, obj, e->wear_pos);
} else {
obj_to_char(obj, mob);
}
#else
equip_char(mob, obj, alt);
continue;
equip_char(mob, obj, e->wear_pos);
#endif
} else {
/* Try the mirrored slot for finger/neck/wrist pairs if intended is occupied. */
int alt = find_alt_slot_same_family(mob, e->wear_pos);
if (alt >= 0 && GET_EQ(mob, alt) == NULL) {
#ifdef STRICT_WEAR_CHECK
if (!invalid_align(mob, obj)) {
equip_char(mob, obj, alt);
} else {
obj_to_char(obj, mob);
}
#else
equip_char(mob, obj, alt);
#endif
} else {
/* Couldnt place it — keep in inventory. */
obj_to_char(obj, mob);
}
}
if (obj_is_storage(obj) || GET_OBJ_TYPE(obj) == ITEM_FURNITURE)
stack[0] = obj;
continue;
}
/* Couldnt place it — keep in inventory. */
obj_to_char(obj, mob);
/* Inventory-only request */
if (e->wear_pos == -1) {
for (i = 0; i < (int)(sizeof(stack) / sizeof(stack[0])); i++)
stack[i] = NULL;
obj_to_char(obj, mob);
if (obj_is_storage(obj) || GET_OBJ_TYPE(obj) == ITEM_FURNITURE)
stack[0] = obj;
continue;
}
/* Nested inventory: wear_pos = -2 (depth 1), -3 (depth 2), etc. */
{
int depth = -(e->wear_pos) - 1;
if (depth <= 0 ||
depth >= (int)(sizeof(stack) / sizeof(stack[0])) ||
!stack[depth - 1]) {
obj_to_char(obj, mob);
continue;
}
obj_to_obj(obj, stack[depth - 1]);
if (obj_is_storage(obj) || GET_OBJ_TYPE(obj) == ITEM_FURNITURE) {
stack[depth] = obj;
for (i = depth + 1; i < (int)(sizeof(stack) / sizeof(stack[0])); i++)
stack[i] = NULL;
}
}
}
}
}
@ -2729,15 +2772,28 @@ struct char_data *read_mobile(mob_vnum nr, int type) /* and mob_rnum */
if (!mob->points.max_hit) {
mob->points.max_hit = dice(mob->points.hit, mob->points.mana) +
mob->points.move;
mob->points.stamina;
} else
mob->points.max_hit = rand_number(mob->points.hit, mob->points.mana);
{
int base_hit = 0;
int base_mana = 0;
int base_stamina = 0;
if (get_species_base_points(GET_SPECIES(mob), &base_hit, &base_mana, &base_stamina)) {
mob->points.max_hit += base_hit;
mob->points.max_mana += base_mana;
mob->points.max_stamina += base_stamina;
}
}
mob->points.hit = mob->points.max_hit;
mob->points.mana = mob->points.max_mana;
mob->points.move = mob->points.max_move;
mob->points.stamina = mob->points.max_stamina;
mob->player.time.birth = time(0);
if (mob->player.time.birth == 0)
mob->player.time.birth = time(0);
mob->player.time.played = 0;
mob->player.time.logon = time(0);
@ -2794,6 +2850,9 @@ struct obj_data *read_object(obj_vnum nr, int type) /* and obj_rnum */
copy_proto_script(&obj_proto[i], obj, OBJ_TRIGGER);
assign_triggers(obj, OBJ_TRIGGER);
if (GET_OBJ_TYPE(obj) == ITEM_MONEY)
update_money_obj(obj);
return (obj);
}
@ -3747,8 +3806,8 @@ void reset_char(struct char_data *ch)
if (GET_HIT(ch) <= 0)
GET_HIT(ch) = 1;
if (GET_MOVE(ch) <= 0)
GET_MOVE(ch) = 1;
if (GET_STAMINA(ch) <= 0)
GET_STAMINA(ch) = 1;
if (GET_MANA(ch) <= 0)
GET_MANA(ch) = 1;
@ -3764,6 +3823,7 @@ void clear_char(struct char_data *ch)
GET_PFILEPOS(ch) = -1;
GET_MOB_RNUM(ch) = NOBODY;
GET_CLASS(ch) = CLASS_UNDEFINED;
GET_SPECIES(ch) = SPECIES_UNDEFINED;
GET_WAS_IN(ch) = NOWHERE;
GET_POS(ch) = POS_STANDING;
ch->mob_specials.default_pos = POS_STANDING;
@ -3802,12 +3862,12 @@ void init_char(struct char_data *ch)
GET_EXP(ch) = 7000000;
/* The implementor never goes through do_start(). */
GET_MAX_HIT(ch) = 500;
GET_MAX_MANA(ch) = 100;
GET_MAX_MOVE(ch) = 82;
GET_MAX_HIT(ch) = 999;
GET_MAX_MANA(ch) = 999;
GET_MAX_STAMINA(ch) = 999;
GET_HIT(ch) = GET_MAX_HIT(ch);
GET_MANA(ch) = GET_MAX_MANA(ch);
GET_MOVE(ch) = GET_MAX_MOVE(ch);
GET_STAMINA(ch) = GET_MAX_STAMINA(ch);
}
ch->player.short_descr = NULL;
@ -3819,7 +3879,12 @@ void init_char(struct char_data *ch)
ch->player_specials->saved.completed_quests = NULL;
GET_QUEST(ch) = NOTHING;
ch->player.time.birth = time(0);
if (ch->player.time.birth == 0)
ch->player.time.birth = time(0);
if (GET_ROLEPLAY_AGE(ch) == 0)
GET_ROLEPLAY_AGE(ch) = MIN_CHAR_AGE;
if (GET_ROLEPLAY_AGE_YEAR(ch) == 0)
GET_ROLEPLAY_AGE_YEAR(ch) = time_info.year;
ch->player.time.logon = time(0);
ch->player.time.played = 0;
@ -3880,7 +3945,7 @@ void init_char(struct char_data *ch)
}
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPHP);
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPMANA);
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPMOVE);
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPSTAMINA);
}
/* returns the real number of the room with given virtual number */
@ -4128,7 +4193,6 @@ static void load_default_config( void )
/* This function is called only once, at boot-time. We assume config_info is
* empty. -Welcor */
/* Game play options. */
CONFIG_PK_ALLOWED = pk_allowed;
CONFIG_PT_ALLOWED = pt_allowed;
CONFIG_LEVEL_CAN_SHOUT = level_can_shout;
CONFIG_TUNNEL_SIZE = tunnel_size;
@ -4388,9 +4452,7 @@ void load_config( void )
break;
case 'p':
if (!str_cmp(tag, "pk_allowed"))
CONFIG_PK_ALLOWED = num;
else if (!str_cmp(tag, "protocol_negotiation"))
if (!str_cmp(tag, "protocol_negotiation"))
CONFIG_PROTOCOL_NEGOTIATION = num;
else if (!str_cmp(tag, "pt_allowed"))
CONFIG_PT_ALLOWED = num;

View file

@ -801,30 +801,6 @@ void find_replacement(void *go, struct script_data *sc, trig_data *trig,
}
}
}
else if (!str_cmp(field, "is_killer")) {
if (subfield && *subfield) {
if (!str_cmp("on", subfield))
SET_BIT_AR(PLR_FLAGS(c), PLR_KILLER);
else if (!str_cmp("off", subfield))
REMOVE_BIT_AR(PLR_FLAGS(c), PLR_KILLER);
}
if (PLR_FLAGGED(c, PLR_KILLER))
strcpy(str, "1");
else
strcpy(str, "0");
}
else if (!str_cmp(field, "is_thief")) {
if (subfield && *subfield) {
if (!str_cmp("on", subfield))
SET_BIT_AR(PLR_FLAGS(c), PLR_THIEF);
else if (!str_cmp("off", subfield))
REMOVE_BIT_AR(PLR_FLAGS(c), PLR_THIEF);
}
if (PLR_FLAGGED(c, PLR_THIEF))
strcpy(str, "1");
else
strcpy(str, "0");
}
break;
case 'l':
if (!str_cmp(field, "level")) {
@ -867,19 +843,19 @@ void find_replacement(void *go, struct script_data *sc, trig_data *trig,
}
snprintf(str, slen, "%d", GET_MAX_MANA(c));
}
else if (!str_cmp(field, "maxmove")) {
else if (!str_cmp(field, "maxmove") || !str_cmp(field, "maxstamina")) {
if (subfield && *subfield) {
int addition = atoi(subfield);
GET_MAX_MOVE(c) = MAX(GET_MAX_MOVE(c) + addition, 1);
GET_MAX_STAMINA(c) = MAX(GET_MAX_STAMINA(c) + addition, 1);
}
snprintf(str, slen, "%d", GET_MAX_MOVE(c));
snprintf(str, slen, "%d", GET_MAX_STAMINA(c));
}
else if (!str_cmp(field, "move")) {
else if (!str_cmp(field, "move") || !str_cmp(field, "stamina")) {
if (subfield && *subfield) {
int addition = atoi(subfield);
GET_MOVE(c) += addition;
GET_STAMINA(c) += addition;
}
snprintf(str, slen, "%d", GET_MOVE(c));
snprintf(str, slen, "%d", GET_STAMINA(c));
}
break;
case 'n':

View file

@ -57,7 +57,6 @@ static struct char_data *next_combat_list = NULL;
static void perform_group_gain(struct char_data *ch, int base, struct char_data *victim);
static void dam_message(int dam, struct char_data *ch, struct char_data *victim, int w_type);
static void make_corpse(struct char_data *ch);
static void change_alignment(struct char_data *ch, struct char_data *victim);
static void group_gain(struct char_data *ch, struct char_data *victim);
static void solo_gain(struct char_data *ch, struct char_data *victim);
/** @todo refactor this function name */
@ -72,8 +71,8 @@ static int roll_damage(struct char_data *ch, struct char_data *victim,
int dam = 0;
if (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON) {
int ndice = GET_OBJ_VAL(wielded, 1); /* #dice */
int sdice = GET_OBJ_VAL(wielded, 2); /* sides */
int ndice = GET_OBJ_VAL(wielded, 0); /* #dice */
int sdice = GET_OBJ_VAL(wielded, 1); /* sides */
dam = dice(ndice, sdice);
dam += GET_ABILITY_MOD(GET_STR(ch)); /* STR adds to weapon damage */
} else {
@ -92,7 +91,7 @@ static int weapon_family_skill_num(struct char_data *ch, struct obj_data *wielde
if (!wielded || GET_OBJ_TYPE(wielded) != ITEM_WEAPON)
return SKILL_UNARMED;
/* NOTE: w_type here is TYPE_HIT + GET_OBJ_VAL(wielded, 3) or mob attack type + TYPE_HIT.
/* NOTE: w_type here is TYPE_HIT + GET_OBJ_VAL(wielded, 2) or mob attack type + TYPE_HIT.
Adjust the cases below to match your game's TYPE_* values. */
switch (w_type) {
/* --- Piercing family --- */
@ -176,20 +175,6 @@ void update_pos(struct char_data *victim)
GET_POS(victim) = POS_STUNNED;
}
void check_killer(struct char_data *ch, struct char_data *vict)
{
if (PLR_FLAGGED(vict, PLR_KILLER) || PLR_FLAGGED(vict, PLR_THIEF))
return;
if (PLR_FLAGGED(ch, PLR_KILLER) || IS_NPC(ch) || IS_NPC(vict) || ch == vict)
return;
SET_BIT_AR(PLR_FLAGS(ch), PLR_KILLER);
send_to_char(ch, "If you want to be a PLAYER KILLER, so be it...\r\n");
mudlog(BRF, MAX(LVL_IMMORT, MAX(GET_INVIS_LEV(ch), GET_INVIS_LEV(vict))),
TRUE, "PC Killer bit set on %s for initiating attack on %s at %s.",
GET_NAME(ch), GET_NAME(vict), world[IN_ROOM(vict)].name);
}
/* start one char fighting another (yes, it is horrible, I know... ) */
void set_fighting(struct char_data *ch, struct char_data *vict)
{
@ -210,8 +195,6 @@ void set_fighting(struct char_data *ch, struct char_data *vict)
FIGHTING(ch) = vict;
GET_POS(ch) = POS_FIGHTING;
if (!CONFIG_PK_ALLOWED)
check_killer(ch, vict);
}
/* remove a char from the list of fighting chars */
@ -297,14 +280,6 @@ static void make_corpse(struct char_data *ch)
obj_to_room(corpse, IN_ROOM(ch));
}
/* When ch kills victim */
static void change_alignment(struct char_data *ch, struct char_data *victim)
{
/* new alignment change algorithm: if you kill a monster with alignment A,
* you move 1/16th of the way to having alignment -A. Simple and fast. */
GET_ALIGNMENT(ch) += (-GET_ALIGNMENT(victim) - GET_ALIGNMENT(ch)) / 16;
}
void death_cry(struct char_data *ch)
{
int door;
@ -363,8 +338,6 @@ struct char_data *i;
void die(struct char_data * ch, struct char_data * killer)
{
if (!IS_NPC(ch)) {
REMOVE_BIT_AR(PLR_FLAGS(ch), PLR_KILLER);
REMOVE_BIT_AR(PLR_FLAGS(ch), PLR_THIEF);
}
raw_kill(ch, killer);
}
@ -372,11 +345,9 @@ void die(struct char_data * ch, struct char_data * killer)
static void perform_group_gain(struct char_data *ch, int base,
struct char_data *victim)
{
int share;
share = MIN(CONFIG_MAX_EXP_GAIN, MAX(1, base));
change_alignment(ch, victim);
(void)ch;
(void)base;
(void)victim;
}
static void group_gain(struct char_data *ch, struct char_data *victim)
@ -418,8 +389,6 @@ static void solo_gain(struct char_data *ch, struct char_data *victim)
exp += MAX(0, (exp * MIN(8, (GET_LEVEL(victim) - GET_LEVEL(ch)))) / 8);
exp = MAX(exp, 1);
change_alignment(ch, victim);
}
static char *replace_string(const char *str, const char *weapon_singular, const char *weapon_plural)
@ -634,6 +603,7 @@ int damage(struct char_data *ch, struct char_data *victim, int dam, int attackty
char local_buf[256];
struct char_data *tmp_char;
struct obj_data *corpse_obj;
int prev_hit = 0;
if (GET_POS(victim) <= POS_DEAD) {
/* This is "normal"-ish now with delayed extraction. -gg 3/15/2001 */
@ -659,8 +629,8 @@ int damage(struct char_data *ch, struct char_data *victim, int dam, int attackty
return (0);
}
/* You can't damage an immortal! */
if (!IS_NPC(victim) && ((GET_LEVEL(victim) >= LVL_IMMORT) && PRF_FLAGGED(victim, PRF_NOHASSLE)))
/* Immortals cannot be damaged. */
if (!IS_NPC(victim) && GET_REAL_LEVEL(victim) >= LVL_IMMORT)
dam = 0;
dam = damage_mtrigger(ch, victim, dam, attacktype);
@ -694,14 +664,9 @@ int damage(struct char_data *ch, struct char_data *victim, int dam, int attackty
dam /= 2;
/* Check for PK if this is not a PK MUD */
if (!CONFIG_PK_ALLOWED) {
check_killer(ch, victim);
if (PLR_FLAGGED(ch, PLR_KILLER) && (ch != victim))
dam = 0;
}
/* Set the maximum damage per round and subtract the hit points */
dam = MAX(MIN(dam, 100), 0);
prev_hit = GET_HIT(victim);
GET_HIT(victim) -= dam;
update_pos(victim);
@ -826,16 +791,30 @@ int damage(struct char_data *ch, struct char_data *victim, int dam, int attackty
void hit(struct char_data *ch, struct char_data *victim, int type)
{
struct obj_data *wielded = GET_EQ(ch, WEAR_WIELD);
bool wielded_weapon = (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON);
struct obj_data *shield = GET_EQ(victim, WEAR_SHIELD);
int w_type, d20, attack_mod = 0, target_ac, dam = 0;
bool hit_success = FALSE;
bool attacker_immortal, victim_immortal;
bool attacker_is_player, victim_is_player;
int unarmed_die_size = 0;
int unarmed_prof_bonus = 0;
int unarmed_str_mod = 0;
/* Basic sanity */
if (!ch || !victim) return;
attacker_is_player = (ch->desc != NULL);
victim_is_player = (victim->desc != NULL);
attacker_immortal = (attacker_is_player &&
(GET_REAL_LEVEL(ch) >= LVL_IMMORT || PRF_FLAGGED(ch, PRF_NOHASSLE)));
victim_immortal = (victim_is_player &&
(GET_REAL_LEVEL(victim) >= LVL_IMMORT || PRF_FLAGGED(victim, PRF_NOHASSLE)));
/* Determine attack message type exactly like stock code */
if (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON)
w_type = GET_OBJ_VAL(wielded, 3) + TYPE_HIT;
if (wielded_weapon)
w_type = GET_OBJ_VAL(wielded, 2) + TYPE_HIT;
else {
if (IS_NPC(ch) && ch->mob_specials.attack_type != 0)
w_type = ch->mob_specials.attack_type + TYPE_HIT;
@ -843,6 +822,39 @@ void hit(struct char_data *ch, struct char_data *victim, int type)
w_type = TYPE_HIT;
} /* matches stock message mapping */
if (victim_immortal) {
damage(ch, victim, 0, w_type);
return;
}
if (attacker_immortal) {
if (wielded_weapon) {
int ndice = GET_OBJ_VAL(wielded, 0);
int sdice = GET_OBJ_VAL(wielded, 1);
dam = (ndice * sdice) + GET_ABILITY_MOD(GET_STR(ch));
} else {
int prof_bonus = (!IS_NPC(ch)) ? GET_PROFICIENCY(GET_SKILL(ch, SKILL_UNARMED)) : 0;
int str_mod = GET_ABILITY_MOD(GET_STR(ch));
int die_size;
switch (prof_bonus) {
case 0: die_size = 4; break; /* untrained */
case 1: die_size = 6; break; /* trained */
case 2: die_size = 8; break; /* expert */
default: die_size = 10; break; /* master or above */
}
if (IS_NPC(ch) && prof_bonus <= 0) {
prof_bonus = MIN(6, (GET_LEVEL(ch) / 4));
}
dam = die_size + str_mod + prof_bonus;
}
damage(ch, victim, dam, w_type);
return;
}
/* Roll d20 */
d20 = rand_number(1, 20);
@ -859,7 +871,7 @@ void hit(struct char_data *ch, struct char_data *victim, int type)
attack_mod += GET_PROFICIENCY(GET_SKILL(ch, skillnum));
/* --- UNARMED ATTACK HANDLING --- */
if (!wielded) {
if (!wielded_weapon) {
int prof_bonus = (!IS_NPC(ch)) ? GET_PROFICIENCY(GET_SKILL(ch, SKILL_UNARMED)) : 0;
int str_mod = GET_ABILITY_MOD(GET_STR(ch));
int die_size;
@ -871,6 +883,7 @@ void hit(struct char_data *ch, struct char_data *victim, int type)
case 2: die_size = 8; break; /* expert */
default: die_size = 10; break; /* master or above */
}
unarmed_die_size = die_size;
/* NPC fallback scaling */
if (IS_NPC(ch) && prof_bonus <= 0) {
@ -879,9 +892,11 @@ void hit(struct char_data *ch, struct char_data *victim, int type)
/* base damage roll for unarmed attacks */
dam = dice(1, die_size) + str_mod + prof_bonus;
unarmed_prof_bonus = prof_bonus;
unarmed_str_mod = str_mod;
/* mark attack type for damage() messaging */
w_type = SKILL_UNARMED + TYPE_HIT;
w_type = TYPE_HIT;
}
/* Weapon magic (cap +3) */
@ -901,8 +916,9 @@ void hit(struct char_data *ch, struct char_data *victim, int type)
/* Ascending AC target */
target_ac = compute_armor_class_asc(victim);
/* Nat 1/20, then normal resolution */
if (d20 == 1) hit_success = FALSE;
/* Nat 1/20, then normal resolution (immortals always hit). */
if (attacker_immortal) hit_success = TRUE;
else if (d20 == 1) hit_success = FALSE;
else if (d20 == 20) hit_success = TRUE;
else hit_success = ((d20 + attack_mod) >= target_ac);
@ -912,13 +928,22 @@ void hit(struct char_data *ch, struct char_data *victim, int type)
* If we are unarmed, dam was already rolled above.
* If wielding a weapon, roll normally.
*/
if (wielded)
if (wielded_weapon)
dam = roll_damage(ch, victim, wielded, w_type);
if (attacker_immortal) {
if (wielded_weapon) {
int ndice = GET_OBJ_VAL(wielded, 0);
int sdice = GET_OBJ_VAL(wielded, 1);
dam = (ndice * sdice) + GET_ABILITY_MOD(GET_STR(ch));
} else if (unarmed_die_size > 0) {
dam = unarmed_die_size + unarmed_str_mod + unarmed_prof_bonus;
}
}
/* --- SHIELD BLOCK CHECK ---
* Only happens if an attack actually lands.
*/
if (shield) {
if (shield && !attacker_immortal) {
int def_prof = (!IS_NPC(victim)) ? GET_PROFICIENCY(GET_SKILL(victim, SKILL_SHIELD_USE)) : 0;
int block_chance = def_prof * 10; /* 060% total chance to block an attack */

View file

@ -22,7 +22,6 @@ struct attack_hit_type {
/* Functions available in fight.c */
void appear(struct char_data *ch);
void check_killer(struct char_data *ch, struct char_data *vict);
int compute_armor_class(struct char_data *ch);
int damage(struct char_data *ch, struct char_data *victim, int dam, int attacktype);
void death_cry(struct char_data *ch);

View file

@ -359,6 +359,18 @@ int write_mobile_espec(mob_vnum mvnum, struct char_data *mob, FILE *fd)
fprintf(fd, "Class: %d\n", (int)GET_CLASS(mob));
count++;
}
if (HAS_VALID_SPECIES(mob)) {
fprintf(fd, "Species: %d\n", (int)GET_SPECIES(mob));
count++;
}
{
int age_years = GET_ROLEPLAY_AGE(mob);
if (age_years >= MIN_CHAR_AGE && age_years <= MAX_CHAR_AGE &&
age_years != MIN_CHAR_AGE) {
fprintf(fd, "Age: %d\n", age_years);
count++;
}
}
/* --- 5e-style saving throws --- */
if (GET_SAVE(mob, ABIL_STR) != 0) {
@ -396,33 +408,60 @@ int write_mobile_record(mob_vnum mvnum, struct char_data *mob, FILE *fd)
char ddesc[MAX_STRING_LENGTH];
char bdesc[MAX_STRING_LENGTH];
char buf[MAX_STRING_LENGTH];
int has_bdesc = 0;
ldesc[MAX_STRING_LENGTH - 1] = '\0';
ddesc[MAX_STRING_LENGTH - 1] = '\0';
bdesc[MAX_STRING_LENGTH - 1] = '\0';
strip_cr(strncpy(ldesc, GET_LDESC(mob), MAX_STRING_LENGTH - 1));
strip_cr(strncpy(ddesc, GET_DDESC(mob), MAX_STRING_LENGTH - 1));
if (GET_BDESC(mob))
if (GET_BDESC(mob)) {
strip_cr(strncpy(bdesc, GET_BDESC(mob), MAX_STRING_LENGTH - 1));
else
{
const char *p;
for (p = bdesc; *p; p++) {
if (*p != ' ' && *p != '\t' && *p != '\r' && *p != '\n') {
has_bdesc = 1;
break;
}
}
}
} else
bdesc[0] = '\0';
int n = snprintf(buf, MAX_STRING_LENGTH,
"#%d\n"
"%s%c\n"
"%s%c\n"
"%s%c\n"
"%s%c\n"
"%s%c\n"
"B\n"
"%s%c\n",
mvnum,
GET_NAME(mob), STRING_TERMINATOR,
GET_KEYWORDS(mob), STRING_TERMINATOR,
GET_SDESC(mob), STRING_TERMINATOR,
ldesc, STRING_TERMINATOR,
ddesc, STRING_TERMINATOR,
bdesc, STRING_TERMINATOR);
int n;
if (has_bdesc) {
n = snprintf(buf, MAX_STRING_LENGTH,
"#%d\n"
"%s%c\n"
"%s%c\n"
"%s%c\n"
"%s%c\n"
"%s%c\n"
"B\n"
"%s%c\n",
mvnum,
GET_NAME(mob), STRING_TERMINATOR,
GET_KEYWORDS(mob), STRING_TERMINATOR,
GET_SDESC(mob), STRING_TERMINATOR,
ldesc, STRING_TERMINATOR,
ddesc, STRING_TERMINATOR,
bdesc, STRING_TERMINATOR);
} else {
n = snprintf(buf, MAX_STRING_LENGTH,
"#%d\n"
"%s%c\n"
"%s%c\n"
"%s%c\n"
"%s%c\n"
"%s%c\n",
mvnum,
GET_NAME(mob), STRING_TERMINATOR,
GET_KEYWORDS(mob), STRING_TERMINATOR,
GET_SDESC(mob), STRING_TERMINATOR,
ldesc, STRING_TERMINATOR,
ddesc, STRING_TERMINATOR);
}
if (n >= MAX_STRING_LENGTH) {
mudlog(BRF, LVL_BUILDER, TRUE,
@ -447,7 +486,7 @@ int write_mobile_record(mob_vnum mvnum, struct char_data *mob, FILE *fd)
GET_LEVEL(mob),
GET_HIT(mob),
GET_MANA(mob),
GET_MOVE(mob));
GET_STAMINA(mob));
/* --- Position / default position / sex --- */
fprintf(fd, "%d %d %d\n",

View file

@ -15,6 +15,7 @@
int delete_mobile(mob_rnum);
int copy_mobile(struct char_data *to, struct char_data *from);
int add_mobile(struct char_data *, mob_vnum);
void init_mobile(struct char_data *mob);
int copy_mob_strings(struct char_data *to, struct char_data *from);
int free_mob_strings(struct char_data *);
int free_mobile(struct char_data *mob);

View file

@ -629,7 +629,7 @@ static int export_mobile_record(mob_vnum mvnum, struct char_data *mob, FILE *fd)
AFF_FLAGS(mob)[2], AFF_FLAGS(mob)[3],
GET_ALIGNMENT(mob),
GET_LEVEL(mob), GET_HIT(mob),
GET_MANA(mob), GET_MOVE(mob));
GET_MANA(mob), GET_STAMINA(mob));
fprintf(fd, "%d %d %d\n",
GET_POS(mob), GET_DEFAULT_POS(mob), GET_SEX(mob)

View file

@ -463,6 +463,7 @@ int free_room_strings(struct room_data *room)
free(room->description);
if (room->ex_description)
free_ex_descriptions(room->ex_description);
room->ex_description = NULL;
if (room->forage)
free_forage_list(room->forage);
room->forage = NULL;

View file

@ -24,7 +24,6 @@
#include "fight.h"
#include "quest.h"
#include "mud_event.h"
#include "roomsave.h"
/* local file scope variables */
static int extractions_pending = 0;
@ -148,7 +147,7 @@ static void aff_apply_modify(struct char_data *ch, byte loc, sbyte mod, char *ms
case APPLY_CHAR_HEIGHT: GET_HEIGHT(ch) += mod; break;
case APPLY_MANA: GET_MAX_MANA(ch) += mod; break;
case APPLY_HIT: GET_MAX_HIT(ch) += mod; break;
case APPLY_MOVE: GET_MAX_MOVE(ch) += mod; break;
case APPLY_STAMINA: GET_MAX_STAMINA(ch) += mod; break;
case APPLY_COINS: break;
case APPLY_EXP: break;
@ -446,6 +445,8 @@ void obj_to_char(struct obj_data *object, struct char_data *ch)
RoomSave_mark_dirty_room(__rs_room);
IS_CARRYING_W(ch) += GET_OBJ_WEIGHT(object);
IS_CARRYING_N(ch)++;
if (AFF_FLAGGED(ch, AFF_MOUNTED) && MOUNT(ch) && RIDDEN_BY(MOUNT(ch)) == ch)
IS_CARRYING_W(MOUNT(ch)) += GET_OBJ_WEIGHT(object);
autoquest_trigger_check(ch, NULL, object, AQ_OBJ_FIND);
@ -487,6 +488,11 @@ void obj_from_char(struct obj_data *object)
IS_CARRYING_W(object->carried_by) -= GET_OBJ_WEIGHT(object);
IS_CARRYING_N(object->carried_by)--;
if (AFF_FLAGGED(object->carried_by, AFF_MOUNTED) &&
MOUNT(object->carried_by) &&
RIDDEN_BY(MOUNT(object->carried_by)) == object->carried_by)
IS_CARRYING_W(MOUNT(object->carried_by)) =
MAX(0, IS_CARRYING_W(MOUNT(object->carried_by)) - GET_OBJ_WEIGHT(object));
object->carried_by = NULL;
object->next_content = NULL;
if (__rs_room != NOWHERE)
@ -525,17 +531,6 @@ static int apply_ac(struct char_data *ch, int eq_pos)
return (factor * GET_OBJ_VAL(GET_EQ(ch, eq_pos), 0));
}
int invalid_align(struct char_data *ch, struct obj_data *obj)
{
if (OBJ_FLAGGED(obj, ITEM_ANTI_EVIL) && IS_EVIL(ch))
return TRUE;
if (OBJ_FLAGGED(obj, ITEM_ANTI_GOOD) && IS_GOOD(ch))
return TRUE;
if (OBJ_FLAGGED(obj, ITEM_ANTI_NEUTRAL) && IS_NEUTRAL(ch))
return TRUE;
return FALSE;
}
void equip_char(struct char_data *ch, struct obj_data *obj, int pos)
{
int j;
@ -558,7 +553,7 @@ void equip_char(struct char_data *ch, struct obj_data *obj, int pos)
log("SYSERR: EQUIP: Obj is in_room when equip.");
return;
}
if (invalid_align(ch, obj) || invalid_class(ch, obj)) {
if (invalid_class(ch, obj)) {
act("You are zapped by $p and instantly let go of it.", FALSE, ch, obj, 0, TO_CHAR);
act("$n is zapped by $p and instantly lets go of it.", FALSE, ch, obj, 0, TO_ROOM);
/* Changed to drop in inventory instead of the ground. */
@ -569,6 +564,9 @@ void equip_char(struct char_data *ch, struct obj_data *obj, int pos)
GET_EQ(ch, pos) = obj;
obj->worn_by = ch;
obj->worn_on = pos;
IS_CARRYING_W(ch) += GET_OBJ_WEIGHT(obj);
if (AFF_FLAGGED(ch, AFF_MOUNTED) && MOUNT(ch) && RIDDEN_BY(MOUNT(ch)) == ch)
IS_CARRYING_W(MOUNT(ch)) += GET_OBJ_WEIGHT(obj);
if (GET_OBJ_TYPE(obj) == ITEM_ARMOR)
GET_AC(ch) -= apply_ac(ch, pos);
@ -601,6 +599,10 @@ struct obj_data *unequip_char(struct char_data *ch, int pos)
obj = GET_EQ(ch, pos);
obj->worn_by = NULL;
obj->worn_on = -1;
IS_CARRYING_W(ch) -= GET_OBJ_WEIGHT(obj);
if (AFF_FLAGGED(ch, AFF_MOUNTED) && MOUNT(ch) && RIDDEN_BY(MOUNT(ch)) == ch)
IS_CARRYING_W(MOUNT(ch)) =
MAX(0, IS_CARRYING_W(MOUNT(ch)) - GET_OBJ_WEIGHT(obj));
if (GET_OBJ_TYPE(obj) == ITEM_ARMOR)
GET_AC(ch) += apply_ac(ch, pos);
@ -798,6 +800,8 @@ void obj_to_obj(struct obj_data *obj, struct obj_data *obj_to)
GET_OBJ_WEIGHT(tmp_obj) += GET_OBJ_WEIGHT(obj);
if (tmp_obj->carried_by)
IS_CARRYING_W(tmp_obj->carried_by) += GET_OBJ_WEIGHT(obj);
else if (tmp_obj->worn_by)
IS_CARRYING_W(tmp_obj->worn_by) += GET_OBJ_WEIGHT(obj);
}
}
@ -831,6 +835,8 @@ void obj_from_obj(struct obj_data *obj)
GET_OBJ_WEIGHT(temp) -= GET_OBJ_WEIGHT(obj);
if (temp->carried_by)
IS_CARRYING_W(temp->carried_by) -= GET_OBJ_WEIGHT(obj);
else if (temp->worn_by)
IS_CARRYING_W(temp->worn_by) -= GET_OBJ_WEIGHT(obj);
}
obj->in_obj = NULL;
obj->next_content = NULL;
@ -990,6 +996,13 @@ void extract_char_final(struct char_data *ch)
STATE(d) = CON_CLOSE;
}
if (GET_POS(ch) == POS_DEAD) {
int pfilepos = GET_PFILEPOS(ch);
if (pfilepos < 0)
pfilepos = get_ptable_by_name(GET_NAME(ch));
if (pfilepos >= 0)
SET_BIT(player_table[pfilepos].flags, PINDEX_DELETED);
STATE(ch->desc) = CON_ACCOUNT_MENU;
send_account_menu(ch->desc);
ch->desc->character = NULL;
@ -1001,6 +1014,25 @@ void extract_char_final(struct char_data *ch)
}
}
if (AFF_FLAGGED(ch, AFF_MOUNTED) || MOUNT(ch)) {
struct char_data *mount = MOUNT(ch);
if (mount && RIDDEN_BY(mount) == ch) {
int rider_weight = GET_WEIGHT(ch) + IS_CARRYING_W(ch);
IS_CARRYING_W(mount) = MAX(0, IS_CARRYING_W(mount) - rider_weight);
RIDDEN_BY(mount) = NULL;
}
MOUNT(ch) = NULL;
REMOVE_BIT_AR(AFF_FLAGS(ch), AFF_MOUNTED);
}
if (RIDDEN_BY(ch)) {
struct char_data *rider = RIDDEN_BY(ch);
if (rider && MOUNT(rider) == ch) {
MOUNT(rider) = NULL;
REMOVE_BIT_AR(AFF_FLAGS(rider), AFF_MOUNTED);
}
RIDDEN_BY(ch) = NULL;
}
/* On with the character's assets... */
if (ch->followers || ch->master)
die_follower(ch);
@ -1456,7 +1488,7 @@ int get_obj_pos_in_equip_vis(struct char_data *ch, char *arg, int *number, struc
static int money_weight(int amount)
{
const int coins_per_weight = 10;
const int coins_per_weight = 30;
if (amount <= 0)
return 0;

View file

@ -40,7 +40,6 @@ void obj_from_char(struct obj_data *object);
void equip_char(struct char_data *ch, struct obj_data *obj, int pos);
struct obj_data *unequip_char(struct char_data *ch, int pos);
int invalid_align(struct char_data *ch, struct obj_data *obj);
void obj_to_room(struct obj_data *object, room_rnum room);
void obj_from_room(struct obj_data *object);

View file

@ -27,6 +27,7 @@
#include "act.h" /* ACMDs located within the act*.c files */
#include "ban.h"
#include "class.h"
#include "species.h"
#include "graph.h"
#include "hedit.h"
#include "house.h"
@ -47,6 +48,11 @@ static int _parse_name(char *arg, char *name);
static bool perform_new_char_dupe_check(struct descriptor_data *d);
/* sort_commands utility */
static int sort_commands_helper(const void *a, const void *b);
static void show_species_menu(struct descriptor_data *d);
static bool is_creation_state(int state);
static void show_stat_pref_prompt(struct descriptor_data *d);
static int ability_from_pref_arg(const char *arg);
static bool parse_stat_preference(char *input, ubyte *order, ubyte *count);
/* globals defined here, used here and elsewhere */
int *cmd_sort_info = NULL;
@ -81,7 +87,7 @@ cpp_extern const struct command_info cmd_info[] = {
{ "sw" , "sw" , POS_STANDING, do_move , 0, SCMD_SW },
/* now, the main list */
{ "acaudit" , "acaudi" , POS_DEAD , do_acaudit , LVL_IMMORT, 0 },
{ "audit" , "aud" , POS_DEAD , do_audit , LVL_IMMORT, 0 },
{ "at" , "at" , POS_DEAD , do_at , LVL_IMMORT, 0 },
{ "advance" , "adv" , POS_DEAD , do_advance , LVL_GRGOD, 0 },
{ "aedit" , "aed" , POS_DEAD , do_oasis_aedit, LVL_GOD, 0 },
@ -112,6 +118,7 @@ cpp_extern const struct command_info cmd_info[] = {
{ "cast" , "c" , POS_SITTING , do_cast , 1, 0 },
{ "cedit" , "cedit" , POS_DEAD , do_oasis_cedit, LVL_IMPL, 0 },
{ "change" , "chang" , POS_SLEEPING , do_change , 0, 0 },
{ "changelog", "cha" , POS_DEAD , do_changelog, LVL_IMPL, 0 },
{ "check" , "ch" , POS_STANDING, do_not_here , 1, 0 },
{ "checkload", "checkl" , POS_DEAD , do_checkloadstatus, LVL_GOD, 0 },
@ -130,6 +137,7 @@ cpp_extern const struct command_info cmd_info[] = {
{ "detach" , "detach" , POS_DEAD , do_detach , LVL_BUILDER, 0 },
{ "diagnose" , "diag" , POS_RESTING , do_diagnose , 0, 0 },
{ "dig" , "dig" , POS_DEAD , do_dig , LVL_BUILDER, 0 },
{ "dismount" , "dism" , POS_STANDING, do_dismount , 0, 0 },
{ "display" , "disp" , POS_DEAD , do_display , 0, 0 },
{ "drink" , "dri" , POS_RESTING , do_drink , 0, SCMD_DRINK },
{ "drop" , "dro" , POS_RESTING , do_drop , 0, SCMD_DROP },
@ -173,6 +181,7 @@ cpp_extern const struct command_info cmd_info[] = {
{ "hold" , "hold" , POS_RESTING , do_grab , 1, 0 },
{ "holylight", "holy" , POS_DEAD , do_gen_tog , LVL_IMMORT, SCMD_HOLYLIGHT },
{ "house" , "house" , POS_RESTING , do_house , 0, 0 },
{ "hitch" , "hitc" , POS_STANDING, do_hitch , 0, 0 },
{ "inventory", "i" , POS_DEAD , do_inventory, 0, 0 },
{ "idea" , "ide" , POS_DEAD , do_ibt , 0, SCMD_IDEA },
@ -191,6 +200,7 @@ cpp_extern const struct command_info cmd_info[] = {
{ "last" , "last" , POS_DEAD , do_last , LVL_GOD, 0 },
{ "leave" , "lea" , POS_STANDING, do_leave , 0, 0 },
{ "list" , "lis" , POS_STANDING, do_not_here , 0, 0 },
{ "mount" , "mou" , POS_STANDING, do_mount , 0, 0 },
{ "listen" , "lisn" , POS_RESTING , do_listen , 0, 0 },
{ "links" , "lin" , POS_STANDING, do_links , LVL_GOD, 0 },
{ "lock" , "loc" , POS_SITTING , do_gen_door , 0, SCMD_LOCK },
@ -202,6 +212,7 @@ cpp_extern const struct command_info cmd_info[] = {
{ "medit" , "med" , POS_DEAD , do_oasis_medit, LVL_BUILDER, 0 },
{ "mlist" , "mlist" , POS_DEAD , do_oasis_list, LVL_BUILDER, SCMD_OASIS_MLIST },
{ "mcopy" , "mcopy" , POS_DEAD , do_oasis_copy, LVL_GOD, CON_MEDIT },
{ "mcreate" , "mcreate" , POS_DEAD , do_mcreate , LVL_BUILDER, 0 },
{ "msave" , "msav" , POS_DEAD , do_msave, LVL_BUILDER, 0 },
{ "msgedit" , "msgedit" , POS_DEAD , do_msgedit, LVL_GOD, 0 },
{ "mute" , "mute" , POS_DEAD , do_wizutil , LVL_GOD, SCMD_MUTE },
@ -219,11 +230,14 @@ cpp_extern const struct command_info cmd_info[] = {
{ "olc" , "olc" , POS_DEAD , do_show_save_list, LVL_BUILDER, 0 },
{ "olist" , "olist" , POS_DEAD , do_oasis_list, LVL_BUILDER, SCMD_OASIS_OLIST },
{ "oedit" , "oedit" , POS_DEAD , do_oasis_oedit, LVL_BUILDER, 0 },
{ "ocreate" , "ocreate" , POS_DEAD , do_ocreate , LVL_BUILDER, 0 },
{ "ooc" , "oo" , POS_RESTING , do_ooc , 0, 0 },
{ "osave" , "osave" , POS_DEAD , do_osave , LVL_BUILDER, 0 },
{ "oset" , "oset" , POS_DEAD , do_oset, LVL_BUILDER, 0 },
{ "ocopy" , "ocopy" , POS_DEAD , do_oasis_copy, LVL_GOD, CON_OEDIT },
{ "put" , "p" , POS_RESTING , do_put , 0, 0 },
{ "pack" , "pac" , POS_RESTING , do_pack , 0, 0 },
{ "peace" , "pe" , POS_DEAD , do_peace , LVL_BUILDER, 0 },
{ "pemote" , "pem" , POS_SLEEPING, do_pemote , 0, SCMD_PEMOTE },
{ "phemote" , "phem" , POS_SLEEPING, do_phemote , 0, SCMD_PHEMOTE },
@ -257,11 +271,13 @@ cpp_extern const struct command_info cmd_info[] = {
{ "recent" , "recent" , POS_DEAD , do_recent , LVL_IMMORT, 0 },
{ "remove" , "rem" , POS_RESTING , do_remove , 0, 0 },
{ "report" , "repo" , POS_RESTING , do_report , 0, 0 },
{ "reroll" , "rero" , POS_DEAD , do_wizutil , LVL_GRGOD, SCMD_REROLL },
{ "reroll" , "rero" , POS_DEAD , do_reroll , 0, 0 },
{ "rescue" , "resc" , POS_FIGHTING, do_rescue , 1, 0 },
{ "restore" , "resto" , POS_DEAD , do_restore , LVL_GOD, 0 },
{ "return" , "retu" , POS_DEAD , do_return , 0, 0 },
{ "redit" , "redit" , POS_DEAD , do_oasis_redit, LVL_BUILDER, 0 },
{ "rcreate" , "rcreate" , POS_DEAD , do_rcreate , LVL_BUILDER, 0 },
{ "rset" , "rset" , POS_DEAD , do_rset , LVL_BUILDER, 0 },
{ "rlist" , "rlist" , POS_DEAD , do_oasis_list, LVL_BUILDER, SCMD_OASIS_RLIST },
{ "rcopy" , "rcopy" , POS_DEAD , do_oasis_copy, LVL_GOD, CON_REDIT },
{ "roomflags", "roomflags", POS_DEAD , do_gen_tog , LVL_IMMORT, SCMD_SHOWVNUMS },
@ -320,7 +336,9 @@ cpp_extern const struct command_info cmd_info[] = {
{ "unlock" , "unlock" , POS_SITTING , do_gen_door , 0, SCMD_UNLOCK },
{ "unban" , "unban" , POS_DEAD , do_unban , LVL_GRGOD, 0 },
{ "unaffect" , "unaffect", POS_DEAD , do_wizutil , LVL_GOD, SCMD_UNAFFECT },
{ "unhitch" , "unh" , POS_STANDING, do_unhitch , 0, 0 },
{ "unfollow" , "unf" , POS_RESTING , do_unfollow , 0, 0 },
{ "unpack" , "unpa" , POS_RESTING , do_unpack , 0, 0 },
{ "uptime" , "uptime" , POS_DEAD , do_date , LVL_GOD, SCMD_UPTIME },
{ "use" , "use" , POS_SITTING , do_use , 1, SCMD_USE },
{ "users" , "users" , POS_DEAD , do_users , LVL_GOD, 0 },
@ -477,8 +495,6 @@ void command_interpreter(struct char_data *ch, char *argument)
char *line;
char arg[MAX_INPUT_LENGTH];
REMOVE_BIT_AR(AFF_FLAGS(ch), AFF_HIDE);
/* just drop to next line for hitting CR */
skip_spaces(&argument);
if (!*argument)
@ -494,6 +510,9 @@ void command_interpreter(struct char_data *ch, char *argument)
} else
line = any_one_arg(argument, arg);
if (!is_abbrev(arg, "change"))
REMOVE_BIT_AR(AFF_FLAGS(ch), AFF_HIDE);
/* Since all command triggers check for valid_dg_target before acting, the levelcheck
* here has been removed. Otherwise, find the command. */
{
@ -1268,7 +1287,7 @@ static bool perform_new_char_dupe_check(struct descriptor_data *d)
/* Do the player names match? */
if (!strcmp(GET_NAME(k->character), GET_NAME(d->character))) {
/* Check the other character is still in creation? */
if ((STATE(k) > CON_PLAYING) && (STATE(k) < CON_QCLASS)) {
if (is_creation_state(STATE(k))) {
/* Boot the older one */
k->character->desc = NULL;
k->character = NULL;
@ -1303,6 +1322,125 @@ static bool perform_new_char_dupe_check(struct descriptor_data *d)
return (found);
}
static void show_species_menu(struct descriptor_data *d)
{
int count = pc_species_count();
write_to_output(d, "Select a species:\r\n");
for (int i = 0; i < count; i++) {
int species = pc_species_list[i];
write_to_output(d, " %2d) %s\r\n", i + 1, species_types[species]);
}
write_to_output(d, "Species: ");
}
static void show_age_prompt(struct descriptor_data *d)
{
write_to_output(d, "Age (%d-%d): ", MIN_CHAR_AGE, MAX_CHAR_AGE);
}
static bool is_creation_state(int state)
{
switch (state) {
case CON_GET_NAME:
case CON_NAME_CNFRM:
case CON_PASSWORD:
case CON_NEWPASSWD:
case CON_CNFPASSWD:
case CON_QSEX:
case CON_QSPECIES:
case CON_QCLASS:
case CON_QAGE:
case CON_QSTAT_PREF:
case CON_QSHORTDESC:
case CON_PLR_DESC:
case CON_PLR_BACKGROUND:
return TRUE;
default:
return FALSE;
}
}
static void show_stat_pref_prompt(struct descriptor_data *d)
{
write_to_output(d,
"\r\nEnter your stat preference, with the first stat being your preferred highest,\r\n"
"followed by the others in descending order.\r\n"
"If you list fewer than six, those listed get the highest rolls; the rest are FIFO.\r\n"
"Example: strength dexterity constitution intelligence wisdom charisma\r\n"
" or: str dex con int wis cha\r\n"
"Press Enter to skip (first-in, first-out).\r\n"
"Stat preference: ");
}
static int ability_from_pref_arg(const char *arg)
{
if (!arg || !*arg)
return -1;
if (!str_cmp(arg, "str") || is_abbrev(arg, "strength"))
return ABIL_STR;
if (!str_cmp(arg, "dex") || is_abbrev(arg, "dexterity"))
return ABIL_DEX;
if (!str_cmp(arg, "con") || is_abbrev(arg, "constitution"))
return ABIL_CON;
if (!str_cmp(arg, "int") || is_abbrev(arg, "intelligence"))
return ABIL_INT;
if (!str_cmp(arg, "wis") || is_abbrev(arg, "wisdom"))
return ABIL_WIS;
if (!str_cmp(arg, "cha") || is_abbrev(arg, "charisma"))
return ABIL_CHA;
return -1;
}
static bool parse_stat_preference(char *input, ubyte *order, ubyte *count)
{
char arg[MAX_INPUT_LENGTH];
bool seen[NUM_ABILITIES] = { FALSE };
if (!order || !count)
return FALSE;
*count = 0;
skip_spaces(&input);
if (!*input)
return TRUE;
if (!str_cmp(input, "none") || !str_cmp(input, "no") || !str_cmp(input, "skip"))
return TRUE;
while (*input) {
size_t len;
int ability;
input = one_argument(input, arg);
if (!*arg)
break;
len = strlen(arg);
while (len > 0 && (arg[len - 1] == ',' || arg[len - 1] == '.')) {
arg[len - 1] = '\0';
len--;
}
if (!*arg)
continue;
ability = ability_from_pref_arg(arg);
if (ability < 0 || ability >= NUM_ABILITIES)
return FALSE;
if (seen[ability])
return FALSE;
if (*count >= NUM_ABILITIES)
return FALSE;
order[*count] = (ubyte)ability;
(*count)++;
seen[ability] = TRUE;
}
return TRUE;
}
/* load the player, put them in the right room - used by copyover_recover too */
int enter_player_game (struct descriptor_data *d)
{
@ -1869,10 +2007,26 @@ void nanny(struct descriptor_data *d, char *arg)
return;
}
write_to_output(d, "%s\r\nClass: ", class_menu);
STATE(d) = CON_QCLASS;
show_species_menu(d);
STATE(d) = CON_QSPECIES;
break;
case CON_QSPECIES: {
int choice = atoi(arg);
int species = species_from_pc_choice(choice);
if (species == SPECIES_UNDEFINED) {
write_to_output(d, "\r\nThat's not a species.\r\n");
show_species_menu(d);
return;
}
GET_SPECIES(d->character) = species;
write_to_output(d, "%s\r\nClass: ", class_menu);
STATE(d) = CON_QCLASS;
break;
}
case CON_QCLASS:
load_result = parse_class(*arg);
if (load_result == CLASS_UNDEFINED) {
@ -1882,6 +2036,51 @@ case CON_QCLASS:
GET_CLASS(d->character) = load_result;
}
show_age_prompt(d);
STATE(d) = CON_QAGE;
return;
case CON_QAGE: {
if (!is_number(arg)) {
write_to_output(d, "\r\nPlease enter a number between %d and %d.\r\n",
MIN_CHAR_AGE, MAX_CHAR_AGE);
show_age_prompt(d);
return;
}
int age_years = atoi(arg);
if (age_years < MIN_CHAR_AGE || age_years > MAX_CHAR_AGE) {
write_to_output(d, "\r\nAge must be between %d and %d.\r\n",
MIN_CHAR_AGE, MAX_CHAR_AGE);
show_age_prompt(d);
return;
}
GET_ROLEPLAY_AGE(d->character) = age_years;
GET_ROLEPLAY_AGE_YEAR(d->character) = time_info.year;
show_stat_pref_prompt(d);
STATE(d) = CON_QSTAT_PREF;
return;
}
case CON_QSTAT_PREF: {
ubyte order[NUM_ABILITIES];
ubyte count = 0;
if (!parse_stat_preference(arg, order, &count)) {
write_to_output(d,
"\r\nInvalid stat list. Please enter a valid order, or press Enter to skip.\r\n");
show_stat_pref_prompt(d);
return;
}
d->character->stat_pref_use = TRUE;
d->character->stat_pref_count = count;
for (int i = 0; i < NUM_ABILITIES; i++) {
d->character->stat_pref_order[i] = (i < count) ? order[i] : 0;
}
/* Create player entry and initialize character now so file exists */
if (d->olc) {
free(d->olc);
@ -1928,6 +2127,7 @@ case CON_QCLASS:
STATE(d) = CON_QSHORTDESC;
return;
}
case CON_QSHORTDESC: {
skip_spaces(&arg);

View file

@ -23,7 +23,7 @@
#include "fight.h"
#include "screen.h"
#include "mud_event.h"
#include "roomsave.h"
#include "set.h"
#include <time.h>
/* local file scope function prototypes */
@ -142,7 +142,7 @@ int hit_gain(struct char_data *ch)
return (gain);
}
/* move gain pr. game hour */
/* stamina gain pr. game hour */
int move_gain(struct char_data *ch)
{
int gain;
@ -209,12 +209,13 @@ void run_autowiz(void)
/* Requires: find_skill_num(), GET_WIS(), wis_app[], GET_SKILL(), SET_SKILL(), rand_number()
* Cooldown: 1 hour - 5 * WIS bonus minutes (floored at 0)
* Rolls: failure -> 1..20, success -> 1..100
* Cap: 90% (change MIN(90, ...) if you want a different cap)
* Cap: per-class max (see class.c)
*/
void gain_skill(struct char_data *ch, char *skill, bool success)
{
int skill_num, base, roll, increase;
int wisb, cd_seconds;
int cap;
time_t now;
if (IS_NPC(ch))
@ -233,8 +234,11 @@ void gain_skill(struct char_data *ch, char *skill, bool success)
}
base = GET_SKILL(ch, skill_num);
cap = class_skill_max(GET_CLASS(ch), skill_num);
if (cap <= 0)
return;
/* If already capped, bail early (and dont start cooldown) */
if (base >= 90)
if (base >= cap)
return;
/* Wisdom bonus from wis_app[] (constants.c). Higher = better learning & shorter cooldown. */
@ -246,7 +250,7 @@ void gain_skill(struct char_data *ch, char *skill, bool success)
/* Old 1..400 with (400 - wisb*4) ⇒ scaled: (100 - wisb) */
if (roll >= (100 - wisb)) {
increase = base + 1;
SET_SKILL(ch, skill_num, MIN(90, increase));
SET_SKILL(ch, skill_num, MIN(cap, increase));
/* Cooldown only when an increase actually happens */
cd_seconds = 3600 - (wisb * 5 * 60); /* 1 hour - 5 * WIS minutes */
@ -259,7 +263,7 @@ void gain_skill(struct char_data *ch, char *skill, bool success)
/* Old 1..100 with (100 - wisb) ⇒ scaled: (20 - wisb) */
if (roll >= (20 - wisb)) {
increase = base + 1;
SET_SKILL(ch, skill_num, MIN(90, increase));
SET_SKILL(ch, skill_num, MIN(cap, increase));
/* Cooldown only when an increase actually happens */
cd_seconds = 3600 - (wisb * 5 * 60); /* 1 hour - 5 * WIS minutes */
@ -413,7 +417,7 @@ void point_update(void)
if (GET_POS(i) >= POS_STUNNED) {
GET_HIT(i) = MIN(GET_HIT(i) + hit_gain(i), GET_MAX_HIT(i));
GET_MANA(i) = MIN(GET_MANA(i) + mana_gain(i), GET_MAX_MANA(i));
GET_MOVE(i) = MIN(GET_MOVE(i) + move_gain(i), GET_MAX_MOVE(i));
GET_STAMINA(i) = MIN(GET_STAMINA(i) + move_gain(i), GET_MAX_STAMINA(i));
if (AFF_FLAGGED(i, AFF_POISON))
if (damage(i, i, 2, SPELL_POISON) == -1)
continue; /* Oops, they died. -gg 6/24/98 */
@ -480,8 +484,8 @@ void point_update(void)
}
/* ---- Room SAVE autosave (every 10 minutes; adjust the 600 as desired) ----
* Requires: #include "roomsave.h" at the top of this file.
* Saves all rooms flagged ROOM_SAVE via roomsave.c.
* Requires: #include "set.h" at the top of this file.
* Saves all rooms flagged ROOM_SAVE via set.c.
*/
if (++roomsave_pulse >= (PASSES_PER_SEC * 600)) {
roomsave_pulse = 0;

View file

@ -189,6 +189,7 @@ int mag_damage(int level, struct char_data *ch, struct char_data *victim,
int spellnum, int savetype)
{
int dam = 0;
int save_dc;
if (victim == NULL || ch == NULL)
return (0);
@ -234,28 +235,6 @@ int mag_damage(int level, struct char_data *ch, struct char_data *victim,
break;
/* Mostly clerics */
case SPELL_DISPEL_EVIL:
dam = dice(6, 8) + 6;
if (IS_EVIL(ch)) {
victim = ch;
dam = GET_HIT(ch) - 1;
} else if (IS_GOOD(victim)) {
act("The gods protect $N.", FALSE, ch, 0, victim, TO_CHAR);
return (0);
}
break;
case SPELL_DISPEL_GOOD:
dam = dice(6, 8) + 6;
if (IS_GOOD(ch)) {
victim = ch;
dam = GET_HIT(ch) - 1;
} else if (IS_EVIL(victim)) {
act("The gods protect $N.", FALSE, ch, 0, victim, TO_CHAR);
return (0);
}
break;
case SPELL_CALL_LIGHTNING:
dam = dice(7, 8) + 7;
break;
@ -279,8 +258,12 @@ int mag_damage(int level, struct char_data *ch, struct char_data *victim,
} /* switch(spellnum) */
save_dc = compute_save_dc(ch, level, spellnum);
if (!IS_NPC(ch) && GET_REAL_LEVEL(ch) >= LVL_IMMORT)
save_dc = 1000;
/* divide damage by two if victim makes his saving throw */
if (mag_savingthrow(victim, savetype, compute_save_dc(ch, level, spellnum)))
if (mag_savingthrow(victim, savetype, save_dc))
dam /= 2;
/* and finally, inflict the damage */
@ -307,6 +290,8 @@ void mag_affects(int level, struct char_data *ch, struct char_data *victim,
return;
save_dc = compute_save_dc(ch, level, spellnum);
if (!IS_NPC(ch) && GET_REAL_LEVEL(ch) >= LVL_IMMORT)
save_dc = 1000;
for (i = 0; i < MAX_SPELL_AFFECTS; i++) {
new_affect(&(af[i]));
@ -391,13 +376,6 @@ void mag_affects(int level, struct char_data *ch, struct char_data *victim,
to_vict = "You feel very uncomfortable.";
break;
case SPELL_DETECT_ALIGN:
af[0].duration = 12 + level;
SET_BIT_AR(af[0].bitvector, AFF_DETECT_ALIGN);
accum_duration = TRUE;
to_vict = "Your eyes tingle.";
break;
case SPELL_DETECT_INVIS:
af[0].duration = 12 + level;
SET_BIT_AR(af[0].bitvector, AFF_DETECT_INVIS);
@ -454,13 +432,6 @@ void mag_affects(int level, struct char_data *ch, struct char_data *victim,
to_room = "$n gets violently ill!";
break;
case SPELL_PROT_FROM_EVIL:
af[0].duration = 24;
SET_BIT_AR(af[0].bitvector, AFF_PROTECT_EVIL);
accum_duration = TRUE;
to_vict = "You feel invulnerable!";
break;
case SPELL_SANCTUARY:
af[0].duration = 4;
SET_BIT_AR(af[0].bitvector, AFF_SANCTUARY);
@ -470,8 +441,6 @@ void mag_affects(int level, struct char_data *ch, struct char_data *victim,
break;
case SPELL_SLEEP:
if (!CONFIG_PK_ALLOWED && !IS_NPC(ch) && !IS_NPC(victim))
return;
if (MOB_FLAGGED(victim, MOB_NOSLEEP))
return;
if (mag_savingthrow(victim, ABIL_WIS, save_dc))
@ -645,8 +614,6 @@ void mag_areas(int level, struct char_data *ch, int spellnum, int savetype)
continue;
if (!IS_NPC(tch) && GET_LEVEL(tch) >= LVL_IMMORT)
continue;
if (!CONFIG_PK_ALLOWED && !IS_NPC(ch) && !IS_NPC(tch))
continue;
if (!IS_NPC(ch) && IS_NPC(tch) && AFF_FLAGGED(tch, AFF_CHARM))
continue;
if (!IS_NPC(tch) && spell_info[spellnum].violent && GROUP(tch) && GROUP(ch) && GROUP(ch) == GROUP(tch))
@ -816,7 +783,7 @@ void mag_points(int level, struct char_data *ch, struct char_data *victim,
break;
}
GET_HIT(victim) = MIN(GET_MAX_HIT(victim), GET_HIT(victim) + healing);
GET_MOVE(victim) = MIN(GET_MAX_MOVE(victim), GET_MOVE(victim) + move);
GET_STAMINA(victim) = MIN(GET_MAX_STAMINA(victim), GET_STAMINA(victim) + move);
update_pos(victim);
}

View file

@ -13,6 +13,7 @@
#include "comm.h"
#include "spells.h"
#include "class.h"
#include "species.h"
#include "db.h"
#include "shop.h"
#include "genolc.h"
@ -30,7 +31,7 @@
/* local functions */
static void medit_setup_new(struct descriptor_data *d);
static void init_mobile(struct char_data *mob);
void init_mobile(struct char_data *mob);
static void medit_save_to_disk(zone_vnum zone_num);
static void medit_disp_positions(struct descriptor_data *d);
static void medit_disp_sex(struct descriptor_data *d);
@ -41,6 +42,9 @@ static void medit_disp_mob_flags(struct descriptor_data *d);
static void medit_disp_aff_flags(struct descriptor_data *d);
static void medit_disp_menu(struct descriptor_data *d);
static void medit_disp_class_menu(struct descriptor_data *d);
static void medit_disp_species_menu(struct descriptor_data *d);
static int roll_stat_for_cap(int cap);
static int autoroll_species_stat(struct char_data *mob, int ability);
/* utility functions */
ACMD(do_oasis_medit)
@ -228,15 +232,18 @@ void medit_setup_existing(struct descriptor_data *d, int rmob_num)
}
/* Ideally, this function should be in db.c, but I'll put it here for portability. */
static void init_mobile(struct char_data *mob)
void init_mobile(struct char_data *mob)
{
int i;
clear_char(mob);
GET_HIT(mob) = GET_MANA(mob) = 1;
GET_MAX_MANA(mob) = GET_MAX_MOVE(mob) = 100;
GET_MAX_MANA(mob) = GET_MAX_STAMINA(mob) = 100;
GET_WEIGHT(mob) = 200;
GET_HEIGHT(mob) = 198;
mob->player.time.birth = time(0);
mob->player.roleplay_age = MIN_CHAR_AGE;
mob->player.roleplay_age_year = time_info.year;
/* Only assign defaults if the individual stat is unset (zero) */
if (!mob->real_abils.str) mob->real_abils.str = 11;
@ -419,19 +426,20 @@ static void medit_disp_menu(struct descriptor_data *d)
{
struct char_data *mob;
char flags[MAX_STRING_LENGTH], flag2[MAX_STRING_LENGTH];
const char *background, *classname;
const char *classname, *speciesname;
mob = OLC_MOB(d);
get_char_colors(d->character);
clear_screen(d);
background = GET_BACKGROUND(mob) ? GET_BACKGROUND(mob) : "<None>\r\n";
classname = HAS_VALID_CLASS(mob) ? pc_class_types[GET_CLASS(mob)] : "Unassigned";
speciesname = HAS_VALID_SPECIES(mob) ? species_types[GET_SPECIES(mob)] : "Unassigned";
write_to_output(d,
"-- Mob Number: [%s%d%s]\r\n"
"%s1%s) Name: %s%s\r\n"
"%s2%s) Keywords: %s%s\r\n"
"%s3%s) Sex: %s%-7.7s%s\r\n"
"%sG%s) Age: %s%d%s\r\n"
"%s4%s) S-Desc: %s%s\r\n"
"%s5%s) L-Desc:-\r\n%s%s\r\n"
"%s6%s) D-Desc:-\r\n%s%s\r\n",
@ -440,6 +448,7 @@ static void medit_disp_menu(struct descriptor_data *d)
grn, nrm, yel, GET_NAME(mob),
grn, nrm, yel, GET_KEYWORDS(mob),
grn, nrm, yel, genders[(int)GET_SEX(mob)], nrm,
grn, nrm, yel, GET_ROLEPLAY_AGE(mob), nrm,
grn, nrm, yel, GET_SDESC(mob),
grn, nrm, yel, GET_LDESC(mob),
grn, nrm, yel, GET_DDESC(mob)
@ -452,12 +461,13 @@ static void medit_disp_menu(struct descriptor_data *d)
"%s8%s) Default : %s%s\r\n"
"%s9%s) Attack : %s%s\r\n"
"%sD%s) Class : %s%s\r\n"
"%sE%s) Species : %s%s\r\n"
"%sK%s) Skinning Menu...\r\n"
"%s0%s) Stats Menu...\r\n"
"%s-%s) Skills Menu...\r\n"
"%sA%s) NPC Flags : %s%s\r\n"
"%sB%s) AFF Flags : %s%s\r\n"
"%sC%s) Background:-\r\n%s%s\r\n"
"%sC%s) Background...\r\n"
"%sS%s) Script : %s%s\r\n"
"%sW%s) Copy mob\r\n"
"%sX%s) Delete mob\r\n"
@ -468,12 +478,13 @@ static void medit_disp_menu(struct descriptor_data *d)
grn, nrm, yel, position_types[(int)GET_DEFAULT_POS(mob)],
grn, nrm, yel, attack_hit_text[(int)GET_ATTACK(mob)].singular,
grn, nrm, yel, classname,
grn, nrm, yel, speciesname,
grn, nrm,
grn, nrm,
grn, nrm,
grn, nrm, cyn, flags,
grn, nrm, cyn, flag2,
grn, nrm, yel, background,
grn, nrm,
grn, nrm, cyn, OLC_SCRIPT(d) ?"Set.":"Not Set.",
grn, nrm,
grn, nrm,
@ -523,6 +534,46 @@ static void medit_disp_class_menu(struct descriptor_data *d)
OLC_MODE(d) = MEDIT_CLASS_MENU;
}
static void medit_disp_species_menu(struct descriptor_data *d)
{
struct char_data *mob = OLC_MOB(d);
const char *current = HAS_VALID_SPECIES(mob) ? species_types[GET_SPECIES(mob)] : "Unassigned";
get_char_colors(d->character);
clear_screen(d);
write_to_output(d,
"-- Mob Number: %s[%s%d%s]%s\r\n"
"Species selection for %s%s%s\r\n\r\n",
cyn, yel, OLC_NUM(d), cyn, nrm,
yel, GET_SDESC(mob), nrm);
for (int i = 0; i < NUM_SPECIES; i++) {
bool selected = HAS_VALID_SPECIES(mob) && (GET_SPECIES(mob) == i);
write_to_output(d, "%s%2d%s) %s%-12s%s%s\r\n",
cyn, i + 1, nrm,
selected ? grn : yel,
species_types[i],
nrm,
selected ? " (current)" : "");
}
write_to_output(d, "%s%2d%s) %sUnassigned%s%s\r\n",
cyn, NUM_SPECIES + 1, nrm,
!HAS_VALID_SPECIES(mob) ? grn : yel,
nrm,
!HAS_VALID_SPECIES(mob) ? " (current)" : "");
write_to_output(d,
"\r\nCurrent choice: %s%s%s\r\n"
"%s0%s) Return to main menu\r\n"
"Enter choice : ",
cyn, current, nrm,
cyn, nrm);
OLC_MODE(d) = MEDIT_SPECIES_MENU;
}
/* Display main menu. */
static void medit_disp_stats_menu(struct descriptor_data *d)
{
@ -534,32 +585,28 @@ static void medit_disp_stats_menu(struct descriptor_data *d)
clear_screen(d);
/* Color codes have to be used here, for count_color_codes to work */
sprintf(buf, "(range \ty%d\tn to \ty%d\tn)", GET_HIT(mob) + GET_MOVE(mob),
(GET_HIT(mob) * GET_MANA(mob)) + GET_MOVE(mob));
sprintf(buf, "(range \ty%d\tn to \ty%d\tn)", GET_HIT(mob) + GET_STAMINA(mob),
(GET_HIT(mob) * GET_MANA(mob)) + GET_STAMINA(mob));
/* Top section - standard stats */
write_to_output(d,
"-- Mob Number: %s[%s%d%s]%s\r\n"
"(%s1%s) Level: %s[%s%4d%s]%s\r\n"
"(%s2%s) %sAuto Set Stats (based on level)%s\r\n\r\n"
"(%s1%s) %sAuto Set Stats (species range)%s\r\n\r\n"
"Hit Points (xdy+z):\r\n"
"(%s3%s) HP NumDice: %s[%s%5d%s]%s\r\n"
"(%s4%s) HP SizeDice: %s[%s%5d%s]%s\r\n"
"(%s5%s) HP Addition: %s[%s%5d%s]%s\r\n"
"(%s8%s) Alignment: %s[%s%5d%s]%s\r\n\r\n",
"(%s2%s) HP NumDice: %s[%s%5d%s]%s\r\n"
"(%s3%s) HP SizeDice: %s[%s%5d%s]%s\r\n"
"(%s4%s) HP Addition: %s[%s%5d%s]%s\r\n\r\n",
cyn, yel, OLC_NUM(d), cyn, nrm,
cyn, nrm, cyn, yel, GET_LEVEL(mob), cyn, nrm,
cyn, nrm, cyn, nrm,
cyn, nrm, cyn, yel, GET_HIT(mob), cyn, nrm,
cyn, nrm, cyn, yel, GET_MANA(mob), cyn, nrm,
cyn, nrm, cyn, yel, GET_MOVE(mob), cyn, nrm,
cyn, nrm, cyn, yel, GET_ALIGNMENT(mob), cyn, nrm
cyn, nrm, cyn, yel, GET_STAMINA(mob), cyn, nrm
);
if (CONFIG_MEDIT_ADVANCED) {
/* Bottom section - non-standard stats, togglable in cedit */
write_to_output(d,
"%sAttributes Saving Throws\r\n"
"%sAttributes Saving Throw bonus\r\n"
"(%sF%s) Str: %s[%s%3d%s]%s (%sR%s) Save STR %s[%s%3d%s]%s\r\n"
"(%sG%s) Dex: %s[%s%3d%s]%s (%sS%s) Save DEX %s[%s%3d%s]%s\r\n"
"(%sH%s) Con: %s[%s%3d%s]%s (%sT%s) Save CON %s[%s%3d%s]%s\r\n"
@ -722,6 +769,11 @@ void medit_parse(struct descriptor_data *d, char *arg)
OLC_MODE(d) = MEDIT_SEX;
medit_disp_sex(d);
return;
case 'g':
case 'G':
OLC_MODE(d) = MEDIT_AGE;
write_to_output(d, "Enter age (%d-%d): ", MIN_CHAR_AGE, MAX_CHAR_AGE);
return;
case '4':
OLC_MODE(d) = MEDIT_S_DESC;
i--;
@ -757,6 +809,10 @@ void medit_parse(struct descriptor_data *d, char *arg)
case 'D':
medit_disp_class_menu(d);
return;
case 'e':
case 'E':
medit_disp_species_menu(d);
return;
case '0':
OLC_MODE(d) = MEDIT_STATS_MENU;
medit_disp_stats_menu(d);
@ -914,31 +970,23 @@ void medit_parse(struct descriptor_data *d, char *arg)
case 'Q':
medit_disp_menu(d);
return;
case '1': /* Edit level */
OLC_MODE(d) = MEDIT_LEVEL;
i++;
break;
case '2': /* Autoroll stats */
case '1': /* Autoroll stats */
medit_autoroll_stats(d);
medit_disp_stats_menu(d);
OLC_VAL(d) = TRUE;
return;
case '3':
case '2':
OLC_MODE(d) = MEDIT_NUM_HP_DICE;
i++;
break;
case '4':
case '3':
OLC_MODE(d) = MEDIT_SIZE_HP_DICE;
i++;
break;
case '5':
case '4':
OLC_MODE(d) = MEDIT_ADD_HP;
i++;
break;
case '8':
OLC_MODE(d) = MEDIT_ALIGNMENT;
i++;
break;
case 'f':
case 'F':
if (!CONFIG_MEDIT_ADVANCED) {
@ -1165,6 +1213,30 @@ void medit_parse(struct descriptor_data *d, char *arg)
medit_disp_menu(d);
return;
case MEDIT_SPECIES_MENU:
i = atoi(arg);
if (i == 0) {
medit_disp_menu(d);
return;
}
if (i == NUM_SPECIES + 1) {
GET_SPECIES(OLC_MOB(d)) = SPECIES_UNDEFINED;
OLC_VAL(d) = TRUE;
write_to_output(d, "Species cleared.\r\n");
medit_disp_menu(d);
return;
}
if (i < 1 || i > NUM_SPECIES + 1) {
write_to_output(d, "Invalid choice!\r\n");
medit_disp_species_menu(d);
return;
}
GET_SPECIES(OLC_MOB(d)) = i - 1;
OLC_VAL(d) = TRUE;
write_to_output(d, "Species set to %s.\r\n", species_types[GET_SPECIES(OLC_MOB(d))]);
medit_disp_menu(d);
return;
case OLC_SCRIPT_EDIT:
if (dg_script_edit_parse(d, arg)) return;
break;
@ -1246,6 +1318,19 @@ void medit_parse(struct descriptor_data *d, char *arg)
GET_SEX(OLC_MOB(d)) = LIMIT(i - 1, 0, NUM_GENDERS - 1);
break;
case MEDIT_AGE:
if (i < MIN_CHAR_AGE || i > MAX_CHAR_AGE) {
write_to_output(d, "Age must be between %d and %d.\r\n",
MIN_CHAR_AGE, MAX_CHAR_AGE);
write_to_output(d, "Enter age (%d-%d): ", MIN_CHAR_AGE, MAX_CHAR_AGE);
return;
}
GET_ROLEPLAY_AGE(OLC_MOB(d)) = i;
GET_ROLEPLAY_AGE_YEAR(OLC_MOB(d)) = time_info.year;
OLC_VAL(d) = TRUE;
medit_disp_menu(d);
return;
case MEDIT_NUM_HP_DICE:
GET_HIT(OLC_MOB(d)) = LIMIT(i, 0, 30);
OLC_VAL(d) = TRUE;
@ -1259,7 +1344,7 @@ void medit_parse(struct descriptor_data *d, char *arg)
return;
case MEDIT_ADD_HP:
GET_MOVE(OLC_MOB(d)) = LIMIT(i, 0, 30000);
GET_STAMINA(OLC_MOB(d)) = LIMIT(i, 0, 30000);
OLC_VAL(d) = TRUE;
medit_disp_stats_menu(d);
return;
@ -1413,25 +1498,83 @@ void medit_string_cleanup(struct descriptor_data *d, int terminator)
}
}
static int roll_stat_for_cap(int cap)
{
int total = 0;
int dice_d4 = 0;
int remainder = 0;
if (cap <= 0)
return 0;
dice_d4 = cap / 4;
remainder = cap % 4;
if (dice_d4 > 0)
total += dice(dice_d4, 4);
if (remainder > 0)
total += dice(1, remainder);
return total;
}
static int autoroll_species_stat(struct char_data *mob, int ability)
{
int min = 0;
int cap = 0;
int mod = 0;
int roll_cap;
int total;
if (HAS_VALID_SPECIES(mob)) {
int species = GET_SPECIES(mob);
int species_min = species_ability_min(species, ability);
int species_cap = species_ability_cap(species, ability);
if (species_min > 0)
min = species_min;
if (species_cap > 0)
cap = species_cap;
mod = species_ability_mod(species, ability);
}
if (cap > 0 && min > cap)
min = cap;
roll_cap = (cap > 0) ? (cap - mod) : (18 - mod);
if (roll_cap < 1)
roll_cap = 1;
total = roll_stat_for_cap(roll_cap) + mod;
if (cap > 0 && total > cap)
total = cap;
if (min > 0 && total < min)
total = min;
return total;
}
void medit_autoroll_stats(struct descriptor_data *d)
{
int mob_lev;
int mob_lev = GET_LEVEL(OLC_MOB(d));
mob_lev = 1;
GET_LEVEL(OLC_MOB(d)) = 1;
if (mob_lev < 1)
mob_lev = 1;
GET_MOVE(OLC_MOB(d)) = mob_lev * 10; /* hit point bonus (mobs don't use movement points) */
GET_STAMINA(OLC_MOB(d)) = mob_lev * 10; /* hit point bonus (mobs don't use stamina points) */
GET_HIT(OLC_MOB(d)) = mob_lev / 5; /* number of hitpoint dice */
GET_MANA(OLC_MOB(d)) = mob_lev / 5; /* size of hitpoint dice */
/* 'Advanced' stats are only rolled if advanced options are enabled */
if (CONFIG_MEDIT_ADVANCED) {
GET_STR(OLC_MOB(d)) = LIMIT((mob_lev * 2) / 3, 11, 18); /* 2/3 level in range 11 to 18 */
GET_INT(OLC_MOB(d)) = LIMIT((mob_lev * 2) / 3, 11, 18);
GET_WIS(OLC_MOB(d)) = LIMIT((mob_lev * 2) / 3, 11, 18);
GET_DEX(OLC_MOB(d)) = LIMIT((mob_lev * 2) / 3, 11, 18);
GET_CON(OLC_MOB(d)) = LIMIT((mob_lev * 2) / 3, 11, 18);
GET_CHA(OLC_MOB(d)) = LIMIT((mob_lev * 2) / 3, 11, 18);
GET_STR(OLC_MOB(d)) = autoroll_species_stat(OLC_MOB(d), ABIL_STR);
GET_INT(OLC_MOB(d)) = autoroll_species_stat(OLC_MOB(d), ABIL_INT);
GET_WIS(OLC_MOB(d)) = autoroll_species_stat(OLC_MOB(d), ABIL_WIS);
GET_DEX(OLC_MOB(d)) = autoroll_species_stat(OLC_MOB(d), ABIL_DEX);
GET_CON(OLC_MOB(d)) = autoroll_species_stat(OLC_MOB(d), ABIL_CON);
GET_CHA(OLC_MOB(d)) = autoroll_species_stat(OLC_MOB(d), ABIL_CHA);
/* New ability-based saving throws: all default to 1/4 of mob level */
GET_SAVE(OLC_MOB(d), ABIL_STR) = mob_lev / 4;

View file

@ -81,6 +81,7 @@ void mobile_activity(void)
((door = rand_number(0, 18)) < DIR_COUNT) && CAN_GO(ch, door) &&
!ROOM_FLAGGED(EXIT(ch, door)->to_room, ROOM_NOMOB) &&
!ROOM_FLAGGED(EXIT(ch, door)->to_room, ROOM_DEATH) &&
!RIDDEN_BY(ch) &&
(!MOB_FLAGGED(ch, MOB_STAY_ZONE) ||
(world[EXIT(ch, door)->to_room].zone == world[IN_ROOM(ch)].zone)))
{
@ -99,10 +100,7 @@ void mobile_activity(void)
if (MOB_FLAGGED(ch, MOB_WIMPY) && AWAKE(vict))
continue;
if (MOB_FLAGGED(ch, MOB_AGGRESSIVE ) ||
(MOB_FLAGGED(ch, MOB_AGGR_EVIL ) && IS_EVIL(vict)) ||
(MOB_FLAGGED(ch, MOB_AGGR_NEUTRAL) && IS_NEUTRAL(vict)) ||
(MOB_FLAGGED(ch, MOB_AGGR_GOOD ) && IS_GOOD(vict))) {
if (MOB_FLAGGED(ch, MOB_AGGRESSIVE)) {
/* Can a master successfully control the charmed monster? */
if (aggressive_mob_on_a_leash(ch, ch->master, vict))
@ -267,4 +265,3 @@ static bool aggressive_mob_on_a_leash(struct char_data *slave, struct char_data
/* So sorry, now you're a player killer... Tsk tsk. */
return (FALSE);
}

View file

@ -269,39 +269,41 @@ extern const char *nrm, *grn, *cyn, *yel;
#define MEDIT_SKILL_MENU 11
#define MEDIT_SKILL_EDIT 12
#define MEDIT_CLASS_MENU 13
#define MEDIT_SPECIES_MENU 14
/* Numerical responses. */
#define MEDIT_NUMERICAL_RESPONSE 14
#define MEDIT_SEX 15
#define MEDIT_NUM_HP_DICE 16
#define MEDIT_SIZE_HP_DICE 17
#define MEDIT_ADD_HP 18
#define MEDIT_POS 19
#define MEDIT_DEFAULT_POS 20
#define MEDIT_ATTACK 21
#define MEDIT_LEVEL 22
#define MEDIT_ALIGNMENT 23
#define MEDIT_DELETE 24
#define MEDIT_COPY 25
#define MEDIT_STR 26
#define MEDIT_INT 27
#define MEDIT_WIS 28
#define MEDIT_DEX 29
#define MEDIT_CON 30
#define MEDIT_CHA 31
#define MEDIT_SAVE_STR 32
#define MEDIT_SAVE_DEX 33
#define MEDIT_SAVE_CON 34
#define MEDIT_SAVE_INT 35
#define MEDIT_SAVE_WIS 36
#define MEDIT_SAVE_CHA 37
#define MEDIT_SKILL_VALUE 38
#define MEDIT_NUMERICAL_RESPONSE 15
#define MEDIT_SEX 16
#define MEDIT_AGE 44
#define MEDIT_NUM_HP_DICE 17
#define MEDIT_SIZE_HP_DICE 18
#define MEDIT_ADD_HP 19
#define MEDIT_POS 20
#define MEDIT_DEFAULT_POS 21
#define MEDIT_ATTACK 22
#define MEDIT_LEVEL 23
#define MEDIT_ALIGNMENT 24
#define MEDIT_DELETE 25
#define MEDIT_COPY 26
#define MEDIT_STR 27
#define MEDIT_INT 28
#define MEDIT_WIS 29
#define MEDIT_DEX 30
#define MEDIT_CON 31
#define MEDIT_CHA 32
#define MEDIT_SAVE_STR 33
#define MEDIT_SAVE_DEX 34
#define MEDIT_SAVE_CON 35
#define MEDIT_SAVE_INT 36
#define MEDIT_SAVE_WIS 37
#define MEDIT_SAVE_CHA 38
#define MEDIT_SKILL_VALUE 39
/* Skinning yield editor */
#define MEDIT_SKIN_MENU 39
#define MEDIT_SKIN_ADD_VNUM 40
#define MEDIT_SKIN_ADD_DC 41
#define MEDIT_SKIN_DELETE 42
#define MEDIT_SKIN_MENU 40
#define MEDIT_SKIN_ADD_VNUM 41
#define MEDIT_SKIN_ADD_DC 42
#define MEDIT_SKIN_DELETE 43
/* Submodes of SEDIT connectedness. */
#define SEDIT_MAIN_MENU 0

View file

@ -226,9 +226,7 @@ static void auto_equip(struct char_data *ch, struct obj_data *obj, int location)
if (location > 0) { /* Wearable. */
if (!GET_EQ(ch,j)) {
/* Check the characters's alignment to prevent them from being zapped
* through the auto-equipping. */
if (invalid_align(ch, obj) || invalid_class(ch, obj))
if (invalid_class(ch, obj))
location = LOC_INVENTORY;
else
equip_char(ch, obj, j);
@ -573,6 +571,9 @@ obj_save_data *objsave_parse_objects(FILE *fl)
void commit_current(void) {
if (!temp) return;
if (GET_OBJ_TYPE(temp) == ITEM_MONEY)
update_money_obj(temp);
/* sanitize top-level locate range only; children will be negative later */
int loc = pending_locate;
if (pending_nest <= 0) {

View file

@ -15,10 +15,10 @@
#define PFDEF_SEX 0
#define PFDEF_CLASS 0
#define PFDEF_SPECIES SPECIES_UNDEFINED
#define PFDEF_LEVEL 0
#define PFDEF_HEIGHT 0
#define PFDEF_WEIGHT 0
#define PFDEF_ALIGNMENT 0
#define PFDEF_PLRFLAGS 0
#define PFDEF_AFFFLAGS 0
#define PFDEF_SAVETHROW 0
@ -29,7 +29,6 @@
#define PFDEF_CONDITION 0
#define PFDEF_BADPWS 0
#define PFDEF_PREFFLAGS 0
#define PFDEF_PRACTICES 0
#define PFDEF_COINS 0
#define PFDEF_BANK_COINS 0
#define PFDEF_EXP 0
@ -44,8 +43,8 @@
#define PFDEF_MAXHIT 0
#define PFDEF_MANA 0
#define PFDEF_MAXMANA 0
#define PFDEF_MOVE 0
#define PFDEF_MAXMOVE 0
#define PFDEF_STAMINA 0
#define PFDEF_MAXSTAMINA 0
#define PFDEF_HUNGER 0
#define PFDEF_THIRST 0
#define PFDEF_DRUNK 0
@ -58,5 +57,7 @@
#define PFDEF_CURRQUEST NOTHING
#define PFDEF_LASTMOTD 0
#define PFDEF_LASTNEWS 0
#define PFDEF_REROLL_USED 0
#define PFDEF_REROLL_EXPIRES 0
#endif /* _PFDEFAULTS_H_ */

View file

@ -25,7 +25,7 @@
#define LOAD_HIT 0
#define LOAD_MANA 1
#define LOAD_MOVE 2
#define LOAD_STAMINA 2
#define LOAD_STRENGTH 3
#define PT_PNAME(i) (player_table[(i)].name)
@ -222,6 +222,20 @@ char *get_name_by_id(long id)
return (NULL);
}
static void update_roleplay_age(struct char_data *ch)
{
if (GET_ROLEPLAY_AGE(ch) == 0)
GET_ROLEPLAY_AGE(ch) = MIN_CHAR_AGE;
if (GET_ROLEPLAY_AGE_YEAR(ch) == 0)
GET_ROLEPLAY_AGE_YEAR(ch) = time_info.year;
if (time_info.year > GET_ROLEPLAY_AGE_YEAR(ch)) {
GET_ROLEPLAY_AGE(ch) += (time_info.year - GET_ROLEPLAY_AGE_YEAR(ch));
GET_ROLEPLAY_AGE_YEAR(ch) = time_info.year;
}
}
/* Stuff related to the save/load player system. */
/* New load_char reads ASCII Player Files. Load a char, TRUE if loaded, FALSE
* if not. */
@ -232,6 +246,7 @@ int load_char(const char *name, struct char_data *ch)
char filename[40];
char buf[128], buf2[128], line[MAX_INPUT_LENGTH + 1], tag[6];
char f1[128], f2[128], f3[128], f4[128];
bool loaded_stamina = FALSE;
trig_data *t = NULL;
trig_rnum t_rnum = NOTHING;
@ -255,10 +270,12 @@ int load_char(const char *name, struct char_data *ch)
ch->player_specials->saved.skills[i] = 0;
GET_SEX(ch) = PFDEF_SEX;
GET_CLASS(ch) = PFDEF_CLASS;
GET_SPECIES(ch) = PFDEF_SPECIES;
GET_LEVEL(ch) = PFDEF_LEVEL;
GET_HEIGHT(ch) = PFDEF_HEIGHT;
GET_WEIGHT(ch) = PFDEF_WEIGHT;
GET_ALIGNMENT(ch) = PFDEF_ALIGNMENT;
GET_ROLEPLAY_AGE(ch) = 0;
GET_ROLEPLAY_AGE_YEAR(ch) = 0;
for (i = 0; i < NUM_OF_SAVING_THROWS; i++)
GET_SAVE(ch, i) = PFDEF_SAVETHROW;
GET_LOADROOM(ch) = PFDEF_LOADROOM;
@ -283,8 +300,8 @@ int load_char(const char *name, struct char_data *ch)
GET_MAX_HIT(ch) = PFDEF_MAXHIT;
GET_MANA(ch) = PFDEF_MANA;
GET_MAX_MANA(ch) = PFDEF_MAXMANA;
GET_MOVE(ch) = PFDEF_MOVE;
GET_MAX_MOVE(ch) = PFDEF_MAXMOVE;
GET_STAMINA(ch) = PFDEF_STAMINA;
GET_MAX_STAMINA(ch) = PFDEF_MAXSTAMINA;
GET_OLC_ZONE(ch) = PFDEF_OLC;
GET_PAGE_LENGTH(ch) = PFDEF_PAGELENGTH;
GET_SCREEN_WIDTH(ch) = PFDEF_SCREENWIDTH;
@ -297,6 +314,9 @@ int load_char(const char *name, struct char_data *ch)
GET_NUM_QUESTS(ch) = PFDEF_COMPQUESTS;
GET_LAST_MOTD(ch) = PFDEF_LASTMOTD;
GET_LAST_NEWS(ch) = PFDEF_LASTNEWS;
GET_REROLL_USED(ch) = PFDEF_REROLL_USED;
GET_REROLL_EXPIRES(ch) = PFDEF_REROLL_EXPIRES;
memset(&GET_REROLL_OLD_ABILS(ch), 0, sizeof(struct char_ability_data));
if (GET_ACCOUNT(ch)) {
free(GET_ACCOUNT(ch));
GET_ACCOUNT(ch) = NULL;
@ -315,6 +335,8 @@ int load_char(const char *name, struct char_data *ch)
switch (*tag) {
case 'A':
if (!strcmp(tag, "Ac ")) GET_AC(ch) = atoi(line);
else if (!strcmp(tag, "AgYr")) GET_ROLEPLAY_AGE_YEAR(ch) = atoi(line);
else if (!strcmp(tag, "Age ")) GET_ROLEPLAY_AGE(ch) = LIMIT(atoi(line), MIN_CHAR_AGE, MAX_CHAR_AGE);
else if (!strcmp(tag, "Acct")) {
if (GET_ACCOUNT(ch))
free(GET_ACCOUNT(ch));
@ -338,7 +360,6 @@ int load_char(const char *name, struct char_data *ch)
AFF_FLAGS(ch)[0] = asciiflag_conv(line);
}
if (!strcmp(tag, "Affs")) load_affects(fl, ch);
else if (!strcmp(tag, "Alin")) GET_ALIGNMENT(ch) = atoi(line);
else if (!strcmp(tag, "Alis")) read_aliases_ascii(fl, ch, atoi(line));
break;
@ -396,7 +417,7 @@ int load_char(const char *name, struct char_data *ch)
case 'M':
if (!strcmp(tag, "Mana")) load_HMVS(ch, line, LOAD_MANA);
else if (!strcmp(tag, "Move")) load_HMVS(ch, line, LOAD_MOVE);
else if (!strcmp(tag, "Move") && !loaded_stamina) load_HMVS(ch, line, LOAD_STAMINA);
break;
case 'N':
@ -434,10 +455,34 @@ int load_char(const char *name, struct char_data *ch)
case 'R':
if (!strcmp(tag, "Room")) GET_LOADROOM(ch) = atoi(line);
else if (!strcmp(tag, "RrUs")) GET_REROLL_USED(ch) = atoi(line);
else if (!strcmp(tag, "RrTm")) GET_REROLL_EXPIRES(ch) = (time_t)atol(line);
else if (!strcmp(tag, "RrAb")) {
int rstr, rint, rwis, rdex, rcon, rcha;
if (sscanf(line, "%d %d %d %d %d %d", &rstr, &rint, &rwis, &rdex, &rcon, &rcha) == 6) {
GET_REROLL_OLD_ABILS(ch).str = rstr;
GET_REROLL_OLD_ABILS(ch).intel = rint;
GET_REROLL_OLD_ABILS(ch).wis = rwis;
GET_REROLL_OLD_ABILS(ch).dex = rdex;
GET_REROLL_OLD_ABILS(ch).con = rcon;
GET_REROLL_OLD_ABILS(ch).cha = rcha;
}
}
break;
case 'S':
if (!strcmp(tag, "Sex ")) GET_SEX(ch) = atoi(line);
if (!strcmp(tag, "Spec")) {
int val = atoi(line);
if (val < SPECIES_UNDEFINED || val >= NUM_SPECIES)
val = SPECIES_UNDEFINED;
GET_SPECIES(ch) = val;
}
else if (!strcmp(tag, "Stam")) {
load_HMVS(ch, line, LOAD_STAMINA);
loaded_stamina = TRUE;
}
else if (!strcmp(tag, "Sex ")) GET_SEX(ch) = atoi(line);
else if (!strcmp(tag, "Sdsc")) {
/* Clear any existing sdesc to avoid leaks */
if (GET_SHORT_DESC(ch))
@ -503,7 +548,13 @@ int load_char(const char *name, struct char_data *ch)
}
}
update_roleplay_age(ch);
ch->player.time.birth = time(0) - get_total_played_seconds(ch);
affect_total(ch);
MOUNT(ch) = NULL;
RIDDEN_BY(ch) = NULL;
HITCHED_TO(ch) = NULL;
REMOVE_BIT_AR(AFF_FLAGS(ch), AFF_MOUNTED);
/* initialization for imms */
if (GET_LEVEL(ch) >= LVL_IMMORT) {
@ -552,6 +603,9 @@ void save_char(struct char_data * ch)
}
}
update_roleplay_age(ch);
ch->player.time.birth = time(0) - get_total_played_seconds(ch);
if (!get_filename(filename, sizeof(filename), PLR_FILE, GET_NAME(ch)))
return;
if (!(fl = fopen(filename, "w"))) {
@ -614,10 +668,13 @@ void save_char(struct char_data * ch)
if (POOFOUT(ch)) fprintf(fl, "PfOt: %s\n", POOFOUT(ch));
if (GET_SEX(ch) != PFDEF_SEX) fprintf(fl, "Sex : %d\n", GET_SEX(ch));
if (GET_CLASS(ch) != PFDEF_CLASS) fprintf(fl, "Clas: %d\n", GET_CLASS(ch));
if (GET_SPECIES(ch) != PFDEF_SPECIES) fprintf(fl, "Spec: %d\n", GET_SPECIES(ch));
if (GET_LEVEL(ch) != PFDEF_LEVEL) fprintf(fl, "Levl: %d\n", GET_LEVEL(ch));
fprintf(fl, "Id : %ld\n", GET_IDNUM(ch));
fprintf(fl, "Brth: %ld\n", (long)ch->player.time.birth);
fprintf(fl, "Age : %d\n", GET_ROLEPLAY_AGE(ch));
fprintf(fl, "AgYr: %d\n", GET_ROLEPLAY_AGE_YEAR(ch));
fprintf(fl, "Plyd: %d\n", ch->player.time.played);
fprintf(fl, "Last: %ld\n", (long)ch->player.time.logon);
@ -625,11 +682,22 @@ void save_char(struct char_data * ch)
fprintf(fl, "Lmot: %d\n", (int)GET_LAST_MOTD(ch));
if (GET_LAST_NEWS(ch) != PFDEF_LASTNEWS)
fprintf(fl, "Lnew: %d\n", (int)GET_LAST_NEWS(ch));
if (GET_REROLL_USED(ch) != PFDEF_REROLL_USED)
fprintf(fl, "RrUs: %d\n", (int)GET_REROLL_USED(ch));
if (GET_REROLL_EXPIRES(ch) != PFDEF_REROLL_EXPIRES) {
fprintf(fl, "RrTm: %ld\n", (long)GET_REROLL_EXPIRES(ch));
fprintf(fl, "RrAb: %d %d %d %d %d %d\n",
GET_REROLL_OLD_ABILS(ch).str,
GET_REROLL_OLD_ABILS(ch).intel,
GET_REROLL_OLD_ABILS(ch).wis,
GET_REROLL_OLD_ABILS(ch).dex,
GET_REROLL_OLD_ABILS(ch).con,
GET_REROLL_OLD_ABILS(ch).cha);
}
if (GET_HOST(ch)) fprintf(fl, "Host: %s\n", GET_HOST(ch));
if (GET_HEIGHT(ch) != PFDEF_HEIGHT) fprintf(fl, "Hite: %d\n", GET_HEIGHT(ch));
if (GET_WEIGHT(ch) != PFDEF_WEIGHT) fprintf(fl, "Wate: %d\n", GET_WEIGHT(ch));
if (GET_ALIGNMENT(ch) != PFDEF_ALIGNMENT) fprintf(fl, "Alin: %d\n", GET_ALIGNMENT(ch));
sprintascii(bits, PLR_FLAGS(ch)[0]);
@ -669,7 +737,7 @@ void save_char(struct char_data * ch)
if (GET_HIT(ch) != PFDEF_HIT || GET_MAX_HIT(ch) != PFDEF_MAXHIT) fprintf(fl, "Hit : %d/%d\n", GET_HIT(ch), GET_MAX_HIT(ch));
if (GET_MANA(ch) != PFDEF_MANA || GET_MAX_MANA(ch) != PFDEF_MAXMANA) fprintf(fl, "Mana: %d/%d\n", GET_MANA(ch), GET_MAX_MANA(ch));
if (GET_MOVE(ch) != PFDEF_MOVE || GET_MAX_MOVE(ch) != PFDEF_MAXMOVE) fprintf(fl, "Move: %d/%d\n", GET_MOVE(ch), GET_MAX_MOVE(ch));
if (GET_STAMINA(ch) != PFDEF_STAMINA || GET_MAX_STAMINA(ch) != PFDEF_MAXSTAMINA) fprintf(fl, "Stam: %d/%d\n", GET_STAMINA(ch), GET_MAX_STAMINA(ch));
if (GET_STR(ch) != PFDEF_STR) fprintf(fl, "Str : %d\n", GET_STR(ch));
if (GET_INT(ch) != PFDEF_INT) fprintf(fl, "Int : %d\n", GET_INT(ch));
@ -946,9 +1014,9 @@ static void load_HMVS(struct char_data *ch, const char *line, int mode)
GET_MAX_MANA(ch) = num2;
break;
case LOAD_MOVE:
GET_MOVE(ch) = num;
GET_MAX_MOVE(ch) = num2;
case LOAD_STAMINA:
GET_STAMINA(ch) = num;
GET_MAX_STAMINA(ch) = num2;
break;
case LOAD_STRENGTH:

View file

@ -104,7 +104,7 @@ static void prefedit_disp_main_menu(struct descriptor_data *d)
vict = PREFEDIT_GET_CHAR;
sprintf(prompt_string, "%s%s%s", PREFEDIT_FLAGGED(PRF_DISPHP) ? "H" : "", PREFEDIT_FLAGGED(PRF_DISPMANA) ? "M" : "",
PREFEDIT_FLAGGED(PRF_DISPMOVE) ? "V" : "" );
PREFEDIT_FLAGGED(PRF_DISPSTAMINA) ? "S" : "" );
sprintf(color_string, "%s", multi_types[(PREFEDIT_FLAGGED(PRF_COLOR_1) ? 1 : 0) + (PREFEDIT_FLAGGED(PRF_COLOR_2) ? 2 : 0)]);
@ -292,12 +292,12 @@ static void prefedit_disp_prompt_menu(struct descriptor_data *d)
sprintf(prompt_string, "<Auto>");
else
sprintf(prompt_string, "%s%s%s", PREFEDIT_FLAGGED(PRF_DISPHP) ? "H" : "", PREFEDIT_FLAGGED(PRF_DISPMANA) ? "M" : "",
PREFEDIT_FLAGGED(PRF_DISPMOVE) ? "V" : "");
PREFEDIT_FLAGGED(PRF_DISPSTAMINA) ? "S" : "");
send_to_char(d->character, "%sPrompt Settings\r\n"
"%s1%s) Toggle HP\r\n"
"%s2%s) Toggle Mana\r\n"
"%s3%s) Toggle Moves\r\n"
"%s3%s) Toggle Stamina\r\n"
"%s4%s) Toggle auto flag\r\n\r\n"
"%sCurrent Prompt: %s%s%s\r\n\r\n"
"%s0%s) Quit (to main menu)\r\n",
@ -730,10 +730,10 @@ void prefedit_parse(struct descriptor_data * d, char *arg)
}
else if (number == 3)
{
if (PREFEDIT_FLAGGED(PRF_DISPMOVE))
REMOVE_BIT_AR(PREFEDIT_GET_FLAGS, PRF_DISPMOVE);
if (PREFEDIT_FLAGGED(PRF_DISPSTAMINA))
REMOVE_BIT_AR(PREFEDIT_GET_FLAGS, PRF_DISPSTAMINA);
else
SET_BIT_AR(PREFEDIT_GET_FLAGS, PRF_DISPMOVE);
SET_BIT_AR(PREFEDIT_GET_FLAGS, PRF_DISPSTAMINA);
}
else if (number == 4)
{
@ -784,9 +784,9 @@ void prefedit_Restore_Defaults(struct descriptor_data *d)
if (!PREFEDIT_FLAGGED(PRF_DISPMANA))
SET_BIT_AR(PREFEDIT_GET_FLAGS, PRF_DISPMANA);
/* PRF_DISPMOVE - On */
if (!PREFEDIT_FLAGGED(PRF_DISPMOVE))
SET_BIT_AR(PREFEDIT_GET_FLAGS, PRF_DISPMOVE);
/* PRF_DISPSTAMINA - On */
if (!PREFEDIT_FLAGGED(PRF_DISPSTAMINA))
SET_BIT_AR(PREFEDIT_GET_FLAGS, PRF_DISPSTAMINA);
/* PRF_AUTOEXIT - On */
if (!PREFEDIT_FLAGGED(PRF_AUTOEXIT))

View file

@ -106,7 +106,7 @@ static const char s_Button5[] = "\005\002Inventory\002inventory\006";
static const char s_Gauge1[] = "\005\002Health\002red\002HEALTH\002HEALTH_MAX\006";
static const char s_Gauge2[] = "\005\002Mana\002blue\002MANA\002MANA_MAX\006";
static const char s_Gauge3[] = "\005\002Movement\002green\002MOVEMENT\002MOVEMENT_MAX\006";
static const char s_Gauge3[] = "\005\002Stamina\002green\002MOVEMENT\002MOVEMENT_MAX\006";
static const char s_Gauge4[] = "\005\002Exp TNL\002yellow\002EXPERIENCE\002EXPERIENCE_MAX\006";
static const char s_Gauge5[] = "\005\002Opponent\002darkred\002OPPONENT_HEALTH\002OPPONENT_HEALTH_MAX\006";
@ -142,11 +142,10 @@ static variable_name_t VariableNameTable[eMSDP_MAX+1] =
{ eMSDP_LEVEL, "LEVEL", NUMBER_READ_ONLY },
{ eMSDP_RACE, "RACE", STRING_READ_ONLY },
{ eMSDP_CLASS, "CLASS", STRING_READ_ONLY },
{ eMSDP_MANA, "MANA", NUMBER_READ_ONLY },
{ eMSDP_MANA_MAX, "MANA_MAX", NUMBER_READ_ONLY },
{ eMSDP_WIMPY, "WIMPY", NUMBER_READ_ONLY },
{ eMSDP_PRACTICE, "PRACTICE", NUMBER_READ_ONLY },
{ eMSDP_MONEY, "MONEY", NUMBER_READ_ONLY },
{ eMSDP_MANA, "MANA", NUMBER_READ_ONLY },
{ eMSDP_MANA_MAX, "MANA_MAX", NUMBER_READ_ONLY },
{ eMSDP_WIMPY, "WIMPY", NUMBER_READ_ONLY },
{ eMSDP_MONEY, "MONEY", NUMBER_READ_ONLY },
{ eMSDP_MOVEMENT, "MOVEMENT", NUMBER_READ_ONLY },
{ eMSDP_MOVEMENT_MAX, "MOVEMENT_MAX", NUMBER_READ_ONLY },
{ eMSDP_AC, "AC", NUMBER_READ_ONLY },

View file

@ -106,11 +106,10 @@ typedef enum
eMSDP_LEVEL,
eMSDP_RACE,
eMSDP_CLASS,
eMSDP_MANA,
eMSDP_MANA_MAX,
eMSDP_WIMPY,
eMSDP_PRACTICE,
eMSDP_MONEY,
eMSDP_MANA,
eMSDP_MANA_MAX,
eMSDP_WIMPY,
eMSDP_MONEY,
eMSDP_MOVEMENT,
eMSDP_MOVEMENT_MAX,
eMSDP_AC,

View file

@ -1,710 +0,0 @@
/**
* @file roomsave.c
* Numeric and string contants used by the MUD.
*
* An addition to the core tbaMUD source code distribution, which is a derivative
* of, and continuation of, CircleMUD.
*
* All rights reserved. See license for complete information.
* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University
* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.
*
*/
#include "conf.h"
#include "sysdep.h"
#include <stdlib.h>
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
#include "structs.h"
#include "utils.h"
#include "db.h"
#include "handler.h"
#include "comm.h"
#include "constants.h"
#include "roomsave.h"
/* Write saved rooms under lib/world/rsv/<vnum>.rsv (like wld/ zon/ obj/). */
#ifndef ROOMSAVE_PREFIX
#define ROOMSAVE_PREFIX LIB_WORLD "rsv/"
#endif
#ifndef ROOMSAVE_EXT
#define ROOMSAVE_EXT ".rsv"
#endif
static unsigned char *roomsave_dirty = NULL;
void RoomSave_init_dirty(void) {
free(roomsave_dirty);
roomsave_dirty = calloc((size_t)top_of_world + 1, 1);
}
void RoomSave_mark_dirty_room(room_rnum rnum) {
if (!roomsave_dirty) return;
if (rnum != NOWHERE && rnum >= 0 && rnum <= top_of_world)
roomsave_dirty[rnum] = 1;
}
/* Where does an object “live” (topmost location -> room)? */
room_rnum RoomSave_room_of_obj(struct obj_data *o) {
if (!o) return NOWHERE;
while (o->in_obj) o = o->in_obj;
if (o->carried_by) return IN_ROOM(o->carried_by);
if (o->worn_by) return IN_ROOM(o->worn_by);
return o->in_room;
}
/* --- helper: read a list of objects until '.' or 'E' and return the head --- */
/* Context-aware implementation: stop_on_E = 1 for nested B..E, 0 for top-level. */
static struct obj_data *roomsave_read_list_ctx(FILE *fl, int stop_on_E)
{
char line[256];
struct obj_data *head = NULL, *tail = NULL;
while (fgets(line, sizeof(line), fl)) {
if (line[0] == '.') {
/* End of this list scope */
break;
}
if (stop_on_E && line[0] == 'E') {
/* End of nested (B..E) scope */
break;
}
/* For top-level reads (stop_on_E==0), or any non-'O', push back
so the outer #R reader can handle M/E/G/P or '.' */
if (line[0] != 'O') {
long back = -((long)strlen(line));
fseek(fl, back, SEEK_CUR);
break;
}
/* Parse object header: O vnum timer weight cost unused */
int vnum, timer, weight, cost, unused_cost;
if (sscanf(line, "O %d %d %d %d %d", &vnum, &timer, &weight, &cost, &unused_cost) != 5)
continue;
/* IMPORTANT: read by VNUM (VIRTUAL), not real index */
struct obj_data *obj = read_object((obj_vnum)vnum, VIRTUAL);
if (!obj) {
mudlog(NRM, LVL_IMMORT, TRUE, "RoomSave: read_object(vnum=%d) failed.", vnum);
/* Skip to next object/header or end-of-scope */
long backpos;
while (fgets(line, sizeof(line), fl)) {
if (line[0] == 'O' || line[0] == '.' || (stop_on_E && line[0] == 'E')) {
backpos = -((long)strlen(line));
fseek(fl, backpos, SEEK_CUR);
break;
}
}
continue;
}
/* Apply core scalars */
GET_OBJ_TIMER(obj) = timer;
GET_OBJ_WEIGHT(obj) = weight;
GET_OBJ_COST(obj) = cost;
GET_OBJ_COST_PER_DAY(obj) = 0;
/* Clear array flags so missing slots don't keep proto bits */
#ifdef EF_ARRAY_MAX
# ifdef GET_OBJ_EXTRA_AR
for (int i = 0; i < EF_ARRAY_MAX; i++) GET_OBJ_EXTRA_AR(obj, i) = 0;
# else
for (int i = 0; i < EF_ARRAY_MAX; i++) GET_OBJ_EXTRA(obj)[i] = 0;
# endif
#endif
#ifdef TW_ARRAY_MAX
for (int i = 0; i < TW_ARRAY_MAX; i++) GET_OBJ_WEAR(obj)[i] = 0;
#endif
/* Read per-object lines until next 'O' or '.' or 'E'(when nested) */
long backpos;
while (fgets(line, sizeof(line), fl)) {
if (line[0] == 'V') {
int idx, val;
if (sscanf(line, "V %d %d", &idx, &val) == 2) {
#ifdef NUM_OBJ_VAL_POSITIONS
if (idx >= 0 && idx < NUM_OBJ_VAL_POSITIONS) GET_OBJ_VAL(obj, idx) = val;
#else
if (idx >= 0 && idx < 6) GET_OBJ_VAL(obj, idx) = val;
#endif
}
continue;
} else if (line[0] == 'X') { /* extra flags */
int idx, val;
if (sscanf(line, "X %d %d", &idx, &val) == 2) {
#if defined(EF_ARRAY_MAX) && defined(GET_OBJ_EXTRA_AR)
if (idx >= 0 && idx < EF_ARRAY_MAX) GET_OBJ_EXTRA_AR(obj, idx) = val;
#elif defined(EF_ARRAY_MAX)
if (idx >= 0 && idx < EF_ARRAY_MAX) GET_OBJ_EXTRA(obj)[idx] = val;
#else
if (idx == 0) GET_OBJ_EXTRA(obj) = val;
#endif
}
continue;
} else if (line[0] == 'W') { /* wear flags */
int idx, val;
if (sscanf(line, "W %d %d", &idx, &val) == 2) {
#ifdef TW_ARRAY_MAX
if (idx >= 0 && idx < TW_ARRAY_MAX) GET_OBJ_WEAR(obj)[idx] = val;
#else
if (idx == 0) GET_OBJ_WEAR(obj) = val;
#endif
}
continue;
} else if (line[0] == 'B') {
/* Nested contents until matching 'E' */
struct obj_data *child_head = roomsave_read_list_ctx(fl, 1 /* stop_on_E */);
/* Detach each node before obj_to_obj(), otherwise we lose siblings */
for (struct obj_data *it = child_head, *next; it; it = next) {
next = it->next_content; /* remember original sibling */
it->next_content = NULL; /* detach from temp list */
obj_to_obj(it, obj); /* push into container (LIFO) */
}
continue;
} else if (line[0] == 'O' || line[0] == '.' || (stop_on_E && line[0] == 'E')) {
/* Next object / end-of-scope: rewind one line for outer loop to see it */
backpos = -((long)strlen(line));
fseek(fl, backpos, SEEK_CUR);
break;
} else {
/* ignore unknown lines */
continue;
}
}
/* Append to this scope's list */
obj->next_content = NULL;
if (!head) head = tail = obj;
else { tail->next_content = obj; tail = obj; }
}
return head;
}
/* Keep your existing one-arg API for callers: top-level semantics (stop_on_E = 0). */
static struct obj_data *roomsave_read_list(FILE *fl)
{
return roomsave_read_list_ctx(fl, 0);
}
/* ---------- Minimal line format ----------
#R <vnum> <unix_time>
O <vnum> <timer> <extra_flags> <wear_flags> <weight> <cost> <unused>
V <i> <val[i]> ; repeated for all value slots present on this obj
B ; begin contents of this object (container)
E ; end contents of this object
. ; end of room
------------------------------------------ */
static void ensure_dir_exists(const char *path) {
if (mkdir(path, 0775) == -1 && errno != EEXIST) {
mudlog(CMP, LVL_IMMORT, TRUE, "SYSERR: roomsave mkdir(%s): %s", path, strerror(errno));
}
}
/* zone vnum for a given room rnum (e.g., 134 -> zone 1) */
static int roomsave_zone_for_rnum(room_rnum rnum) {
if (rnum == NOWHERE || rnum < 0 || rnum > top_of_world) return 0;
zone_rnum znum = world[rnum].zone;
if (znum < 0 || znum > top_of_zone_table) return 0;
return zone_table[znum].number; /* e.g., 1 for rooms 100199, 2 for 200299, etc. */
}
/* lib/world/rsv/<zone>.rsv */
static void roomsave_zone_filename(int zone_vnum, char *out, size_t outsz) {
snprintf(out, outsz, "%s%d%s", ROOMSAVE_PREFIX, zone_vnum, ROOMSAVE_EXT);
}
/* Write one object (and its recursive contents) */
static void write_one_object(FILE *fl, struct obj_data *obj) {
int i;
/* Core scalars (flags printed separately per-slot) */
fprintf(fl, "O %d %d %d %d %d\n",
GET_OBJ_VNUM(obj),
GET_OBJ_TIMER(obj),
GET_OBJ_WEIGHT(obj),
GET_OBJ_COST(obj),
GET_OBJ_COST_PER_DAY(obj));
/* Extra flags array */
#if defined(EF_ARRAY_MAX) && defined(GET_OBJ_EXTRA_AR)
for (i = 0; i < EF_ARRAY_MAX; i++)
fprintf(fl, "X %d %d\n", i, GET_OBJ_EXTRA_AR(obj, i));
#elif defined(EF_ARRAY_MAX)
for (i = 0; i < EF_ARRAY_MAX; i++)
fprintf(fl, "X %d %d\n", i, GET_OBJ_EXTRA(obj)[i]);
#else
fprintf(fl, "X %d %d\n", 0, GET_OBJ_EXTRA(obj));
#endif
/* Wear flags array */
#ifdef TW_ARRAY_MAX
for (i = 0; i < TW_ARRAY_MAX; i++)
fprintf(fl, "W %d %d\n", i, GET_OBJ_WEAR(obj)[i]);
#else
fprintf(fl, "W %d %d\n", 0, GET_OBJ_WEAR(obj));
#endif
/* Values[] (durability, liquids, charges, etc.) */
#ifdef NUM_OBJ_VAL_POSITIONS
for (i = 0; i < NUM_OBJ_VAL_POSITIONS; i++)
fprintf(fl, "V %d %d\n", i, GET_OBJ_VAL(obj, i));
#else
for (i = 0; i < 6; i++)
fprintf(fl, "V %d %d\n", i, GET_OBJ_VAL(obj, i));
#endif
/* Contents (recursive) */
if (obj->contains) {
struct obj_data *cont;
fprintf(fl, "B\n");
for (cont = obj->contains; cont; cont = cont->next_content)
write_one_object(fl, cont);
fprintf(fl, "E\n");
}
}
/* Forward declaration for RoomSave_now*/
static void RS_write_room_mobs(FILE *out, room_rnum rnum);
/* Public: write the entire rooms contents */
int RoomSave_now(room_rnum rnum) {
char path[PATH_MAX], tmp[PATH_MAX], line[512];
FILE *in = NULL, *out = NULL;
room_vnum rvnum;
int zvnum;
if (rnum == NOWHERE)
return 0;
rvnum = world[rnum].number;
zvnum = roomsave_zone_for_rnum(rnum);
if (zvnum < 0)
return 0;
ensure_dir_exists(ROOMSAVE_PREFIX);
roomsave_zone_filename(zvnum, path, sizeof(path));
{
int n = snprintf(tmp, sizeof(tmp), "%s.tmp", path);
if (n < 0 || n >= (int)sizeof(tmp)) {
mudlog(NRM, LVL_IMMORT, TRUE,
"SYSERR: RoomSave: temp path too long for %s", path);
return 0;
}
}
if (!(out = fopen(tmp, "w"))) {
mudlog(NRM, LVL_IMMORT, TRUE,
"SYSERR: RoomSave: fopen(%s) failed: %s",
tmp, strerror(errno));
return 0;
}
if ((in = fopen(path, "r")) != NULL) {
while (fgets(line, sizeof(line), in)) {
if (strncmp(line, "#R ", 3) == 0) {
int file_rvnum;
long ts;
if (sscanf(line, "#R %d %ld", &file_rvnum, &ts) == 2) {
if (file_rvnum == (int)rvnum) {
/* Skip old block completely until and including '.' line */
while (fgets(line, sizeof(line), in)) {
if (line[0] == '.') {
/* consume it and break */
break;
}
}
continue; /* do NOT write skipped lines */
}
}
}
fputs(line, out); /* keep unrelated lines */
}
fclose(in);
}
/* Append new block */
fprintf(out, "#R %d %ld\n", rvnum, (long)time(0));
RS_write_room_mobs(out, rnum);
for (struct obj_data *obj = world[rnum].contents; obj; obj = obj->next_content)
write_one_object(out, obj);
/* Always terminate block */
fprintf(out, ".\n");
if (fclose(out) != 0) {
mudlog(NRM, LVL_IMMORT, TRUE,
"SYSERR: RoomSave: fclose(%s) failed: %s",
tmp, strerror(errno));
return 0;
}
if (rename(tmp, path) != 0) {
mudlog(NRM, LVL_IMMORT, TRUE,
"SYSERR: RoomSave: rename(%s -> %s) failed: %s",
tmp, path, strerror(errno));
return 0;
}
return 1;
}
/* --- M/E/G/P load helpers (mob restore) -------------------------------- */
struct rs_load_ctx {
room_rnum rnum;
struct char_data *cur_mob; /* last mob spawned by 'M' */
struct obj_data *stack[16]; /* container stack by depth for 'P' */
};
static struct obj_data *RS_create_obj_by_vnum(obj_vnum ov) {
obj_rnum ornum;
if (ov <= 0) return NULL;
ornum = real_object(ov);
if (ornum == NOTHING) return NULL;
return read_object(ornum, REAL);
}
static struct char_data *RS_create_mob_by_vnum(mob_vnum mv) {
mob_rnum mrnum;
if (mv <= 0) return NULL;
mrnum = real_mobile(mv);
if (mrnum == NOBODY) return NULL;
return read_mobile(mrnum, REAL);
}
/* Reset the loader context before reading a new #R block */
static void RS_ctx_clear(struct rs_load_ctx *ctx) {
if (!ctx)
return;
/* DO NOT reset ctx->rnum — each #R block sets this explicitly
* before parsing mobs or objects. Resetting it causes cross-room
* bleed (e.g., mobs from one room spawning in another).
*/
ctx->cur_mob = NULL;
/* Clear all container stack pointers */
for (int i = 0; i < 16; i++)
ctx->stack[i] = NULL;
}
/* Optional autosave hook (invoked by limits.c:point_update). */
void RoomSave_autosave_tick(void) {
/* Iterate all rooms; only save flagged ones. */
for (room_rnum rnum = 0; rnum <= top_of_world; ++rnum) {
if (ROOM_FLAGGED(rnum, ROOM_SAVE))
RoomSave_now(rnum);
}
}
/* Forward decl so RS_parse_mob_line can use it without implicit declaration */
static void RS_stack_clear(struct rs_load_ctx *ctx);
/* Handle one line inside a #R block. Returns 1 if handled here. */
static int RS_parse_mob_line(struct rs_load_ctx *ctx, char *line)
{
if (!line) return 0;
while (*line == ' ' || *line == '\t') ++line;
if (!*line) return 0;
switch (line[0]) {
case 'M': {
mob_vnum mv;
if (sscanf(line+1, " %d", (int *)&mv) != 1) return 0;
ctx->cur_mob = RS_create_mob_by_vnum(mv);
if (!ctx->cur_mob) return 1;
/* Place in the block's room */
char_to_room(ctx->cur_mob, ctx->rnum);
/* Safety: if anything put it elsewhere, force it back */
if (IN_ROOM(ctx->cur_mob) != ctx->rnum)
char_to_room(ctx->cur_mob, ctx->rnum);
RS_stack_clear(ctx); /* clear only container stack */
return 1;
}
case 'E': { /* E <wear_pos> <obj_vnum> */
int pos; obj_vnum ov; struct obj_data *obj;
if (!ctx->cur_mob) return 1; /* orphan -> ignore */
if (sscanf(line+1, " %d %d", &pos, (int *)&ov) != 2) return 0;
obj = RS_create_obj_by_vnum(ov);
if (!obj) return 1;
if (pos < 0 || pos >= NUM_WEARS) pos = WEAR_HOLD; /* clamp */
equip_char(ctx->cur_mob, obj, pos);
/* Reset ONLY container stack for following P-lines; keep cur_mob */
RS_stack_clear(ctx);
ctx->stack[0] = obj;
return 1;
}
case 'G': { /* G <obj_vnum> */
obj_vnum ov; struct obj_data *obj;
if (!ctx->cur_mob) return 1; /* orphan -> ignore */
if (sscanf(line+1, " %d", (int *)&ov) != 1) return 0;
obj = RS_create_obj_by_vnum(ov);
if (!obj) return 1;
obj_to_char(obj, ctx->cur_mob);
RS_stack_clear(ctx);
ctx->stack[0] = obj;
return 1;
}
case 'P': { /* P <depth> <obj_vnum> : put into last obj at (depth-1) */
int depth; obj_vnum ov; struct obj_data *parent, *obj;
if (sscanf(line+1, " %d %d", &depth, (int *)&ov) != 2) return 0;
if (depth <= 0 || depth >= (int)(sizeof(ctx->stack)/sizeof(ctx->stack[0])))
return 1;
parent = ctx->stack[depth-1];
if (!parent) return 1;
obj = RS_create_obj_by_vnum(ov);
if (!obj) return 1;
obj_to_obj(obj, parent);
ctx->stack[depth] = obj;
{ int d; for (d = depth+1; d < (int)(sizeof(ctx->stack)/sizeof(ctx->stack[0])); ++d) ctx->stack[d] = NULL; }
return 1;
}
default:
return 0;
}
}
/* Forward decls for mob restore helpers */
static void RS_stack_clear(struct rs_load_ctx *ctx);
void RoomSave_boot(void)
{
DIR *dirp;
struct dirent *dp;
ensure_dir_exists(ROOMSAVE_PREFIX);
dirp = opendir(ROOMSAVE_PREFIX);
if (!dirp) {
mudlog(NRM, LVL_IMMORT, TRUE,
"SYSERR: RoomSave_boot: cannot open %s", ROOMSAVE_PREFIX);
return;
}
log("RoomSave: scanning %s for *.rsv", ROOMSAVE_PREFIX);
while ((dp = readdir(dirp))) {
size_t n = strlen(dp->d_name);
if (n < 5) continue; /* skip . and .. */
if (strcmp(dp->d_name + n - 4, ROOMSAVE_EXT) != 0) continue;
{
char path[PATH_MAX];
int wn = snprintf(path, sizeof(path), "%s%s", ROOMSAVE_PREFIX, dp->d_name);
if (wn < 0 || wn >= (int)sizeof(path)) {
mudlog(NRM, LVL_IMMORT, TRUE,
"SYSERR: RoomSave_boot: path too long: %s%s",
ROOMSAVE_PREFIX, dp->d_name);
continue;
}
FILE *fl = fopen(path, "r");
if (!fl) {
mudlog(NRM, LVL_IMMORT, TRUE,
"SYSERR: RoomSave_boot: fopen(%s) failed: %s",
path, strerror(errno));
continue;
}
log("RoomSave: reading %s", path);
int blocks = 0;
int restored_objs_total = 0;
int restored_mobs_total = 0;
/* Outer loop: read every #R block in this .rsv file */
char line[512];
while (fgets(line, sizeof(line), fl)) {
/* Skip until a valid #R header */
if (strncmp(line, "#R ", 3) != 0)
continue;
/* Parse header line */
int rvnum; long ts;
if (sscanf(line, "#R %d %ld", &rvnum, &ts) != 2) {
mudlog(NRM, LVL_IMMORT, TRUE,
"RoomSave: malformed #R header in %s: %s", path, line);
/* Skip malformed block */
while (fgets(line, sizeof(line), fl))
if (line[0] == '.') break;
continue;
}
blocks++;
/* Resolve the room for this block */
room_rnum rnum = real_room((room_vnum)rvnum);
if (rnum == NOWHERE) {
mudlog(NRM, LVL_IMMORT, FALSE,
"RoomSave: unknown room vnum %d in %s (skipping)",
rvnum, path);
/* Skip to next block */
while (fgets(line, sizeof(line), fl))
if (line[0] == '.') break;
continue;
}
/* Clear this room's ground contents before restoring */
while (world[rnum].contents)
extract_obj(world[rnum].contents);
/* Clear and set mob context for this block */
struct rs_load_ctx mctx;
RS_ctx_clear(&mctx);
mctx.rnum = rnum;
/* Per-block counts */
int count_objs = 0, count_mobs = 0;
char inner[512];
/* Inner loop: read this #R block until '.' */
while (fgets(inner, sizeof(inner), fl)) {
/* Trim spaces */
while (inner[0] == ' ' || inner[0] == '\t')
memmove(inner, inner + 1, strlen(inner));
/* Stop at end of block */
if (inner[0] == '.')
break;
/* Defensive: stop if another #R starts (malformed file) */
if (!strncmp(inner, "#R ", 3)) {
fseek(fl, -((long)strlen(inner)), SEEK_CUR);
break;
}
/* Handle object blocks */
if (inner[0] == 'O') {
long pos = ftell(fl);
fseek(fl, pos - strlen(inner), SEEK_SET);
struct obj_data *list = roomsave_read_list(fl);
for (struct obj_data *it = list, *next; it; it = next) {
next = it->next_content;
it->next_content = NULL;
obj_to_room(it, rnum);
count_objs++;
}
continue;
}
/* Handle mob & equipment/inventory */
if (RS_parse_mob_line(&mctx, inner)) {
if (inner[0] == 'M')
count_mobs++;
continue;
}
/* Unknown token: ignore gracefully */
}
restored_objs_total += count_objs;
restored_mobs_total += count_mobs;
if (count_mobs > 0)
log("RoomSave: room %d <- %d object(s) and %d mob(s)",
rvnum, count_objs, count_mobs);
else
log("RoomSave: room %d <- %d object(s)", rvnum, count_objs);
}
log("RoomSave: finished %s (blocks=%d, objects=%d, mobs=%d)",
path, blocks, restored_objs_total, restored_mobs_total);
fclose(fl);
}
}
closedir(dirp);
}
/* ======== MOB SAVE: write NPCs and their equipment/inventory ========== */
/* Depth-aware writer for container contents under a parent object.
* Writes: P <depth> <obj_vnum>
* depth starts at 1 for direct children. */
static void RS_write_P_chain(FILE *fp, struct obj_data *parent, int depth) {
struct obj_data *c;
for (c = parent->contains; c; c = c->next_content) {
obj_vnum cv = GET_OBJ_VNUM(c);
if (cv <= 0) continue; /* skip non-proto / invalid */
fprintf(fp, "P %d %d\n", depth, (int)cv);
if (c->contains)
RS_write_P_chain(fp, c, depth + 1);
}
}
/* Writes: E <wear_pos> <obj_vnum> (then P-chain) */
static void RS_write_mob_equipment(FILE *fp, struct char_data *mob) {
int w;
for (w = 0; w < NUM_WEARS; ++w) {
struct obj_data *eq = GET_EQ(mob, w);
if (!eq) continue;
if (GET_OBJ_VNUM(eq) <= 0) continue;
fprintf(fp, "E %d %d\n", w, (int)GET_OBJ_VNUM(eq));
if (eq->contains) RS_write_P_chain(fp, eq, 1);
}
}
/* Writes: G <obj_vnum> for inventory items (then P-chain) */
static void RS_write_mob_inventory(FILE *fp, struct char_data *mob) {
struct obj_data *o;
for (o = mob->carrying; o; o = o->next_content) {
if (GET_OBJ_VNUM(o) <= 0) continue;
fprintf(fp, "G %d\n", (int)GET_OBJ_VNUM(o));
if (o->contains) RS_write_P_chain(fp, o, 1);
}
}
/* Top-level writer: for each NPC in room, emit:
* M <mob_vnum>
* [E ...]*
* [G ...]*
* (Players are ignored.) */
static void RS_write_room_mobs(FILE *out, room_rnum rnum) {
struct char_data *mob;
for (mob = world[rnum].people; mob; mob = mob->next_in_room) {
if (!IS_NPC(mob)) continue;
if (GET_MOB_VNUM(mob) <= 0) continue;
fprintf(out, "M %d\n", (int)GET_MOB_VNUM(mob));
RS_write_mob_equipment(out, mob);
RS_write_mob_inventory(out, mob);
}
}
/* Clear only the container stack, NOT cur_mob */
static void RS_stack_clear(struct rs_load_ctx *ctx) {
int i;
for (i = 0; i < (int)(sizeof(ctx->stack)/sizeof(ctx->stack[0])); ++i)
ctx->stack[i] = NULL;
}

View file

@ -31,6 +31,7 @@ static void sedit_shop_flags_menu(struct descriptor_data *d);
static void sedit_no_trade_menu(struct descriptor_data *d);
static void sedit_types_menu(struct descriptor_data *d);
static void sedit_disp_menu(struct descriptor_data *d);
static void format_notrade_classes(bitvector_t flags, char *out, size_t outsz);
void sedit_save_internally(struct descriptor_data *d)
@ -44,6 +45,34 @@ static void sedit_save_to_disk(int num)
save_shops(num);
}
static void format_notrade_classes(bitvector_t flags, char *out, size_t outsz)
{
size_t len = 0;
int i, found = 0;
if (!out || outsz == 0)
return;
out[0] = '\0';
for (i = TRADE_CLASS_START; i < NUM_TRADERS; i++) {
if (IS_SET(flags, 1 << i)) {
int n = snprintf(out + len, outsz - len, "%s%s", found ? " " : "", trade_letters[i]);
if (n < 0 || (size_t)n >= outsz - len) {
out[outsz - 1] = '\0';
return;
}
len += (size_t)n;
found = 1;
}
}
if (!found)
strlcpy(out, "NOBITS", outsz);
}
/* utility functions */
ACMD(do_oasis_sedit)
{
@ -346,11 +375,12 @@ static void sedit_no_trade_menu(struct descriptor_data *d)
get_char_colors(d->character);
clear_screen(d);
for (i = 0; i < NUM_TRADERS; i++) {
write_to_output(d, "%s%2d%s) %-20.20s %s", grn, i + 1, nrm, trade_letters[i],
for (i = 0; i < NUM_TRADE_CLASSES; i++) {
write_to_output(d, "%s%2d%s) %-20.20s %s", grn, i + 1, nrm,
trade_letters[TRADE_CLASS_START + i],
!(++count % 2) ? "\r\n" : "");
}
sprintbit(S_NOTRADE(OLC_SHOP(d)), trade_letters, bits, sizeof(bits));
format_notrade_classes(S_NOTRADE(OLC_SHOP(d)), bits, sizeof(bits));
write_to_output(d, "\r\nCurrently won't trade with: %s%s%s\r\n"
"Enter choice : ", cyn, bits, nrm);
OLC_MODE(d) = SEDIT_NOTRADE;
@ -382,7 +412,7 @@ static void sedit_disp_menu(struct descriptor_data *d)
get_char_colors(d->character);
clear_screen(d);
sprintbit(S_NOTRADE(shop), trade_letters, buf1, sizeof(buf1));
format_notrade_classes(S_NOTRADE(shop), buf1, sizeof(buf1));
sprintbit(S_BITVECTOR(shop), shop_bits, buf2, sizeof(buf2));
write_to_output(d,
"-- Shop Number : [%s%d%s]\r\n"
@ -756,8 +786,8 @@ void sedit_parse(struct descriptor_data *d, char *arg)
}
break;
case SEDIT_NOTRADE:
if ((i = LIMIT(atoi(arg), 0, NUM_TRADERS)) > 0) {
TOGGLE_BIT(S_NOTRADE(OLC_SHOP(d)), 1 << (i - 1));
if ((i = LIMIT(atoi(arg), 0, NUM_TRADE_CLASSES)) > 0) {
TOGGLE_BIT(S_NOTRADE(OLC_SHOP(d)), 1 << (TRADE_CLASS_START + i - 1));
sedit_no_trade_menu(d);
return;
}

3708
src/set.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,21 +1,24 @@
/**
* @file roomsave.h
* Core structures used within the core mud code.
*
* An addition to the core tbaMUD source code distribution, which is a derivative
* of, and continuation of, CircleMUD.
*
* All rights reserved. See license for complete information.
* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University
* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.
* @file set.h
* Builder room/object creation and utility headers.
*
* This set of code was not originally part of the circlemud distribution.
*/
#ifndef ROOMSAVE_H_
#define ROOMSAVE_H_
#include "conf.h"
#include "sysdep.h"
#ifndef SET_H
#define SET_H
#include "structs.h"
ACMD(do_rset);
ACMD(do_rcreate);
ACMD(do_ocreate);
ACMD(do_mcreate);
ACMD(do_oset);
ACMD(do_osave);
ACMD(do_msave);
ACMD(do_rsave);
/* Boot-time loader: scans ROOMSAVE_DIR and restores contents into rooms. */
void RoomSave_boot(void);
@ -32,4 +35,4 @@ void RoomSave_mark_dirty_room(room_rnum rnum);
/* For container edits: find the room an object ultimately lives in */
room_rnum RoomSave_room_of_obj(struct obj_data *obj);
#endif /* ROOMSAVE_H_ */
#endif

View file

@ -29,9 +29,9 @@
/* Global variables definitions used externally */
/* Constant list for printing out who we sell to */
const char *trade_letters[] = {
"Good", /* First, the alignment based ones */
"Evil",
"Neutral",
"RESERVED1",
"RESERVED2",
"RESERVED3",
"Sorceror", /* Then the class based ones */
"Cleric",
"Rogue",
@ -119,13 +119,6 @@ static int is_ok_char(struct char_data *keeper, struct char_data *ch, int shop_n
if (IS_GOD(ch))
return (TRUE);
if ((IS_GOOD(ch) && NOTRADE_GOOD(shop_nr)) ||
(IS_EVIL(ch) && NOTRADE_EVIL(shop_nr)) ||
(IS_NEUTRAL(ch) && NOTRADE_NEUTRAL(shop_nr))) {
snprintf(buf, sizeof(buf), "%s %s", GET_NAME(ch), MSG_NO_SELL_ALIGN);
do_tell(keeper, buf, cmd_tell, 0);
return (FALSE);
}
if (IS_NPC(ch))
return (TRUE);
@ -1361,7 +1354,7 @@ void assign_the_shopkeepers(void)
static char *customer_string(int shop_nr, int detailed)
{
int sindex = 0, flag = 1, nlen;
int sindex = TRADE_CLASS_START, flag = (1 << TRADE_CLASS_START), nlen;
size_t len = 0;
static char buf[256];
@ -1433,7 +1426,7 @@ static void list_all_shops(struct char_data *ch)
static void list_detailed_shop(struct char_data *ch, int shop_nr)
{
struct char_data *k;
int sindex, column, flag = 1, found = 0;
int sindex, column, flag = (1 << TRADE_CLASS_START), found = 0;
/* char *ptrsave; */
send_to_char(ch, "Vnum: [%5d], Rnum: [%5d]\r\n", SHOP_NUM(shop_nr), shop_nr + 1);
@ -1484,7 +1477,7 @@ static void list_detailed_shop(struct char_data *ch, int shop_nr)
/* send_to_char(ch, "Customers: %s\r\n", (ptrsave = customer_string(shop_nr, TRUE)) ? ptrsave : "None"); */
send_to_char(ch, "Customers: ");
column = 12; /* ^^^ strlen ^^^ */
for (sindex = 0; *trade_letters[sindex] != '\n'; sindex++) {
for (sindex = TRADE_CLASS_START; *trade_letters[sindex] != '\n'; sindex++) {
char buf1[128];
int linelen;

View file

@ -74,9 +74,9 @@ struct shop_data {
#define LIST_ROOM 2
/* Whom will we not trade with (bitvector for SHOP_TRADE_WITH()) */
#define TRADE_NOGOOD (1 << 0)
#define TRADE_NOEVIL (1 << 1)
#define TRADE_NONEUTRAL (1 << 2)
#define TRADE_RESERVED1 (1 << 0)
#define TRADE_RESERVED2 (1 << 1)
#define TRADE_RESERVED3 (1 << 2)
#define TRADE_NOSORCEROR (1 << 3)
#define TRADE_NOCLERIC (1 << 4)
#define TRADE_NOROGUE (1 << 5)
@ -87,6 +87,9 @@ struct shop_data {
#define TRADE_NODRUID (1 << 10)
/** Total number of trade types */
#define NUM_TRADERS 11
#define TRADE_RESERVED_COUNT 3
#define TRADE_CLASS_START TRADE_RESERVED_COUNT
#define NUM_TRADE_CLASSES (NUM_TRADERS - TRADE_RESERVED_COUNT)
struct stack_data {
int data[100];
@ -123,9 +126,6 @@ struct stack_data {
#define SHOP_SELLPROFIT(i) (shop_index[(i)].profit_sell)
#define SHOP_FUNC(i) (shop_index[(i)].func)
#define NOTRADE_GOOD(i) (IS_SET(SHOP_TRADE_WITH((i)), TRADE_NOGOOD))
#define NOTRADE_EVIL(i) (IS_SET(SHOP_TRADE_WITH((i)), TRADE_NOEVIL))
#define NOTRADE_NEUTRAL(i) (IS_SET(SHOP_TRADE_WITH((i)), TRADE_NONEUTRAL))
#define NOTRADE_SORCEROR(i) (IS_SET(SHOP_TRADE_WITH((i)), TRADE_NOSORCEROR))
#define NOTRADE_CLERIC(i) (IS_SET(SHOP_TRADE_WITH((i)), TRADE_NOCLERIC))
#define NOTRADE_ROGUE(i) (IS_SET(SHOP_TRADE_WITH((i)), TRADE_NOROGUE))
@ -153,7 +153,6 @@ struct stack_data {
#define MSG_CLOSED_FOR_DAY "Sorry, come back tomorrow."
#define MSG_NO_STEAL_HERE "$n is a bloody thief!"
#define MSG_NO_SEE_CHAR "I don't trade with someone I can't see!"
#define MSG_NO_SELL_ALIGN "Get out of here before I call the guards!"
#define MSG_NO_SELL_CLASS "We don't serve your kind here!"
#define MSG_NO_USED_WANDSTAFF "I don't buy used up wands or staves!"
#define MSG_CANT_KILL_KEEPER "Get out of here before I call the guards!"

View file

@ -81,22 +81,6 @@ static const char *how_good(int percent)
return " (superb)";
}
static const char *prac_types[] = {
"spell",
"skill"
};
/* TO-DO: Dig deeper and figure out if the min/max practice defines can be removed */
#define LEARNED_LEVEL 0 /* % known which is considered "learned" */
#define MAX_PER_PRAC 1 /* max percent gain in skill per practice */
#define MIN_PER_PRAC 2 /* min percent gain in skill per practice */
#define PRAC_TYPE 3 /* should it say 'spell' or 'skill'? */
#define LEARNED(ch) (prac_params[LEARNED_LEVEL][(int)GET_CLASS(ch)])
#define MINGAIN(ch) (prac_params[MIN_PER_PRAC][(int)GET_CLASS(ch)])
#define MAXGAIN(ch) (prac_params[MAX_PER_PRAC][(int)GET_CLASS(ch)])
#define SPLSKL(ch) (prac_types[prac_params[PRAC_TYPE][(int)GET_CLASS(ch)]])
void list_skills(struct char_data *ch)
{
const char *overflow = "\r\n**OVERFLOW**\r\n";
@ -104,7 +88,7 @@ void list_skills(struct char_data *ch)
size_t len = 0;
char buf2[MAX_STRING_LENGTH];
len = snprintf(buf2, sizeof(buf2), "You know of the following %ss:\r\n", SPLSKL(ch));
len = snprintf(buf2, sizeof(buf2), "You know of the following skills:\r\n");
if (IS_NPC(ch)) {
/* NPCs: show only the skills actually assigned to them */
@ -229,7 +213,7 @@ SPECIAL(mayor)
break;
case 'E':
act("$n says 'I hereby declare Midgaard closed!'", FALSE, ch, 0, 0, TO_ROOM);
act("$n says 'I hereby declare the city closed!'", FALSE, ch, 0, 0, TO_ROOM);
break;
case 'O':
@ -335,13 +319,6 @@ SPECIAL(sorceror)
if (GET_LEVEL(ch) > 7 && rand_number(0, 8) == 0)
cast_spell(ch, vict, NULL, SPELL_BLINDNESS);
if (GET_LEVEL(ch) > 12 && rand_number(0, 12) == 0) {
if (IS_EVIL(ch))
cast_spell(ch, vict, NULL, SPELL_ENERGY_DRAIN);
else if (IS_GOOD(ch))
cast_spell(ch, vict, NULL, SPELL_DISPEL_EVIL);
}
if (rand_number(0, 4))
return (TRUE);
@ -504,18 +481,6 @@ SPECIAL(cityguard)
for (tch = world[IN_ROOM(ch)].people; tch; tch = tch->next_in_room) {
if (!CAN_SEE(ch, tch))
continue;
if (!IS_NPC(tch) && PLR_FLAGGED(tch, PLR_KILLER)) {
act("$n screams 'HEY!!! You're one of those PLAYER KILLERS!!!!!!'", FALSE, ch, 0, 0, TO_ROOM);
hit(ch, tch, TYPE_UNDEFINED);
return (TRUE);
}
if (!IS_NPC(tch) && PLR_FLAGGED(tch, PLR_THIEF)) {
act("$n screams 'HEY!!! You're one of those PLAYER THIEVES!!!!!!'", FALSE, ch, 0, 0, TO_ROOM);
hit(ch, tch, TYPE_UNDEFINED);
return (TRUE);
}
if (FIGHTING(tch) && GET_ALIGNMENT(tch) < max_evil && (IS_NPC(tch) || IS_NPC(FIGHTING(tch)))) {
max_evil = GET_ALIGNMENT(tch);
evil = tch;

510
src/species.c Normal file
View file

@ -0,0 +1,510 @@
/**
* @file species.c
* Race/species related configuration, skill bonuses, modifiers, and stat limitations.
*
* This set of code was not originally part of the circlemud distribution.
*/
#include "conf.h"
#include "sysdep.h"
#include "structs.h"
#include "utils.h"
#include "db.h"
#include "spells.h"
#include "species.h"
struct species_skill_bonus {
int skill;
int start;
};
/* Keep species ordering in sync with the SPECIES_* defines. */
const char *species_types[NUM_SPECIES] = {
"Human",
"City Elf",
"Desert Elf",
"Half-Elf",
"Dwarf",
"Mul",
"Half-Giant",
"Mantis",
"Gith",
"Aarakocra",
"Dray",
"Kenku",
"Jozhal",
"Pterran",
"Tarek",
"Aprig",
"Carru",
"Crodlu",
"Erdlu",
"Inix",
"Jhakar",
"Kank",
"Mekillot",
"Worm",
"Renk",
"Rat",
"Undead",
"Dragon"
};
/* PC creation options (1-based ordering in menus). */
const int pc_species_list[] = {
SPECIES_HUMAN,
SPECIES_CITY_ELF,
SPECIES_DESERT_ELF,
SPECIES_HALF_ELF,
SPECIES_DWARF,
SPECIES_MUL,
SPECIES_HALF_GIANT,
SPECIES_MANTIS,
SPECIES_GITH,
-1
};
struct species_base_points {
int hit;
int mana;
int stamina;
};
static const struct species_base_points species_base_points[NUM_SPECIES] = {
{ 100, 90, 100 }, /* Human */
{ 90, 110, 90 }, /* City Elf */
{ 100, 80, 140 }, /* Desert Elf */
{ 95, 90, 95 }, /* Half-Elf */
{ 110, 75, 110 }, /* Dwarf */
{ 110, 80, 110 }, /* Mul */
{ 180, 50, 180 }, /* Half-Giant */
{ 90, 90, 90 }, /* Mantis */
{ 90, 100, 90 }, /* Gith */
{ 90, 90, 90 }, /* Aarakocra */
{ 100, 100, 100 }, /* Dray */
{ 70, 120, 80 }, /* Kenku */
{ 60, 10, 80 }, /* Jozhal */
{ 120, 10, 120 }, /* Pterran */
{ 100, 60, 100 }, /* Tarek */
{ 60, 10, 80 }, /* Aprig */
{ 100, 10, 100 }, /* Carru */
{ 100, 10, 80 }, /* Crodlu */
{ 80, 10, 200 }, /* Erdlu */
{ 120, 10, 400 }, /* Inix */
{ 75, 10, 75 }, /* Jhakar */
{ 100, 10, 300 }, /* Kank */
{ 200, 10, 150 }, /* Mekillot */
{ 100, 10, 150 }, /* Worm */
{ 10, 10, 20 }, /* Renk */
{ 10, 10, 20 }, /* Rat */
{ 110, 110, 110 }, /* Undead */
{ 250, 250, 250 } /* Dragon */
};
static const struct species_skill_bonus species_skill_none[] = {
{ -1, 0 }
};
/* Skill percent values map to proficiency tiers via GET_PROFICIENCY(). */
static const struct species_skill_bonus species_skill_crodlu[] = {
{ SKILL_PERCEPTION, 45 }, /* +3 proficiency */
{ -1, 0 }
};
static const struct species_skill_bonus species_skill_jhakar[] = {
{ SKILL_PERCEPTION, 45 }, /* +3 proficiency */
{ SKILL_STEALTH, 60 }, /* +4 proficiency */
{ -1, 0 }
};
/* Per-species skill bonuses.
* Usage:
* 1) Define a bonus list for a species (see commented example below).
* 2) In species_skill_bonuses[], point that species at the new list.
* 3) Keep the { -1, 0 } terminator as the final entry.
*
* Example (commented out):
*
* static const struct species_skill_bonus species_skill_human[] = {
* { SKILL_PERCEPTION, 5 },
* { -1, 0 }
* };
*/
static const struct species_skill_bonus *species_skill_bonuses[NUM_SPECIES] = {
/* species_skill_human, */ /* Human (example if you enable the list above) */
species_skill_none, /* Human */
species_skill_none, /* City Elf */
species_skill_none, /* Desert Elf */
species_skill_none, /* Half-Elf */
species_skill_none, /* Dwarf */
species_skill_none, /* Mul */
species_skill_none, /* Half-Giant */
species_skill_none, /* Mantis */
species_skill_none, /* Gith */
species_skill_none, /* Aarakocra */
species_skill_none, /* Dray */
species_skill_none, /* Kenku */
species_skill_none, /* Jozhal */
species_skill_none, /* Pterran */
species_skill_none, /* Tarek */
species_skill_none, /* Aprig */
species_skill_none, /* Carru */
species_skill_crodlu, /* Crodlu */
species_skill_none, /* Erdlu */
species_skill_none, /* Inix */
species_skill_jhakar, /* Jhakar */
species_skill_none, /* Kank */
species_skill_none, /* Mekillot */
species_skill_none, /* Worm */
species_skill_none, /* Renk */
species_skill_none, /* Rat */
species_skill_none, /* Undead */
species_skill_none /* Dragon */
};
/* Ability minimums by species (STR, DEX, CON, INT, WIS, CHA). Zero = no min. */
static const int species_ability_mins[NUM_SPECIES][NUM_ABILITIES] = {
{ 0, 0, 0, 0, 0, 0 }, /* Human */
{ 0, 0, 0, 0, 0, 0 }, /* City Elf */
{ 0, 0, 0, 0, 0, 0 }, /* Desert Elf */
{ 0, 0, 0, 0, 0, 0 }, /* Half-Elf */
{ 0, 0, 0, 0, 0, 0 }, /* Dwarf */
{ 0, 0, 0, 0, 0, 0 }, /* Mul */
{ 0, 0, 0, 0, 0, 0 }, /* Half-Giant */
{ 0, 0, 0, 0, 0, 0 }, /* Mantis */
{ 0, 0, 0, 0, 0, 0 }, /* Gith */
{ 0, 0, 0, 0, 0, 0 }, /* Aarakocra */
{ 0, 0, 0, 0, 0, 0 }, /* Dray */
{ 0, 0, 0, 0, 0, 0 }, /* Kenku */
{ 0, 0, 0, 0, 0, 0 }, /* Jozhal */
{ 0, 0, 0, 0, 0, 0 }, /* Pterran */
{ 0, 0, 0, 0, 0, 0 }, /* Tarek */
{ 0, 0, 0, 0, 0, 0 }, /* Aprig */
{ 0, 0, 0, 0, 0, 0 }, /* Carru */
{ 0, 0, 0, 0, 0, 0 }, /* Crodlu */
{ 0, 0, 0, 0, 0, 0 }, /* Erdlu */
{ 0, 0, 0, 0, 0, 0 }, /* Inix */
{ 0, 0, 0, 0, 0, 0 }, /* Jhakar */
{ 0, 0, 0, 0, 0, 0 }, /* Kank */
{ 0, 0, 0, 0, 0, 0 }, /* Mekillot */
{ 0, 0, 0, 0, 0, 0 }, /* Worm */
{ 0, 0, 0, 0, 0, 0 }, /* Renk */
{ 0, 0, 0, 0, 0, 0 }, /* Rat */
{ 0, 0, 0, 0, 0, 0 }, /* Undead */
{ 0, 0, 0, 0, 0, 0 } /* Dragon */
};
/* Ability modifiers by species (STR, DEX, CON, INT, WIS, CHA). */
static const int species_ability_mods[NUM_SPECIES][NUM_ABILITIES] = {
{ 0, 0, 0, 0, 0, 0 }, /* Human */
{ 0, 0, 0, 0, 0, 0 }, /* City Elf */
{ 0, 0, 0, 0, 0, 0 }, /* Desert Elf */
{ 0, 0, 0, 0, 0, 0 }, /* Half-Elf */
{ 0, 0, 0, 0, 0, 0 }, /* Dwarf */
{ 0, 0, 0, 0, 0, 0 }, /* Mul */
{ 0, 0, 0, 0, 0, 0 }, /* Half-Giant */
{ 0, 0, 0, 0, 0, 0 }, /* Mantis */
{ 0, 0, 0, 0, 0, 0 }, /* Gith */
{ 0, 0, 0, 0, 0, 0 }, /* Aarakocra */
{ 0, 0, 0, 0, 0, 0 }, /* Dray */
{ 0, 0, 0, 0, 0, 0 }, /* Kenku */
{ 0, 0, 0, 0, 0, 0 }, /* Jozhal */
{ 0, 0, 0, 0, 0, 0 }, /* Pterran */
{ 0, 0, 0, 0, 0, 0 }, /* Tarek */
{ 0, 0, 0, 0, 0, 0 }, /* Aprig */
{ 0, 0, 0, 0, 0, 0 }, /* Carru */
{ 0, 0, 0, 0, 0, 0 }, /* Crodlu */
{ 0, 0, 0, 0, 0, 0 }, /* Erdlu */
{ 0, 0, 0, 0, 0, 0 }, /* Inix */
{ 0, 0, 0, 0, 0, 0 }, /* Jhakar */
{ 0, 0, 0, 0, 0, 0 }, /* Kank */
{ 0, 0, 0, 0, 0, 0 }, /* Mekillot */
{ 0, 0, 0, 0, 0, 0 }, /* Worm */
{ 0, 0, 0, 0, 0, 0 }, /* Renk */
{ 0, 0, 0, 0, 0, 0 }, /* Rat */
{ 0, 0, 0, 0, 0, 0 }, /* Undead */
{ 0, 0, 0, 0, 0, 0 } /* Dragon */
};
/* Ability caps by species (STR, DEX, CON, INT, WIS, CHA). Zero = no cap. */
static const int species_ability_caps[NUM_SPECIES][NUM_ABILITIES] = {
{ 14, 14, 14, 14, 14, 14 }, /* Human */
{ 10, 18, 10, 14, 16, 14 }, /* City Elf */
{ 12, 18, 12, 12, 14, 12 }, /* Desert Elf */
{ 13, 16, 13, 13, 15, 10 }, /* Half-Elf */
{ 18, 10, 18, 10, 10, 10 }, /* Dwarf */
{ 20, 14, 18, 10, 10, 8 }, /* Mul */
{ 24, 8, 20, 6, 6, 6 }, /* Half-Giant */
{ 14, 14, 16, 10, 12, 6 }, /* Mantis */
{ 12, 16, 14, 10, 8, 6 }, /* Gith */
{ 13, 16, 14, 13, 13, 13 }, /* Aarakocra */
{ 15, 13, 15, 12, 12, 14 }, /* Dray */
{ 12, 16, 12, 15, 15, 13 }, /* Kenku */
{ 8, 10, 8, 8, 8, 6 }, /* Jozhal */
{ 15, 12, 15, 15, 12, 10 }, /* Pterran */
{ 17, 13, 18, 10, 10, 6 }, /* Tarek */
{ 8, 10, 12, 2, 10, 5 }, /* Aprig */
{ 21, 8, 15, 2, 12, 6 }, /* Carru */
{ 16, 15, 14, 4, 12, 6 }, /* Crodlu */
{ 16, 10, 12, 2, 11, 7 }, /* Erdlu */
{ 22, 12, 18, 2, 10, 7 }, /* Inix */
{ 17, 15, 16, 3, 12, 7 }, /* Jhakar */
{ 18, 10, 14, 2, 10, 4 }, /* Kank */
{ 24, 9, 21, 3, 11, 6 }, /* Mekillot */
{ 22, 8, 19, 2, 2, 6 }, /* Worm */
{ 2, 4, 10, 4, 10, 4 }, /* Renk */
{ 2, 11, 9, 2, 10, 4 }, /* Rat */
{ 18, 16, 18, 16, 16, 16 }, /* Undead */
{ 25, 25, 25, 25, 25, 25 } /* Dragon */
};
const char *get_species_name(int species)
{
if (species >= 0 && species < NUM_SPECIES)
return species_types[species];
return "Unassigned";
}
int pc_species_count(void)
{
int count = 0;
while (pc_species_list[count] != -1)
count++;
return count;
}
bool species_is_pc_selectable(int species)
{
int i = 0;
while (pc_species_list[i] != -1) {
if (pc_species_list[i] == species)
return TRUE;
i++;
}
return FALSE;
}
int species_from_pc_choice(int choice)
{
int count = pc_species_count();
if (choice < 1 || choice > count)
return SPECIES_UNDEFINED;
return pc_species_list[choice - 1];
}
int species_ability_mod(int species, int ability)
{
if (species < 0 || species >= NUM_SPECIES)
return 0;
if (ability < 0 || ability >= NUM_ABILITIES)
return 0;
return species_ability_mods[species][ability];
}
int species_ability_min(int species, int ability)
{
if (species < 0 || species >= NUM_SPECIES)
return 0;
if (ability < 0 || ability >= NUM_ABILITIES)
return 0;
return species_ability_mins[species][ability];
}
int species_ability_cap(int species, int ability)
{
if (species < 0 || species >= NUM_SPECIES)
return 0;
if (ability < 0 || ability >= NUM_ABILITIES)
return 0;
return species_ability_caps[species][ability];
}
bool get_species_base_points(int species, int *hit, int *mana, int *stamina)
{
if (species < 0 || species >= NUM_SPECIES)
return FALSE;
if (hit)
*hit = species_base_points[species].hit;
if (mana)
*mana = species_base_points[species].mana;
if (stamina)
*stamina = species_base_points[species].stamina;
return TRUE;
}
static void apply_species_ranges(struct char_data *ch)
{
int species;
int cap, min;
if (!ch)
return;
species = GET_SPECIES(ch);
if (species < 0 || species >= NUM_SPECIES)
return;
min = species_ability_min(species, ABIL_STR);
if (min > 0 && ch->real_abils.str < min)
ch->real_abils.str = min;
cap = species_ability_cap(species, ABIL_STR);
if (cap > 0 && ch->real_abils.str > cap)
ch->real_abils.str = cap;
min = species_ability_min(species, ABIL_DEX);
if (min > 0 && ch->real_abils.dex < min)
ch->real_abils.dex = min;
cap = species_ability_cap(species, ABIL_DEX);
if (cap > 0 && ch->real_abils.dex > cap)
ch->real_abils.dex = cap;
min = species_ability_min(species, ABIL_CON);
if (min > 0 && ch->real_abils.con < min)
ch->real_abils.con = min;
cap = species_ability_cap(species, ABIL_CON);
if (cap > 0 && ch->real_abils.con > cap)
ch->real_abils.con = cap;
min = species_ability_min(species, ABIL_INT);
if (min > 0 && ch->real_abils.intel < min)
ch->real_abils.intel = min;
cap = species_ability_cap(species, ABIL_INT);
if (cap > 0 && ch->real_abils.intel > cap)
ch->real_abils.intel = cap;
min = species_ability_min(species, ABIL_WIS);
if (min > 0 && ch->real_abils.wis < min)
ch->real_abils.wis = min;
cap = species_ability_cap(species, ABIL_WIS);
if (cap > 0 && ch->real_abils.wis > cap)
ch->real_abils.wis = cap;
min = species_ability_min(species, ABIL_CHA);
if (min > 0 && ch->real_abils.cha < min)
ch->real_abils.cha = min;
cap = species_ability_cap(species, ABIL_CHA);
if (cap > 0 && ch->real_abils.cha > cap)
ch->real_abils.cha = cap;
}
void apply_species_bonuses(struct char_data *ch)
{
int species;
if (!ch)
return;
species = GET_SPECIES(ch);
if (species < 0 || species >= NUM_SPECIES)
return;
apply_species_ranges(ch);
ch->real_abils.str += species_ability_mod(species, ABIL_STR);
ch->real_abils.dex += species_ability_mod(species, ABIL_DEX);
ch->real_abils.con += species_ability_mod(species, ABIL_CON);
ch->real_abils.intel += species_ability_mod(species, ABIL_INT);
ch->real_abils.wis += species_ability_mod(species, ABIL_WIS);
ch->real_abils.cha += species_ability_mod(species, ABIL_CHA);
apply_species_ranges(ch);
ch->aff_abils = ch->real_abils;
}
void grant_species_skills(struct char_data *ch)
{
int species;
const struct species_skill_bonus *bonus;
if (!ch)
return;
species = GET_SPECIES(ch);
if (species < 0 || species >= NUM_SPECIES)
return;
for (bonus = species_skill_bonuses[species]; bonus->skill != -1; bonus++) {
if (bonus->skill >= 0 && bonus->skill <= MAX_SKILLS) {
if (GET_SKILL(ch, bonus->skill) < bonus->start)
SET_SKILL(ch, bonus->skill, bonus->start);
}
}
}
static void remove_species_skills(struct char_data *ch, int species)
{
const struct species_skill_bonus *bonus;
if (!ch)
return;
if (species < 0 || species >= NUM_SPECIES)
return;
for (bonus = species_skill_bonuses[species]; bonus->skill != -1; bonus++) {
if (bonus->skill >= 0 && bonus->skill <= MAX_SKILLS) {
if (GET_SKILL(ch, bonus->skill) <= bonus->start)
SET_SKILL(ch, bonus->skill, 0);
}
}
}
static void remove_species_bonuses(struct char_data *ch, int species)
{
if (!ch)
return;
if (species < 0 || species >= NUM_SPECIES)
return;
ch->real_abils.str -= species_ability_mod(species, ABIL_STR);
ch->real_abils.dex -= species_ability_mod(species, ABIL_DEX);
ch->real_abils.con -= species_ability_mod(species, ABIL_CON);
ch->real_abils.intel -= species_ability_mod(species, ABIL_INT);
ch->real_abils.wis -= species_ability_mod(species, ABIL_WIS);
ch->real_abils.cha -= species_ability_mod(species, ABIL_CHA);
}
int parse_species(const char *arg)
{
int i;
if (!arg || !*arg)
return SPECIES_UNDEFINED;
for (i = 0; i < NUM_SPECIES; i++) {
if (!str_cmp(arg, species_types[i]))
return i;
}
return SPECIES_UNDEFINED;
}
void update_species(struct char_data *ch, int new_species)
{
int old_species;
if (!ch)
return;
old_species = GET_SPECIES(ch);
if (old_species == new_species)
return;
if (old_species >= 0 && old_species < NUM_SPECIES) {
remove_species_skills(ch, old_species);
remove_species_bonuses(ch, old_species);
}
GET_SPECIES(ch) = new_species;
if (new_species >= 0 && new_species < NUM_SPECIES) {
apply_species_bonuses(ch);
grant_species_skills(ch);
} else {
ch->aff_abils = ch->real_abils;
}
}

30
src/species.h Normal file
View file

@ -0,0 +1,30 @@
/**
* @file species.h
* Race/species related header.
*
* This set of code was not originally part of the circlemud distribution.
*/
#ifndef _SPECIES_H_
#define _SPECIES_H_
struct char_data;
const char *get_species_name(int species);
int species_from_pc_choice(int choice);
int pc_species_count(void);
bool species_is_pc_selectable(int species);
int species_ability_mod(int species, int ability);
int species_ability_min(int species, int ability);
int species_ability_cap(int species, int ability);
bool get_species_base_points(int species, int *hit, int *mana, int *stamina);
void apply_species_bonuses(struct char_data *ch);
void grant_species_skills(struct char_data *ch);
int parse_species(const char *arg);
void update_species(struct char_data *ch, int new_species);
extern const char *species_types[];
extern const int pc_species_list[];
#endif /* _SPECIES_H_ */

View file

@ -795,9 +795,6 @@ void mag_assign_spells(void) {
spello(SPELL_DARKNESS, "darkness", 30, 5, 4, POS_STANDING,
TAR_IGNORE, FALSE, MAG_ROOMS, NULL);
spello(SPELL_DETECT_ALIGN, "detect alignment", 20, 10, 2, POS_STANDING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_AFFECTS, "You feel less aware.");
spello(SPELL_DETECT_INVIS, "detect invisibility", 20, 10, 2, POS_STANDING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_AFFECTS,
"Your eyes stop tingling.");
@ -810,12 +807,6 @@ void mag_assign_spells(void) {
TAR_CHAR_ROOM | TAR_OBJ_INV | TAR_OBJ_ROOM, FALSE, MAG_MANUAL,
"The detect poison wears off.");
spello(SPELL_DISPEL_EVIL, "dispel evil", 40, 25, 3, POS_FIGHTING,
TAR_CHAR_ROOM | TAR_FIGHT_VICT, TRUE, MAG_DAMAGE, NULL);
spello(SPELL_DISPEL_GOOD, "dispel good", 40, 25, 3, POS_FIGHTING,
TAR_CHAR_ROOM | TAR_FIGHT_VICT, TRUE, MAG_DAMAGE, NULL);
spello(SPELL_EARTHQUAKE, "earthquake", 40, 25, 3, POS_FIGHTING,
TAR_IGNORE, TRUE, MAG_AREAS, NULL);

View file

@ -124,28 +124,6 @@ ASPELL(spell_summon)
return;
}
if (!CONFIG_PK_ALLOWED) {
if (MOB_FLAGGED(victim, MOB_AGGRESSIVE)) {
act("As the words escape your lips and $N travels\r\n"
"through time and space towards you, you realize that $E is\r\n"
"aggressive and might harm you, so you wisely send $M back.",
FALSE, ch, 0, victim, TO_CHAR);
return;
}
if (!IS_NPC(victim) && !PRF_FLAGGED(victim, PRF_SUMMONABLE) &&
!PLR_FLAGGED(victim, PLR_KILLER)) {
send_to_char(victim, "%s just tried to summon you to: %s.\r\n"
"This failed because you have summon protection on.\r\n"
"Type NOSUMMON to allow other players to summon you.\r\n",
GET_NAME(ch), world[IN_ROOM(ch)].name);
send_to_char(ch, "You failed because %s has summon protection on.\r\n", GET_NAME(victim));
mudlog(BRF, MAX(LVL_IMMORT, MAX(GET_INVIS_LEV(ch), GET_INVIS_LEV(victim))), TRUE,
"%s failed summoning %s to %s.", GET_NAME(ch), GET_NAME(victim), world[IN_ROOM(ch)].name);
return;
}
}
if (MOB_FLAGGED(victim, MOB_NOSUMMON) ||
(IS_NPC(victim) && mag_savingthrow(victim, SAVING_CHA, save_dc))) {
send_to_char(ch, "%s", SUMMON_FAIL);
@ -272,8 +250,6 @@ ASPELL(spell_charm)
else if (AFF_FLAGGED(victim, AFF_CHARM) || level < GET_LEVEL(victim))
send_to_char(ch, "You fail.\r\n");
/* player charming another player - no legal reason for this */
else if (!CONFIG_PK_ALLOWED && !IS_NPC(victim))
send_to_char(ch, "You fail - shouldn't be doing it anyway.\r\n");
else if (circle_follow(victim, ch))
send_to_char(ch, "Sorry, following in circles is not allowed.\r\n");
else if (mag_savingthrow(victim, SAVING_WIS, save_dc))
@ -412,14 +388,7 @@ ASPELL(spell_enchant_weapon)
obj->affected[1].location = APPLY_PROFICIENCY;
obj->affected[1].modifier = 1 + (level >= 20);
if (IS_GOOD(ch)) {
SET_BIT_AR(GET_OBJ_EXTRA(obj), ITEM_ANTI_EVIL);
act("$p glows blue.", FALSE, ch, obj, 0, TO_CHAR);
} else if (IS_EVIL(ch)) {
SET_BIT_AR(GET_OBJ_EXTRA(obj), ITEM_ANTI_GOOD);
act("$p glows red.", FALSE, ch, obj, 0, TO_CHAR);
} else
act("$p glows yellow.", FALSE, ch, obj, 0, TO_CHAR);
act("$p glows yellow.", FALSE, ch, obj, 0, TO_CHAR);
}
ASPELL(spell_detect_poison)

View file

@ -56,45 +56,42 @@
#define SPELL_CURE_CRITIC 15 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_CURE_LIGHT 16 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_CURSE 17 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_DETECT_ALIGN 18 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_DETECT_INVIS 19 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_DETECT_MAGIC 20 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_DETECT_POISON 21 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_DISPEL_EVIL 22 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_EARTHQUAKE 23 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_ENCHANT_WEAPON 24 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_ENERGY_DRAIN 25 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_FIREBALL 26 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_HARM 27 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_HEAL 28 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_INVISIBLE 29 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_LIGHTNING_BOLT 30 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_LOCATE_OBJECT 31 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_MAGIC_MISSILE 32 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_POISON 33 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_PROT_FROM_EVIL 34 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_REMOVE_CURSE 35 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_SANCTUARY 36 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_SHOCKING_GRASP 37 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_SLEEP 38 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_STRENGTH 39 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_SUMMON 40 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_VENTRILOQUATE 41 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_WORD_OF_RECALL 42 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_REMOVE_POISON 43 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_SENSE_LIFE 44 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_ANIMATE_DEAD 45 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_DISPEL_GOOD 46 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_GROUP_ARMOR 47 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_GROUP_HEAL 48 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_GROUP_RECALL 49 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_INFRAVISION 50 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_WATERWALK 51 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_IDENTIFY 52 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_FLY 53 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_DARKNESS 54
#define SPELL_DETECT_INVIS 18 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_DETECT_MAGIC 19 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_DETECT_POISON 20 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_EARTHQUAKE 21 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_ENCHANT_WEAPON 22 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_ENERGY_DRAIN 23 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_FIREBALL 24 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_HARM 25 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_HEAL 26 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_INVISIBLE 27 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_LIGHTNING_BOLT 28 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_LOCATE_OBJECT 29 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_MAGIC_MISSILE 30 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_POISON 31 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_PROT_FROM_EVIL 32 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_REMOVE_CURSE 33 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_SANCTUARY 34 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_SHOCKING_GRASP 35 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_SLEEP 36 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_STRENGTH 37 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_SUMMON 38 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_VENTRILOQUATE 39 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_WORD_OF_RECALL 40 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_REMOVE_POISON 41 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_SENSE_LIFE 42 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_ANIMATE_DEAD 43 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_GROUP_ARMOR 44 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_GROUP_HEAL 45 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_GROUP_RECALL 46 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_INFRAVISION 47 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_WATERWALK 48 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_IDENTIFY 49 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_FLY 50 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_DARKNESS 51
/** Total Number of defined spells */
#define NUM_SPELLS 54
#define NUM_SPELLS 51
/* Insert new spells here, up to MAX_SPELLS */
#define MAX_SPELLS 130
@ -136,7 +133,7 @@
/* New skills may be added here up to MAX_SKILLS (200) */
/* NON-PLAYER AND OBJECT SPELLS AND SKILLS: The practice levels for the spells
/* NON-PLAYER AND OBJECT SPELLS AND SKILLS: The skill levels for the spells
* and skills below are _not_ recorded in the players file; therefore, the
* intended use is for spells and skills associated with objects (such as
* SPELL_IDENTIFY used with scrolls of identify) or non-players (such as NPC

View file

@ -164,6 +164,38 @@
/** Total number of available PC Classes */
#define NUM_CLASSES 8
/* Species */
#define SPECIES_UNDEFINED (-1) /**< Species undefined */
#define SPECIES_HUMAN 0
#define SPECIES_CITY_ELF 1
#define SPECIES_DESERT_ELF 2
#define SPECIES_HALF_ELF 3
#define SPECIES_DWARF 4
#define SPECIES_MUL 5
#define SPECIES_HALF_GIANT 6
#define SPECIES_MANTIS 7
#define SPECIES_GITH 8
#define SPECIES_AARAKOCRA 9
#define SPECIES_DRAY 10
#define SPECIES_KENKU 11
#define SPECIES_JOZHAL 12
#define SPECIES_PTERRAN 13
#define SPECIES_TAREK 14
#define SPECIES_APRIG 15
#define SPECIES_CARRU 16
#define SPECIES_CRODLU 17
#define SPECIES_ERDLU 18
#define SPECIES_INIX 19
#define SPECIES_JHAKAR 20
#define SPECIES_KANK 21
#define SPECIES_MEKILLOT 22
#define SPECIES_WORM 23
#define SPECIES_RENK 24
#define SPECIES_RAT 25
#define SPECIES_UNDEAD 26
#define SPECIES_DRAGON 27
#define NUM_SPECIES 28
/* NPC classes (currently unused - feel free to implement!) */
#define CLASS_OTHER 0 /**< NPC Class Other (or undefined) */
#define CLASS_UNDEAD 1 /**< NPC Class Undead */
@ -193,8 +225,8 @@
#define NUM_POSITIONS 9
/* Player flags: used by char_data.char_specials.act */
#define PLR_KILLER 0 /**< Player is a player-killer */
#define PLR_THIEF 1 /**< Player is a player-thief */
#define PLR_UNUSED0 0 /**< Reserved (unused) */
#define PLR_UNUSED1 1 /**< Reserved (unused) */
#define PLR_FROZEN 2 /**< Player is frozen */
#define PLR_DONTSET 3 /**< Don't EVER set (ISNPC bit, set by mud) */
#define PLR_WRITING 4 /**< Player writing (board/mail/olc) */
@ -223,9 +255,9 @@
#define MOB_AGGRESSIVE 5 /**< Mob auto-attacks everybody nearby */
#define MOB_STAY_ZONE 6 /**< Mob shouldn't wander out of zone */
#define MOB_WIMPY 7 /**< Mob flees if severely injured */
#define MOB_AGGR_EVIL 8 /**< Auto-attack any evil PC's */
#define MOB_AGGR_GOOD 9 /**< Auto-attack any good PC's */
#define MOB_AGGR_NEUTRAL 10 /**< Auto-attack any neutral PC's */
#define MOB_AGGR_RESERVED1 8 /**< Reserved (unused) */
#define MOB_AGGR_RESERVED2 9 /**< Reserved (unused) */
#define MOB_AGGR_RESERVED3 10 /**< Reserved (unused) */
#define MOB_MEMORY 11 /**< remember attackers if attacked */
#define MOB_HELPER 12 /**< attack PCs fighting other NPCs */
#define MOB_NOCHARM 13 /**< Mob can't be charmed */
@ -235,8 +267,9 @@
#define MOB_NOBLIND 17 /**< Mob can't be blinded */
#define MOB_NOKILL 18 /**< Mob can't be attacked */
#define MOB_NOTDEADYET 19 /**< (R) Mob being extracted */
#define MOB_MOUNT 20 /**< Mob can be mounted */
#define NUM_MOB_FLAGS 19
#define NUM_MOB_FLAGS 21
/* Preference flags: used by char_data.player_specials.pref */
#define PRF_BRIEF 0 /**< Room descs won't normally be shown */
@ -245,7 +278,7 @@
#define PRF_NOTELL 3 /**< Can't receive tells */
#define PRF_DISPHP 4 /**< Display hit points in prompt */
#define PRF_DISPMANA 5 /**< Display mana points in prompt */
#define PRF_DISPMOVE 6 /**< Display move points in prompt */
#define PRF_DISPSTAMINA 6 /**< Display stamina points in prompt */
#define PRF_AUTOEXIT 7 /**< Display exits in a room */
#define PRF_NOHASSLE 8 /**< Aggr mobs won't attack */
#define PRF_QUEST 9 /**< On quest */
@ -279,28 +312,28 @@
#define AFF_DONTUSE 0 /**< DON'T USE! This allows 0 to mean "no bits set" in the database */
#define AFF_BLIND 1 /**< (R) Char is blind */
#define AFF_INVISIBLE 2 /**< Char is invisible */
#define AFF_DETECT_ALIGN 3 /**< Char is sensitive to align */
#define AFF_DETECT_INVIS 4 /**< Char can see invis chars */
#define AFF_DETECT_MAGIC 5 /**< Char is sensitive to magic */
#define AFF_SENSE_LIFE 6 /**< Char can sense hidden life */
#define AFF_WATERWALK 7 /**< Char can walk on water */
#define AFF_SANCTUARY 8 /**< Char protected by sanct */
#define AFF_UNUSED 9 /**< (R) Char is grouped */
#define AFF_CURSE 10 /**< Char is cursed */
#define AFF_INFRAVISION 11 /**< Char can see in dark */
#define AFF_POISON 12 /**< (R) Char is poisoned */
#define AFF_PROTECT_EVIL 13 /**< Char protected from evil */
#define AFF_PROTECT_GOOD 14 /**< Char protected from good */
#define AFF_SLEEP 15 /**< (R) Char magically asleep */
#define AFF_NOTRACK 16 /**< Char can't be tracked */
#define AFF_FLYING 17 /**< Char is flying */
#define AFF_SCUBA 18 /**< Room for future expansion */
#define AFF_SNEAK 19 /**< Char can move quietly */
#define AFF_HIDE 20 /**< Char is hidden */
#define AFF_SCAN 21 /**< Actively scanning for hidden threats */
#define AFF_CHARM 22 /**< Char is charmed */
#define AFF_BANDAGED 23 /**< Character was bandaged recently */
#define AFF_LISTEN 24 /**< Actively eavesdropping */
#define AFF_DETECT_INVIS 3 /**< Char can see invis chars */
#define AFF_DETECT_MAGIC 4 /**< Char is sensitive to magic */
#define AFF_SENSE_LIFE 5 /**< Char can sense hidden life */
#define AFF_WATERWALK 6 /**< Char can walk on water */
#define AFF_SANCTUARY 7 /**< Char protected by sanct */
#define AFF_UNUSED 8 /**< (R) Char is grouped */
#define AFF_CURSE 9 /**< Char is cursed */
#define AFF_INFRAVISION 10 /**< Char can see in dark */
#define AFF_POISON 11 /**< (R) Char is poisoned */
#define AFF_RESERVED13 12 /**< Reserved (unused) */
#define AFF_RESERVED14 13 /**< Reserved (unused) */
#define AFF_SLEEP 14 /**< (R) Char magically asleep */
#define AFF_NOTRACK 15 /**< Char can't be tracked */
#define AFF_FLYING 16 /**< Char is flying */
#define AFF_SCUBA 17 /**< Room for future expansion */
#define AFF_SNEAK 18 /**< Char can move quietly */
#define AFF_HIDE 19 /**< Char is hidden */
#define AFF_SCAN 20 /**< Actively scanning for hidden threats */
#define AFF_CHARM 21 /**< Char is charmed */
#define AFF_BANDAGED 22 /**< Character was bandaged recently */
#define AFF_LISTEN 23 /**< Actively eavesdropping */
#define AFF_MOUNTED 24 /**< Riding a mount */
/** Total number of affect flags */
#define NUM_AFF_FLAGS 25
@ -313,42 +346,45 @@
#define CON_NEWPASSWD 5 /**< New character, create password */
#define CON_CNFPASSWD 6 /**< New character, confirm password */
#define CON_QSEX 7 /**< Choose character sex */
#define CON_QCLASS 8 /**< Choose character class */
#define CON_QSHORTDESC 9 /**< Enter a new character short description prompt */
#define CON_RMOTD 10 /**< Reading the message of the day */
#define CON_MENU 11 /**< At the main menu */
#define CON_PLR_DESC 12 /**< Enter a new character description prompt */
#define CON_CHPWD_GETOLD 13 /**< Changing passwd: Get old */
#define CON_CHPWD_GETNEW 14 /**< Changing passwd: Get new */
#define CON_CHPWD_VRFY 15 /**< Changing passwd: Verify new password */
#define CON_DELCNF1 16 /**< Character Delete: Confirmation 1 */
#define CON_DELCNF2 17 /**< Character Delete: Confirmation 2 */
#define CON_DISCONNECT 18 /**< In-game link loss (leave character) */
#define CON_OEDIT 19 /**< OLC mode - object editor */
#define CON_REDIT 20 /**< OLC mode - room editor */
#define CON_ZEDIT 21 /**< OLC mode - zone info editor */
#define CON_MEDIT 22 /**< OLC mode - mobile editor */
#define CON_SEDIT 23 /**< OLC mode - shop editor */
#define CON_TEDIT 24 /**< OLC mode - text editor */
#define CON_CEDIT 25 /**< OLC mode - conf editor */
#define CON_AEDIT 26 /**< OLC mode - social (action) edit */
#define CON_TRIGEDIT 27 /**< OLC mode - trigger edit */
#define CON_HEDIT 28 /**< OLC mode - help edit */
#define CON_QEDIT 29 /**< OLC mode - quest edit */
#define CON_PREFEDIT 30 /**< OLC mode - preference edit */
#define CON_IBTEDIT 31 /**< OLC mode - idea/bug/typo edit */
#define CON_MSGEDIT 32 /**< OLC mode - message editor */
#define CON_PLR_BACKGROUND 33 /**< Entering a new character background */
#define CON_GET_PROTOCOL 33 /**< Used at log-in while attempting to get protocols > */
#define CON_GET_CONNECT 34 /**< Login connect/disconnect menu */
#define CON_GET_ACCOUNT 35 /**< Login with account name */
#define CON_ACCOUNT_CNFRM 36 /**< New account, confirm name */
#define CON_ACCOUNT_PASSWORD 37 /**< Login with account password */
#define CON_ACCOUNT_NEWPASSWD 38 /**< New account, create password */
#define CON_ACCOUNT_CNFPASSWD 39 /**< New account, confirm password */
#define CON_ACCOUNT_EMAIL 40 /**< New account, optional email */
#define CON_ACCOUNT_MENU 41 /**< Account main menu */
#define CON_ACCOUNT_LIST 42 /**< Viewing account character list */
#define CON_QSPECIES 8 /**< Choose character species */
#define CON_QCLASS 9 /**< Choose character class */
#define CON_QSTAT_PREF 44 /**< Choose character stat preference order */
#define CON_QAGE 45 /**< Choose character age */
#define CON_QSHORTDESC 10 /**< Enter a new character short description prompt */
#define CON_RMOTD 11 /**< Reading the message of the day */
#define CON_MENU 12 /**< At the main menu */
#define CON_PLR_DESC 13 /**< Enter a new character description prompt */
#define CON_CHPWD_GETOLD 14 /**< Changing passwd: Get old */
#define CON_CHPWD_GETNEW 15 /**< Changing passwd: Get new */
#define CON_CHPWD_VRFY 16 /**< Changing passwd: Verify new password */
#define CON_DELCNF1 17 /**< Character Delete: Confirmation 1 */
#define CON_DELCNF2 18 /**< Character Delete: Confirmation 2 */
#define CON_DISCONNECT 19 /**< In-game link loss (leave character) */
#define CON_OEDIT 20 /**< OLC mode - object editor */
#define CON_REDIT 21 /**< OLC mode - room editor */
#define CON_ZEDIT 22 /**< OLC mode - zone info editor */
#define CON_MEDIT 23 /**< OLC mode - mobile editor */
#define CON_SEDIT 24 /**< OLC mode - shop editor */
#define CON_TEDIT 25 /**< OLC mode - text editor */
#define CON_CEDIT 26 /**< OLC mode - conf editor */
#define CON_AEDIT 27 /**< OLC mode - social (action) edit */
#define CON_TRIGEDIT 28 /**< OLC mode - trigger edit */
#define CON_HEDIT 29 /**< OLC mode - help edit */
#define CON_QEDIT 30 /**< OLC mode - quest edit */
#define CON_PREFEDIT 31 /**< OLC mode - preference edit */
#define CON_IBTEDIT 32 /**< OLC mode - idea/bug/typo edit */
#define CON_MSGEDIT 33 /**< OLC mode - message editor */
#define CON_PLR_BACKGROUND 34 /**< Entering a new character background */
#define CON_GET_PROTOCOL 34 /**< Used at log-in while attempting to get protocols > */
#define CON_GET_CONNECT 35 /**< Login connect/disconnect menu */
#define CON_GET_ACCOUNT 36 /**< Login with account name */
#define CON_ACCOUNT_CNFRM 37 /**< New account, confirm name */
#define CON_ACCOUNT_PASSWORD 38 /**< Login with account password */
#define CON_ACCOUNT_NEWPASSWD 39 /**< New account, create password */
#define CON_ACCOUNT_CNFPASSWD 40 /**< New account, confirm password */
#define CON_ACCOUNT_EMAIL 41 /**< New account, optional email */
#define CON_ACCOUNT_MENU 42 /**< Account main menu */
#define CON_ACCOUNT_LIST 43 /**< Viewing account character list */
/* OLC States range - used by IS_IN_OLC and IS_PLAYING */
#define FIRST_OLC_STATE CON_OEDIT /**< The first CON_ state that is an OLC */
@ -439,23 +475,12 @@
#define ITEM_MAGIC 6 /**< Item is magical */
#define ITEM_NODROP 7 /**< Item is cursed: can't drop */
#define ITEM_BLESS 8 /**< Item is blessed */
#define ITEM_ANTI_GOOD 9 /**< Not usable by good people */
#define ITEM_ANTI_EVIL 10 /**< Not usable by evil people */
#define ITEM_ANTI_NEUTRAL 11 /**< Not usable by neutral people */
#define ITEM_ANTI_SORCEROR 12 /**< Not usable by sorcerors */
#define ITEM_ANTI_CLERIC 13 /**< Not usable by clerics */
#define ITEM_ANTI_ROGUE 14 /**< Not usable by rogues */
#define ITEM_ANTI_FIGHTER 15 /**< Not usable by fighters */
#define ITEM_ANTI_BARBARIAN 16 /**< Not usable by barbarians */
#define ITEM_ANTI_RANGER 17 /**< Not usable by rangers */
#define ITEM_ANTI_BARD 18 /**< Not usable by bards */
#define ITEM_ANTI_DRUID 19 /**< Not usable by druids */
#define ITEM_NOSELL 20 /**< Shopkeepers won't touch it */
#define ITEM_QUEST 21 /**< Item is a quest item */
#define ITEM_HOOD_UP 22 /**< WORN item hood is currently up */
#define ITEM_SKINNED 23 /* Item/corpse can be skinned */
#define ITEM_NOSELL 9 /**< Shopkeepers won't touch it */
#define ITEM_QUEST 10 /**< Item is a quest item */
#define ITEM_HOOD_UP 11 /**< WORN item hood is currently up */
#define ITEM_SKINNED 12 /* Item/corpse can be skinned */
/** Total number of item flags */
#define NUM_ITEM_FLAGS 24
#define NUM_ITEM_FLAGS 13
/* Modifier constants used with obj affects ('A' fields) */
#define APPLY_NONE 0 /**< No effect */
@ -472,7 +497,7 @@
#define APPLY_CHAR_HEIGHT 11 /**< Apply to height */
#define APPLY_MANA 12 /**< Apply to max mana */
#define APPLY_HIT 13 /**< Apply to max hit points */
#define APPLY_MOVE 14 /**< Apply to max move points */
#define APPLY_STAMINA 14 /**< Apply to max stamina points */
#define APPLY_COINS 15 /**< Reserved */
#define APPLY_EXP 16 /**< Reserved */
#define APPLY_AC 17 /**< Apply to Armor Class */
@ -872,7 +897,7 @@ struct time_info_data
/** Player specific time information. */
struct time_data
{
time_t birth; /**< Represents the PCs birthday, used to calculate age. */
time_t birth; /**< Anchor for calculating mechanical age from played time. */
time_t logon; /**< Time of the last logon, used to calculate time played */
int played; /**< This is the total accumulated time played in secs */
};
@ -907,8 +932,11 @@ struct char_player_data
char *background; /**< PC / NPC background / history text */
byte sex; /**< PC / NPC sex */
byte chclass; /**< PC / NPC class */
byte species; /**< PC / NPC species */
byte level; /**< PC / NPC level */
struct time_data time; /**< PC AGE in days */
struct time_data time; /**< Playtime tracking */
int roleplay_age; /**< Roleplay age in years */
int roleplay_age_year; /**< Last mud year roleplay age was updated */
ubyte weight; /**< PC / NPC weight */
ubyte height; /**< PC / NPC height */
};
@ -945,8 +973,8 @@ struct char_point_data
sh_int max_mana; /**< Max mana level */
sh_int hit; /**< Curent hit point, or health, level */
sh_int max_hit; /**< Max hit point, or health, level */
sh_int move; /**< Current move point, or stamina, level */
sh_int max_move; /**< Max move point, or stamina, level */
sh_int stamina; /**< Current stamina level */
sh_int max_stamina; /**< Max stamina level */
/** Current armor class. Internal use goes from -100 (totally armored) to
* 100 (totally naked). Externally expressed as -10 (totally armored) to
@ -978,6 +1006,9 @@ struct char_special_data
struct char_data *fighting; /**< Target of fight; else NULL */
struct char_data *hunting; /**< Target of NPC hunt; else NULL */
struct obj_data *furniture; /**< Object being sat on/in; else NULL */
struct char_data *mount; /**< Mount being ridden; else NULL */
struct char_data *rider; /**< Rider, if being mounted; else NULL */
struct char_data *hitched_to; /**< Person this mount is hitched to; else NULL */
struct char_data *next_in_furniture; /**< Next person sitting, else NULL */
byte position; /**< Standing, fighting, sleeping, etc. */
@ -986,6 +1017,7 @@ struct char_special_data
byte carry_items; /**< Number of items carried */
int timer; /**< Timer for update */
int stealth_check; /* last rolled Stealth value for Hide; 0 = not hiding/opposed */
bool custom_ldesc; /* temporary ldesc override from change command */
struct char_special_data_saved saved; /**< Constants saved for PCs. */
};
@ -1013,6 +1045,9 @@ struct player_special_data_saved
int quest_counter; /**< Count of targets left to get */
time_t lastmotd; /**< Last time player read motd */
time_t lastnews; /**< Last time player read news */
bool reroll_used; /**< Has the PC used their one-time reroll */
time_t reroll_expires; /**< When the reroll review period ends */
struct char_ability_data reroll_old_abils; /**< Original stats before reroll */
time_t next_skill_gain[MAX_SKILLS+1]; /* indexed by skill/spell number */
};
@ -1128,6 +1163,10 @@ struct char_data
struct group_data *group; /**< Character's Group */
long pref; /**< unique session id */
bool stat_pref_use; /**< Use stat preference ordering when rolling abilities */
ubyte stat_pref_count; /**< Number of preferred stats entered */
ubyte stat_pref_order[NUM_ABILITIES]; /**< Ability order preferences */
struct list_data * events;
};
@ -1347,6 +1386,7 @@ struct recent_player
/* NPC loadout helpers */
void loadout_free_list(struct mob_loadout **head);
void loadout_add_entry(struct mob_loadout **head, obj_vnum vnum, sh_int wear_pos, int qty);
void loadout_append_entry(struct mob_loadout **head, obj_vnum vnum, sh_int wear_pos, int qty);
struct mob_loadout *loadout_deep_copy(const struct mob_loadout *src);
/* Furniture defines for object values */
@ -1387,7 +1427,6 @@ struct forage_entry {
* variables. */
struct game_data
{
int pk_allowed; /**< Is player killing allowed? */
int pt_allowed; /**< Is player thieving allowed? */
int level_can_shout; /**< Level player must be to shout. */
int tunnel_size; /**< Number of people allowed in a tunnel.*/

View file

@ -39,8 +39,8 @@ struct char_point_data_plrtoascii {
sh_int max_mana; /* Max mana for PC/NPC */
sh_int hit;
sh_int max_hit; /* Max hit for PC/NPC */
sh_int move;
sh_int max_move; /* Max move for PC/NPC */
sh_int stamina;
sh_int max_stamina; /* Max stamina for PC/NPC */
sh_int armor; /* Internal -100..100, external -10..10 AC */
int coins; /* Money carried */
@ -59,7 +59,7 @@ struct char_point_data_plrtoascii {
* in player_special_data.
*/
struct char_special_data_saved_plrtoascii {
int alignment; /* +-1000 for alignments */
int alignment;
long idnum; /* player's idnum; -1 for mobiles */
long /*bitvector_t*/ act; /* act flag for NPC's; player flag for PC's */
@ -70,7 +70,7 @@ struct char_special_data_saved_plrtoascii {
struct player_special_data_saved_plrtoascii {
byte skills[MAX_SKILLS+1]; /* array of skills plus skill 0 */
byte PADDING0; /* used to be spells_to_learn */
byte legacy0; /* legacy unused byte */
bool talks[MAX_TONGUE]; /* PC s Tongues 0 for NPC */
int wimp_level; /* Below this # of hit points, flee! */
byte freeze_level; /* Level of god who froze char, if any */
@ -89,7 +89,7 @@ struct player_special_data_saved_plrtoascii {
ubyte spare3;
ubyte spare4;
ubyte page_length;
int spells_to_learn; /* How many can you learn yet this level*/
int spare5;
int olc_zone;
int spare8;
int spare9;
@ -212,8 +212,6 @@ void convert(char *filename)
/* char_special_data_saved */
csds = &(player.char_specials_saved);
if (csds->alignment != PFDEF_ALIGNMENT)
fprintf(outfile, "Alin: %d\n", csds->alignment);
fprintf(outfile, "Id : %d\n", (int)csds->idnum);
if (csds->act != PFDEF_PLRFLAGS)
fprintf(outfile, "Act : %d\n", (int)csds->act);
@ -263,8 +261,6 @@ void convert(char *filename)
if (psds->conditions[2] && player.level < LVL_IMMORT &&
psds->conditions[DRUNK] != PFDEF_DRUNK)
fprintf(outfile, "Drnk: %d\n", (int)psds->conditions[2]);
if (psds->spells_to_learn != PFDEF_PRACTICES)
fprintf(outfile, "Lern: %d\n", (int)psds->spells_to_learn);
/* char_ability_data */
cad = &(player.abilities);
@ -287,8 +283,8 @@ void convert(char *filename)
fprintf(outfile, "Hit : %d/%d\n", cpd->hit, cpd->max_hit);
if (cpd->mana != PFDEF_MANA || cpd->max_mana != PFDEF_MAXMANA)
fprintf(outfile, "Mana: %d/%d\n", cpd->mana, cpd->max_mana);
if (cpd->move != PFDEF_MOVE || cpd->max_move != PFDEF_MAXMOVE)
fprintf(outfile, "Move: %d/%d\n", cpd->move, cpd->max_move);
if (cpd->stamina != PFDEF_STAMINA || cpd->max_stamina != PFDEF_MAXSTAMINA)
fprintf(outfile, "Stam: %d/%d\n", cpd->stamina, cpd->max_stamina);
if (cpd->armor != PFDEF_AC)
fprintf(outfile, "Ac : %d\n", cpd->armor);
if (cpd->coins != PFDEF_COINS)

View file

@ -570,17 +570,24 @@ time_t mud_time_to_secs(struct time_info_data *now)
return (time(NULL) - when);
}
/** Calculate a player's MUD age.
* @todo The minimum starting age of 17 is hardcoded in this function. Recommend
* changing the minimum age to a property (variable) external to this function.
time_t get_total_played_seconds(const struct char_data *ch)
{
time_t played = ch->player.time.played;
if (ch->desc && STATE(ch->desc) == CON_PLAYING)
played += time(0) - ch->player.time.logon;
return played;
}
/** Calculate a player's mechanical age based on total played time.
* @param ch A valid player character. */
struct time_info_data *age(struct char_data *ch)
{
static struct time_info_data player_age;
player_age = *mud_time_passed(time(0), ch->player.time.birth);
player_age.year += 17; /* All players start at 17 */
time_t played = get_total_played_seconds(ch);
player_age = *mud_time_passed(time(0), time(0) - played);
return (&player_age);
}
@ -647,6 +654,8 @@ void stop_follower(struct char_data *ch)
}
ch->master = NULL;
if (HITCHED_TO(ch))
HITCHED_TO(ch) = NULL;
REMOVE_BIT_AR(AFF_FLAGS(ch), AFF_CHARM);
}
@ -1700,6 +1709,74 @@ const char *get_char_sdesc(const struct char_data *ch)
return "someone";
}
void clear_custom_ldesc(struct char_data *ch)
{
char base_buf[MAX_INPUT_LENGTH];
char ldesc[MAX_STRING_LENGTH];
const char *base;
if (!ch || !ch->char_specials.custom_ldesc)
return;
ch->char_specials.custom_ldesc = FALSE;
if (ch->player.long_descr) {
if (!IS_NPC(ch) || GET_MOB_RNUM(ch) == NOBODY ||
ch->player.long_descr != mob_proto[GET_MOB_RNUM(ch)].player.long_descr) {
free(ch->player.long_descr);
}
ch->player.long_descr = NULL;
}
base = (GET_SHORT_DESC(ch) && *GET_SHORT_DESC(ch)) ? GET_SHORT_DESC(ch) : GET_NAME(ch);
if (!base || !*base)
base = "someone";
strlcpy(base_buf, base, sizeof(base_buf));
if (*base_buf)
base_buf[0] = UPPER(*base_buf);
snprintf(ldesc, sizeof(ldesc), "%s is standing here.\r\n", base_buf);
ch->player.long_descr = strdup(ldesc);
}
bool build_hidden_ldesc(const struct char_data *ch, char *out, size_t outsz)
{
char base_buf[MAX_INPUT_LENGTH];
const char *base;
size_t base_len;
const char *suffix;
if (!out || outsz == 0) return FALSE;
*out = '\0';
if (!ch || !ch->char_specials.custom_ldesc || !ch->player.long_descr)
return FALSE;
if (GET_POS(ch) != GET_DEFAULT_POS(ch))
return FALSE;
base = (GET_SHORT_DESC(ch) && *GET_SHORT_DESC(ch)) ? GET_SHORT_DESC(ch) : GET_NAME(ch);
if (!base || !*base)
base = "someone";
strlcpy(base_buf, base, sizeof(base_buf));
if (*base_buf)
base_buf[0] = UPPER(*base_buf);
base_len = strlen(base_buf);
if (strncmp(ch->player.long_descr, base_buf, base_len) != 0)
return FALSE;
suffix = ch->player.long_descr + base_len;
if (*suffix == ' ')
suffix++;
else
return FALSE;
snprintf(out, outsz, "A shadowy figure %s", suffix);
return TRUE;
}
/* 5e system helpers */
extern const struct armor_slot armor_slots[]; /* in constants.c */
@ -2074,6 +2151,24 @@ void loadout_add_entry(struct mob_loadout **head, obj_vnum vnum, sh_int wear_pos
*head = e;
}
void loadout_append_entry(struct mob_loadout **head, obj_vnum vnum, sh_int wear_pos, int qty) {
struct mob_loadout *e = NULL;
struct mob_loadout *tail;
if (qty < 1) qty = 1;
CREATE(e, struct mob_loadout, 1);
e->vnum = vnum;
e->wear_pos = wear_pos;
e->quantity = qty;
e->next = NULL;
if (!*head) {
*head = e;
return;
}
for (tail = *head; tail->next; tail = tail->next)
;
tail->next = e;
}
struct mob_loadout *loadout_deep_copy(const struct mob_loadout *src) {
struct mob_loadout *head = NULL, *tail = NULL;
for (const struct mob_loadout *p = src; p; p = p->next) {

View file

@ -54,6 +54,7 @@ void sprintbitarray(int bitvector[], const char *names[], int maxar, char *resul
int get_line(FILE *fl, char *buf);
int get_filename(char *filename, size_t fbufsize, int mode, const char *orig_name);
time_t mud_time_to_secs(struct time_info_data *now);
time_t get_total_played_seconds(const struct char_data *ch);
struct time_info_data *age(struct char_data *ch);
int num_pc_in_room(struct room_data *room);
void core_dump_real(const char *who, int line);
@ -80,6 +81,8 @@ char *right_trim_whitespace(const char *string);
void remove_from_string(char *string, const char *to_remove);
const char *const *obj_value_labels(int item_type);
const char *get_char_sdesc(const struct char_data *ch);
void clear_custom_ldesc(struct char_data *ch);
bool build_hidden_ldesc(const struct char_data *ch, char *out, size_t outsz);
int obj_is_storage(const struct obj_data *obj);
int obj_storage_is_closed(const struct obj_data *obj);
int roll_skill_check(struct char_data *ch, int skillnum, int mode, int *out_d20);
@ -193,6 +196,12 @@ void advance_level(struct char_data *ch);
void char_from_furniture(struct char_data *ch);
/** What ch is currently sitting on. */
#define SITTING(ch) ((ch)->char_specials.furniture)
/** Mount ch is currently riding. */
#define MOUNT(ch) ((ch)->char_specials.mount)
/** Rider currently mounted on ch. */
#define RIDDEN_BY(ch) ((ch)->char_specials.rider)
/** Person ch is hitched to. */
#define HITCHED_TO(ch) ((ch)->char_specials.hitched_to)
/** Who is sitting next to ch, if anyone. */
#define NEXT_SITTING(ch) ((ch)->char_specials.next_in_furniture)
/** Who is sitting on this obj */
@ -234,6 +243,9 @@ void char_from_furniture(struct char_data *ch);
* Current calculation ~= 12.4 real life days */
#define SECS_PER_MUD_YEAR (17*SECS_PER_MUD_MONTH)
#define MIN_CHAR_AGE 18
#define MAX_CHAR_AGE 65
/** The number of seconds in a real minute. */
#define SECS_PER_REAL_MIN 60
/** The number of seconds in a real hour. */
@ -514,6 +526,8 @@ do \
#define GET_WAS_IN(ch) ((ch)->was_in_room)
/** How old is PC/NPC, at last recorded time? */
#define GET_AGE(ch) (age(ch)->year)
#define GET_ROLEPLAY_AGE(ch) ((ch)->player.roleplay_age)
#define GET_ROLEPLAY_AGE_YEAR(ch) ((ch)->player.roleplay_age_year)
/** Proper name for PCs and NPCs. */
#define GET_NAME(ch) ((ch)->player.name)
@ -556,6 +570,8 @@ do \
/** Class of ch. */
#define GET_CLASS(ch) ((ch)->player.chclass)
/** Species of ch. */
#define GET_SPECIES(ch) ((ch)->player.species)
/** Height of ch. */
#define GET_HEIGHT(ch) ((ch)->player.height)
/** Weight of ch. */
@ -596,10 +612,10 @@ do \
#define GET_HIT(ch) ((ch)->points.hit)
/** Maximum hit points of ch. */
#define GET_MAX_HIT(ch) ((ch)->points.max_hit)
/** Current move points (stamina) of ch. */
#define GET_MOVE(ch) ((ch)->points.move)
/** Maximum move points (stamina) of ch. */
#define GET_MAX_MOVE(ch) ((ch)->points.max_move)
/** Current stamina points of ch. */
#define GET_STAMINA(ch) ((ch)->points.stamina)
/** Maximum stamina points of ch. */
#define GET_MAX_STAMINA(ch) ((ch)->points.max_stamina)
/** Current mana points (magic) of ch. */
#define GET_MANA(ch) ((ch)->points.mana)
/** Maximum mana points (magic) of ch. */
@ -668,6 +684,9 @@ do \
#define GET_SCAN_RESULTS(ch) CHECK_PLAYER_SPECIAL((ch), ((ch)->player_specials->scan_results))
#define GET_LAST_MOTD(ch) CHECK_PLAYER_SPECIAL((ch), ((ch)->player_specials->saved.lastmotd))
#define GET_LAST_NEWS(ch) CHECK_PLAYER_SPECIAL((ch), ((ch)->player_specials->saved.lastnews))
#define GET_REROLL_USED(ch) CHECK_PLAYER_SPECIAL((ch), ((ch)->player_specials->saved.reroll_used))
#define GET_REROLL_EXPIRES(ch) CHECK_PLAYER_SPECIAL((ch), ((ch)->player_specials->saved.reroll_expires))
#define GET_REROLL_OLD_ABILS(ch) CHECK_PLAYER_SPECIAL((ch), ((ch)->player_specials->saved.reroll_old_abils))
/** Get channel history i for ch. */
#define GET_HISTORY(ch, i) CHECK_PLAYER_SPECIAL((ch), ((ch)->player_specials->saved.comm_hist[i]))
/** Return the page length (height) for ch. */
@ -736,13 +755,6 @@ do \
#define CAN_SEE_IN_DARK(ch) \
(AFF_FLAGGED(ch, AFF_INFRAVISION) || (!IS_NPC(ch) && PRF_FLAGGED(ch, PRF_HOLYLIGHT)))
/** Defines if ch is good. */
#define IS_GOOD(ch) (GET_ALIGNMENT(ch) >= 350)
/** Defines if ch is evil. */
#define IS_EVIL(ch) (GET_ALIGNMENT(ch) <= -350)
/** Defines if ch is neither good nor evil. */
#define IS_NEUTRAL(ch) (!IS_GOOD(ch) && !IS_EVIL(ch))
/** Old wait state function.
* @deprecated Use GET_WAIT_STATE */
#define WAIT_STATE(ch, cycle) do { GET_WAIT_STATE(ch) = (cycle); } while(0)
@ -936,6 +948,7 @@ do \
/** True if ch has a valid player class assigned. */
#define HAS_VALID_CLASS(ch) ((GET_CLASS(ch) >= CLASS_SORCEROR) && (GET_CLASS(ch) < NUM_CLASSES))
#define HAS_VALID_SPECIES(ch) ((GET_SPECIES(ch) >= 0) && (GET_SPECIES(ch) < NUM_SPECIES))
/** Return the class abbreviation for ch. */
#define CLASS_ABBR(ch) (HAS_VALID_CLASS(ch) ? class_abbrevs[(int)GET_CLASS(ch)] : "--")
@ -1021,7 +1034,6 @@ do \
#define CONFIG_CONFFILE config_info.CONFFILE
/** Player killing allowed or not? */
#define CONFIG_PK_ALLOWED config_info.play.pk_allowed
/** Player thieving allowed or not? */
#define CONFIG_PT_ALLOWED config_info.play.pt_allowed
/** What level to use the shout command? */