Add initial reputation support

As reputation support is experimental and still under development, the
database is being kept in memory and is lost after every server restart.

git-svn-id: svn://svn.code.sf.net/p/crossfire/code/maps/trunk@20493 282e977c-c81d-0410-88c4-b93c2d0d6712
master
partmedia 2017-08-15 22:49:01 +00:00
parent cd6ceec80e
commit d77739aad0
7 changed files with 219 additions and 0 deletions

View File

@ -0,0 +1,66 @@
import os.path
import sqlite3
import Crossfire
_dict_name = 'CFReputationConnection'
def _init_schema(con, version, *init_files):
con.execute("PRAGMA journal_mode=WAL;");
con.execute("PRAGMA synchronous=NORMAL;");
con.execute("CREATE TABLE IF NOT EXISTS schema(version INT);");
result = con.execute("SELECT version FROM schema").fetchall();
curr = len(result)
if curr < version:
Crossfire.Log(Crossfire.LogInfo,
"Initializing factions schema %d->%d" % (curr, version))
for f in init_files:
with open(f) as initfile:
con.executescript(initfile.read())
con.commit()
def _get_sql_path(f):
return os.path.join(Crossfire.DataDirectory(), Crossfire.MapDirectory(),
"python/CFReputation/sql", f)
def _init_db():
init_files = map(_get_sql_path, ["init.sql", "gods.sql"])
db_path = os.path.join(Crossfire.LocalDirectory(), "factions.db")
con = sqlite3.connect(':memory:')
_init_schema(con, 1, *init_files)
Crossfire.GetSharedDictionary()[_dict_name] = con
def _get_db():
d = Crossfire.GetSharedDictionary()
if _dict_name not in d:
_init_db()
return d[_dict_name]
def reputation(player):
con = _get_db()
query="""
SELECT faction, CAST(ROUND(reputation*100) as integer) as rep
FROM reputations
WHERE name=? AND ABS(rep) > 0;
"""
result = con.execute(query, (player,)).fetchall()
return result
def record_kill(race, region, player, fraction=0.0005, limit=0.4):
con = _get_db()
query = """
WITH updates AS (
SELECT faction, -attitude*? AS change
FROM regions
NATURAL JOIN relations
WHERE race=? AND (region=? OR region='ALL'))
REPLACE INTO reputations
SELECT ? AS player, updates.faction,
COALESCE(reputation, 0) + change AS new_rep
FROM updates
LEFT JOIN reputations
ON updates.faction=reputations.faction AND player=reputations.name
WHERE ABS(new_rep) <= ?;
"""
con.execute(query, (fraction, race, region, player, limit))
con.commit()

View File

@ -0,0 +1,51 @@
insert into regions values ('Valriel', 'ALL', 0.25);
insert into relations values ('Valriel', 'angel', 1);
insert into relations values ('Valriel', 'demon', -1);
insert into regions values ('Gorokh', 'ALL', 0.25);
insert into relations values ('Gorokh', 'demon', 1);
insert into relations values ('Gorokh', 'angel', -1);
insert into regions values ('Devourers', 'ALL', 0.25);
insert into relations values ('Devourers', 'undead', 1);
insert into relations values ('Devourers', 'none', -1);
insert into regions values ('Sorig', 'ALL', 0.25);
insert into relations values ('Sorig', 'air_elemental', 1);
insert into relations values ('Sorig', 'none', -1);
insert into regions values ('Ruggilli', 'ALL', 0.25);
insert into relations values ('Ruggilli', 'consuming_fire_creatures', 1);
insert into relations values ('Ruggilli', 'chaotic_water_creatures', -1);
insert into regions values ('Ixalovh', 'ALL', 0.25);
insert into relations values ('Ixalovh', 'chaotic_water_creatures', 1);
insert into relations values ('Ixalovh', 'consuming_fire_creatures', -1);
insert into regions values ('Gaea', 'ALL', 0.25);
insert into relations values ('Gaea', 'animal', 1);
insert into relations values ('Gaea', 'bird', 1);
insert into relations values ('Gaea', 'slime', 1);
insert into relations values ('Gaea', 'insect', 1);
insert into relations values ('Gaea', 'reptile', 1);
insert into relations values ('Gaea', 'water_elemental', 1);
insert into relations values ('Gaea', 'earth_elemental', 1);
insert into relations values ('Gaea', 'air_elemental', 1);
insert into relations values ('Gaea', 'fire_elemental', 1);
insert into relations values ('Gaea', 'undead', -1);
insert into relations values ('Gaea', 'unnatural', -1);
insert into regions values ('Valkyrie', 'ALL', 0.25);
insert into relations values ('Valkyrie', 'human', 1);
insert into relations values ('Valkyrie', 'troll', 1);
insert into relations values ('Valkyrie', 'unnatural', -1);
insert into relations values ('Valkyrie', 'angel', -1);
insert into relations values ('Valkyrie', 'demon', -1);
insert into relations values ('Valkyrie', 'undead', -1);
insert into regions values ('Mostrai', 'ALL', 0.25);
insert into relations values ('Mostrai', 'dwarf', 1);
insert into relations values ('Mostrai', 'goblin', -1);
insert into relations values ('Mostrai', 'giant', -1);
insert into regions values ('Lythander', 'ALL', 0.25);
insert into relations values ('Lythander', 'faerie', 1);
insert into relations values ('Lythander', 'goblin', -1);
insert into relations values ('Lythander', 'troll', -1);
insert into regions values ('Gnarg', 'ALL', 0.25);
insert into relations values ('Gnarg', 'goblin', 1);
insert into relations values ('Gnarg', 'giant', 1);
insert into relations values ('Gnarg', 'troll', 1);
insert into relations values ('Gnarg', 'faerie', -1);
insert into relations values ('Gnarg', 'dwarf', -1);

View File

@ -0,0 +1,47 @@
CREATE TABLE IF NOT EXISTS schema(version INT);
CREATE TABLE regions(
faction TEXT,
region TEXT, -- region name (or 'ALL') this faction controls
influence NUMERIC,
CONSTRAINT influence_range CHECK(influence BETWEEN 0 AND 1)
);
CREATE TABLE relations(
faction TEXT,
race TEXT,
attitude NUMERIC,
PRIMARY KEY (faction, race),
CONSTRAINT attitude_range CHECK(attitude BETWEEN -1 AND 1)
);
CREATE TABLE reputations(
name TEXT, -- player name
faction TEXT,
reputation NUMERIC,
PRIMARY KEY (name, faction),
CONSTRAINT reputation_range CHECK(reputation BETWEEN -1 AND 1)
);
INSERT INTO regions VALUES
('Dragons', 'ALL', 0.4),
('Scorn', 'scorn', 0.5),
('Scorn', 'scornarena', 0.5),
('Scorn', 'scorncounty', 0.5),
('Scorn', 'scornoldcity', 0.5);
INSERT INTO relations VALUES
('Dragons', 'dragon', 1),
('Dragons', 'faerie', -1),
('Dragons', 'human', -1),
('Scorn', 'demon', -1),
('Scorn', 'dragon', -1),
('Scorn', 'giant', -1),
('Scorn', 'goblin', -1),
('Scorn', 'human', 1),
('Scorn', 'reptile', -1),
('Scorn', 'troll', -1),
('Scorn', 'undead', -1),
('Scorn', 'unnatural', -1);
INSERT INTO schema VALUES(1);

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python
# gods2factions -- convert `crossfire-server -m6` output to factions SQL
import sys
def print_rels(faction, rel_str, rel):
rels = rel_str.split(',')
for r in rels:
print("insert into relations values ('%s', '%s', %d);"
% (faction, r, rel))
for l in sys.stdin.read().splitlines():
xs = map(str.strip, l.split(':'))
if len(xs) < 1:
continue
if xs[0] == 'GOD':
curr_god = xs[1]
print("insert into regions values ('%s', 'ALL', 0.25);" % curr_god)
elif xs[0] == 'aligned_race(s)':
print_rels(curr_god, xs[1], 1)
elif xs[0] == 'enemy_race(s)':
print_rels(curr_god, xs[1], -1)

View File

@ -0,0 +1,15 @@
import CFReputation
import Crossfire
def show_reputation(who):
reputations = CFReputation.reputation(who.Name)
lines = []
if len(reputations) == 0:
lines.append("You don't have a reputation with anyone.")
else:
lines.append("You are known by the following factions:")
for p in reputations:
lines.append("\t%s....................%d" % (p[0], p[1]))
who.Message("\n".join(lines))
show_reputation(Crossfire.WhoAmI())

View File

@ -0,0 +1,17 @@
import Crossfire
import CFReputation
def get_killer(hitter):
owner = hitter.Owner
if owner is not None:
return owner
return hitter
def is_player(op):
return op.Type == Crossfire.Type.PLAYER
killer = get_killer(Crossfire.WhoIsActivator())
victim = Crossfire.WhoAmI()
if is_player(killer):
CFReputation.record_kill(victim.Race, victim.Map.Region.Name, killer.Name)

View File

@ -0,0 +1,2 @@
import Crossfire
Crossfire.RegisterCommand("reputation", "/python/commands/reputation", 0)