mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Added an initial brainstorm for a more fully-featured tutorial multiplayer game "Battle for Evennia". The idea is to use this as a basis for a series of tutorials on building a relatively complete game in Evennia. The details will probably change, so putting it up for comments from the community.
This commit is contained in:
parent
fcc338c027
commit
75cc8bf1ad
2 changed files with 158 additions and 29 deletions
139
contrib/battle_for_evennia/README
Normal file
139
contrib/battle_for_evennia/README
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
|
||||
Battle for Evennia
|
||||
------------------
|
||||
|
||||
Evennia contrib - Griatch 2012 (WORK IN PROGRESS)
|
||||
|
||||
This is the beginnings of what will be a tutorial for building a
|
||||
simple yet still reasonably playable and not-quite-bog-standard
|
||||
starting game in Evennia. The tutorial text itself will eventually be
|
||||
found from the Dev blog and from the wiki.
|
||||
|
||||
Ideas & Initial Brainstorm
|
||||
---------------------------
|
||||
|
||||
This is to be a hack&slash game. Characters fight mobiles and each
|
||||
other for random loot and better weapons. The highscore is based on
|
||||
most accumulated gold. They can sell loot to NPC merchants for gold,
|
||||
and also buy stuff others sold there (spending gold). Characters get
|
||||
better in the skills they use (no levels). They automatically collect
|
||||
loot when they kill things, and they cannot drop it (but they can give
|
||||
it away and, most importantly sell it). Death sends the player back to
|
||||
a starting position, but gives all but their weakest gear to their
|
||||
nemesis (they keep all their gold though).
|
||||
|
||||
Inventory of code we need:
|
||||
- Loot/Equipment lists of Weapons, Armour, Potions and Spells - maybe partly randomly generated.
|
||||
- Way to spawn in-game objects based on the loot lists
|
||||
- Character creation module (choose skills, attributes, assigns starting gear)
|
||||
- 3 Attributes, about 10 skills (some magic?)
|
||||
- Experience -> skill increase code
|
||||
- Skill success code - same between PCs as between NPC and PC
|
||||
- Combat code (twitch-based? Turn-based? Turn based seems easier to balance. Same for NPC vs PC and PC vs PC)
|
||||
- Mobile code (same for NPCs and enemies)
|
||||
- 'Give' mechanism (should require consent by receiver)
|
||||
- No quests, for simplicity. Use gold as a highscore.
|
||||
- Death respawn mechanism
|
||||
|
||||
Elaboration based on Brainstorm
|
||||
-------------------------------
|
||||
|
||||
* Loot/Equipment lists and spawn - These could be global-level
|
||||
dictionaries in a module. Each dictionary gives info such as name,
|
||||
description and typeclass. Attributes could be set or
|
||||
randomized. The loot-spawner (probably a handler tied to a dead
|
||||
mob, treasure chest etc) would use utils.variable_from_module to
|
||||
extract a random item-template.
|
||||
|
||||
* Characters have 3 attributes: Wile, Strength and Agility. At
|
||||
creation, they distribute points between them. Wile is used for
|
||||
bartering with merchants, and using Magic. Strength determines
|
||||
hand-weapon damage and how heavy armour can be worn. Agility
|
||||
determines ability to dodge, initiative and using lighter weapons.
|
||||
Health is based on an average of all three attributes (i.e. all
|
||||
chars start with the same health).
|
||||
|
||||
* Skills are as follows (may change):
|
||||
- Long blades (str) ability to hit with swords and also axes.
|
||||
- Blunt (str) usage of blunt weapons like clubs. Good on armoured foes.
|
||||
- Spears (agi) usage of spears and hillebards. Bonus on first attack, minus on initiative.
|
||||
- Daggers (agi) usage of daggers and short blades. Bonus on initiative, bad on armour
|
||||
- Unarmoured (agi) usage of your fists and feet. Very fast. Bad on armour.
|
||||
- Dodge (agi) avoiding blows by swift footwork
|
||||
- Feint (agi) faign attacks to keep the enemy guessing
|
||||
- Shield (str) absorbing hits with a shielf
|
||||
- Platemail (str) utilizing heavy armour
|
||||
- Chainmail (max(str, agi)) utilizing medium armour
|
||||
- Leather (agi) utilizing light armour
|
||||
- Barter (wil) barter with merchants for a good price
|
||||
- Magic (wil) use of single-use magical scrolls to achieve various effects
|
||||
- Potions (wil) making the best of potions with various effects
|
||||
- Heal (wil) fixing yourself (or a friend) up between combat. Also judge opponent's wounds.
|
||||
|
||||
* Experience simply rises upon kills and is distributed between the
|
||||
skills used in the battle (so we need to log this). After N amount of
|
||||
XP in a skill, that skill automatically goes up one
|
||||
point. Increasing skills at least N points in 3+ different skills
|
||||
of a certain type (str, agi, wil) will increase the most trained
|
||||
Attribute by one point.
|
||||
|
||||
* Skill success is a comparison between the value of a random.gauss
|
||||
centered around the attacker's skill value vs the result of a
|
||||
random.gauss centered on the defender's skill. Certain
|
||||
weapons/defense combinations might be especially effective against
|
||||
one another (or not). The difference is the base damage, then
|
||||
adjusted by weapon and armour. In the case of bartering, skill
|
||||
challenge is between barter skill of both sides; difference
|
||||
influences the discount/higher price offered for selling/buying.
|
||||
|
||||
* Attacking another player or NPC will start a combat queue.
|
||||
Combat happens in turns. Each turn each player may do two actions,
|
||||
picking among the following:
|
||||
- attack
|
||||
- parry (with weapon)
|
||||
- shield
|
||||
- feint
|
||||
- dodge
|
||||
- flee <direction>
|
||||
- block (anti-flee)
|
||||
Emoting is free in each round, but movement is forbidden unless one
|
||||
tries to flee (agi challenge, or cancelled by block action). All
|
||||
combattants involved in a fight submits their actions, then combat
|
||||
is resolved simultaneously by the code. Order of the two actions
|
||||
matter, so for example if both attack, neither is trying to parry,
|
||||
but may hit each other simultaneously. If both parry, shield or
|
||||
dodge, it means both are dancing about each other. If one feints
|
||||
and the other parries or dodges, they will have a disadvantage on
|
||||
the next defensive movement. A successful parry will give the
|
||||
parried attacker a disadvantage on their next attack. And so on.
|
||||
Another player may "join the queue" at any time by attacking one of
|
||||
the combatting PCs. They get to insert their actions together with
|
||||
the rest on the next round. A round should probably have a timeout
|
||||
to avoid a Character clogging the queue.
|
||||
|
||||
* Mobiles will use "a global ticker system" where they
|
||||
subscribe. They act the same way as PCs in combat, except with a
|
||||
semi- random selection of actions they take (they will probably be
|
||||
more predictable than PCs). Adding aggressive and passive mobiles
|
||||
should be straightforward, as well as un-killable ones (merchants).
|
||||
|
||||
* The inventory of a defeated enemy is automatically transferred to
|
||||
the winner's inventory. If there are many alternative pieces of
|
||||
equipment, they get to keep the weakest one, otherwise it's all
|
||||
transferred. There are no limits to carrying except the fear of
|
||||
losing gear. This should hopefully prevent hoarding of good items.
|
||||
One can give item(s) to another player - that player must then
|
||||
conceed to receiving it (use Y/N module in contrib.menusystem).
|
||||
There is no way of dropping items on the ground; one must either
|
||||
give them away or sell them for gold to a merchant.
|
||||
|
||||
* Gold is used for buying items from merchants, but is also the
|
||||
highscore. Whereas sell prices are fixed, buy prices are not fixed
|
||||
but is based on a percentage of the gold carried, adjusted by the
|
||||
barter skill (this should defeat inflation quite effectively). Items
|
||||
sold to merchants are made available for other players to buy.
|
||||
|
||||
* Death means loosing inventory (except weakest item, as mentioned),
|
||||
but no loss of gold. Otherwise death is cheap - one respawns at a
|
||||
random starting position (probably needing special-aliased rooms to
|
||||
use for this - maybe with one-way exits).
|
||||
|
|
@ -603,48 +603,38 @@ def mod_import(mod_path, propname=None):
|
|||
return mod_prop
|
||||
return mod
|
||||
|
||||
def variable_from_module(modpath, variable, default=None):
|
||||
def variable_from_module(modpath, variable=None, default=None):
|
||||
"""
|
||||
Retrieve a given variable from a module. The variable must be
|
||||
defined globally in the module. This can be used to implement
|
||||
arbitrary plugin imports in the server.
|
||||
Retrieve a variable from a module. The variable must be defined
|
||||
globally in the module. If no variable is given, a random variable
|
||||
is returned from the module.
|
||||
|
||||
If module cannot be imported or variable not found, default
|
||||
If module cannot be imported or given variable not found, default
|
||||
is returned.
|
||||
"""
|
||||
if not modpath:
|
||||
return None
|
||||
return default
|
||||
try:
|
||||
mod = __import__(modpath, fromlist=["None"])
|
||||
return mod.__dict__.get(variable, default)
|
||||
except ImportError:
|
||||
return default
|
||||
if variable:
|
||||
# try to pick a named variable
|
||||
return mod.__dict__.get(variable, default)
|
||||
else:
|
||||
# random selection
|
||||
mvars = [val for key, val in mod.__dict__.items() if not key.startswith("_")]
|
||||
return mvars and random.choice(mvars)
|
||||
|
||||
def string_from_module(modpath, variable=None, default=None):
|
||||
"""
|
||||
This is a variation used primarily to get login screens randomly
|
||||
from a module.
|
||||
|
||||
This obtains a string from a given module python path. Using a
|
||||
specific variable name will also retrieve non-strings.
|
||||
|
||||
The variable must be global within that module - that is, defined
|
||||
in the outermost scope of the module. The value of the variable
|
||||
will be returned. If not found, default is returned. If no variable is
|
||||
given, a random string variable is returned.
|
||||
|
||||
This is useful primarily for storing various game strings in a
|
||||
module and extract them by name or randomly.
|
||||
This is a wrapper for variable_from_module that requires return
|
||||
value to be a string to pass. It's primarily used by login screen.
|
||||
"""
|
||||
mod = __import__(modpath, fromlist=[None])
|
||||
if variable:
|
||||
return mod.__dict__.get(variable, default)
|
||||
else:
|
||||
mvars = [val for key, val in mod.__dict__.items()
|
||||
if not key.startswith('_') and isinstance(val, basestring)]
|
||||
if not mvars:
|
||||
return default
|
||||
return mvars[random.randint(0, len(mvars)-1)]
|
||||
val = variable_from_module(modpath, variable=variable, default=default)
|
||||
if isinstance(val, basestring):
|
||||
return val
|
||||
return default
|
||||
|
||||
def init_new_player(player):
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue