def func(self): """Implement the command""" caller = self.caller all_recipes = CraftingRecipe.objects.all() recipes = all_recipes.filter(known_by__player__player=caller.player) unknown = all_recipes.exclude(known_by__player__player=caller.player) if self.args and (not self.switches or "known" in self.switches): filters = (Q(name__iexact=self.args) | Q(skill__iexact=self.args) | Q(ability__iexact=self.args)) recipes = recipes.filter(filters) unknown = unknown.filter(filters) orgs = AssetOwner.objects.select_related("organization_owner").filter( organization_owner__isnull=False) unknown = unknown.prefetch_related( Prefetch("known_by", queryset=orgs, to_attr="_org_owners")) recipes = list(recipes) can_learn = [ob for ob in unknown if ob.can_be_learned_by(self.caller)] try: dompc = PlayerOrNpc.objects.get(player=caller.player) except PlayerOrNpc.DoesNotExist: dompc = setup_dom_for_char(caller) if not self.switches: visible = recipes + can_learn self.display_recipes(visible) return if "known" in self.switches: self.display_recipes(recipes) return if "learn" in self.switches or "cost" in self.switches: match = None if self.args: match = [ ob for ob in can_learn if ob.name.lower() == self.args.lower() ] if not match: learn_msg = ("You cannot learn '%s'. " % self.lhs) if self.lhs else "" caller.msg("%sRecipes you can learn:" % learn_msg) self.display_recipes(can_learn) return match = match[0] cost = 0 if caller.check_permstring( "builders") else match.additional_cost cost_msg = "It will cost %s for you to learn %s." % ( cost or "nothing", match.name, ) if "cost" in self.switches: return caller.msg(cost_msg) elif cost > caller.currency: return caller.msg("You have %s silver. %s" % (caller.currency, cost_msg)) caller.pay_money(cost) dompc.assets.recipes.add(match) coststr = (" for %s silver" % cost) if cost else "" caller.msg("You have learned %s%s." % (match.name, coststr)) return if "info" in self.switches: match = None info = list(can_learn) + list(recipes) if self.args: match = [ ob for ob in info if ob.name.lower() == self.args.lower() ] if not match: caller.msg( "No recipe by that name. Recipes you can get /info on:") self.display_recipes(info) return match = match[0] display = match.display_reqs(dompc, full=True) caller.msg(display, options={"box": True}) return if "teach" in self.switches: match = None can_teach = [ob for ob in recipes if ob.access(caller, "teach")] if self.rhs: match = [ ob for ob in can_teach if ob.name.lower() == self.rhs.lower() ] if not match: teach_msg = ("You cannot teach '%s'. " % self.rhs) if self.rhs else "" caller.msg("%sRecipes you can teach:" % teach_msg) self.display_recipes(can_teach) return recipe = match[0] character = caller.search(self.lhs) if not character: return if not recipe.access(character, "learn"): caller.msg("They cannot learn %s." % recipe.name) return try: dompc = PlayerOrNpc.objects.get(player=character.player) except PlayerOrNpc.DoesNotExist: dompc = setup_dom_for_char(character) if recipe in dompc.assets.recipes.all(): caller.msg("They already know %s." % recipe.name) return dompc.assets.recipes.add(recipe) caller.msg("Taught %s %s." % (character, recipe.name))
def func(self): """Execute command.""" caller = self.caller try: dompc = caller.player.Dominion except AttributeError: dompc = setup_utils.setup_dom_for_char(caller) org_accounts = [member.organization.assets for member in dompc.memberships.filter(deguilded=False)] all_accounts = [dompc.assets] + org_accounts if ("payments" in self.switches or "endpayment" in self.switches or "adjustpayment" in self.switches or "payment" in self.switches): debts = list(dompc.assets.debts.all()) for acc in org_accounts: if acc.can_be_viewed_by(caller) and acc.debts.all(): debts += list(acc.debts.all()) if not self.args: caller.msg(str(self.get_debt_table(debts)), options={'box': True}) return if "endpayment" in self.switches or "adjustpayment" in self.switches: try: if "endpayment" in self.switches: debts += list(dompc.assets.incomes.all()) val = int(self.lhs) debt = AccountTransaction.objects.get(id=val, id__in=(ob.id for ob in debts)) except (ValueError, AccountTransaction.DoesNotExist): caller.msg("Invalid number. Select one of the following:") caller.msg(str(self.get_debt_table(debts)), options={'box': True}) return if "endpayment" in self.switches: debt.delete() caller.msg("Payment cancelled.") return try: amt = int(self.rhs) if amt <= 0: raise ValueError except ValueError: caller.msg("Please give a positive value as the new amount.") return check = self.check_money(debt.sender, (amt - debt.weekly_amount)) if check < 0: caller.msg("Insufficient funds. You need %s more." % (-check)) return debt.weekly_amount = amt debt.save() caller.msg("Weekly payment amount is now %s." % amt) return # set up a new payment try: sender = self.match_account(all_accounts, self.lhslist[0]) if not sender: return if not sender.access(caller, 'withdraw'): caller.msg("You lack permission to set up a payment.") return amt = int(self.lhslist[1]) if amt <= 0: raise ValueError try: receiver = AssetOwner.objects.get(player__player__username__iexact=self.rhs) except AssetOwner.DoesNotExist: receiver = AssetOwner.objects.get(organization_owner__name__iexact=self.rhs) if sender == receiver: caller.msg("Sender and receiver must be different.") return except (ValueError, IndexError): caller.msg("Must give a positive number as an amount.") return except (AssetOwner.DoesNotExist, AssetOwner.MultipleObjectsReturned): caller.msg("Could find neither a player nor organization by that name.") return check = self.check_money(sender, amt) if check < 0: caller.msg("Insufficient funds: %s more required to set up payment." % (-check)) return sender.debts.create(receiver=receiver, weekly_amount=amt, repetitions_left=-1) caller.msg("New weekly payment set up: %s pays %s to %s every week." % (sender, amt, receiver)) return if not self.args: msg = "{wAccounts{n".center(60) msg += "\n" actable = evtable.EvTable("{wOwner{n", "{wBalance{n", "{wNet Income{n", "{wMaterials{n", "{wEcon{n", "{wSoc{n", "{wMil{n", width=78, border="cells") for account in all_accounts: if not account.can_be_viewed_by(self.caller): continue mats = ", ".join(str(mat) for mat in account.materials.filter(amount__gte=1)) actable.add_row(str(account.owner), str(account.vault), str(account.net_income), mats, account.economic, account.social, account.military) actable.reformat_column(0, width=14) actable.reformat_column(1, width=11) actable.reformat_column(2, width=10) actable.reformat_column(3, width=21) actable.reformat_column(4, width=8) actable.reformat_column(5, width=7) actable.reformat_column(6, width=7) incomes = account.incomes.all() debts = account.debts.all() if incomes: msg += ("{w%s Incomes{n" % str(account)).center(60) msg += "\n" table = evtable.EvTable("{wSender{n", "{wAmount{n", "{wTime Remaining{n", width=60) for inc in incomes: time = "Permanent" if inc.repetitions_left == -1 else inc.repetitions_left table.add_row(inc.sender, inc.weekly_amount, time) msg += str(table) msg += "\n" if debts: msg += ("{w%s Payments{n" % str(account)).center(60) msg += "\n" msg += str(self.get_debt_table(debts)) msg += "\n" msg += str(actable) caller.msg(msg, options={'box': True}) return if ("depositmats" in self.switches or "withdrawmats" in self.switches or "depositres" in self.switches or "withdrawres" in self.switches): account = self.match_account(all_accounts) if not account: return if account == dompc.assets: caller.msg("Characters always have access to their own materials as an " "abstraction, so withdraws and deposits " + "are only between organizations and characters.") return usingmats = "depositmats" in self.switches or "withdrawmats" in self.switches if usingmats: attr_type = "materials" else: attr_type = "resources" if "depositmats" in self.switches or "depositres" in self.switches: sender = dompc.assets receiver = account verb = "deposit" else: if not account.access(caller, 'withdraw'): caller.msg("You do not have permission to withdraw from that account.") return receiver = dompc.assets sender = account verb = "withdraw" try: matname, val = self.lhslist[0], int(self.lhslist[1]) source = sender targ = receiver if val <= 0: caller.msg("You must specify a positive number.") return if usingmats: source = sender.materials.get(type__name__iexact=matname) if source.amount < val: caller.msg("You tried to %s %s %s, but only %s available." % ( verb, val, source.type.name, source.amount)) return try: targ = receiver.materials.get(type__name__iexact=matname) except CraftingMaterials.DoesNotExist: targ = receiver.materials.create(type=source.type, amount=0) source.amount -= val targ.amount += val samt = source.amount tamt = targ.amount else: restypes = ("economic", "social", "military") matname = matname.lower() if matname not in restypes: caller.msg("Resource must be one of: %s" % ", ".join(restypes)) return sresamt = getattr(sender, matname) if sresamt < val: matname += " resources" caller.msg("You tried to %s %s %s, but only %s available." % ( verb, val, matname, sresamt)) return tresamt = getattr(receiver, matname) samt = sresamt - val tamt = tresamt + val setattr(sender, matname, samt) setattr(receiver, matname, tamt) matname += " resources" source.save() targ.save() caller.msg("You have transferred %s %s from %s to %s." % ( val, matname, sender, receiver)) if account.can_be_viewed_by(caller): caller.msg("Sender now has %s, receiver has %s." % (samt, tamt)) else: caller.msg("Transaction successful.") self.inform_owner(account, verb, val, attr_type, matname) except CraftingMaterials.DoesNotExist: caller.msg("No match for that material. Valid materials: %s" % ", ".join( str(mat) for mat in sender.materials.all())) return except (ValueError, IndexError): caller.msg("Invalid usage.") return return try: amount = int(self.lhs) if amount <= 0: caller.msg("Amount must be positive.") return except ValueError: caller.msg("Amount must be a number.") return if not self.rhs: account = dompc.assets else: account = self.match_account(all_accounts) if not account: return if "deposit" in self.switches: cash = caller.db.currency or 0.0 if not cash: caller.msg("You have no money to deposit.") return if amount > cash: caller.msg("You tried to deposit %s, but only have %s on hand." % (amount, cash)) return account.vault += amount caller.db.currency = cash - amount account.save() if account.can_be_viewed_by(caller): caller.msg("You have deposited %s. The new balance is %s." % (amount, account.vault)) else: caller.msg("You have deposited %s." % amount) self.inform_owner(account, "deposited", amount) return if "withdraw" in self.switches: if not account.access(caller, "withdraw"): caller.msg("You do not have permission to withdraw from that account.") return cash = caller.db.currency or 0.0 check = self.check_money(account, amount) if check < 0: caller.msg("You cannot withdraw more than the balance minus an account's debt obligations.") caller.msg("You want to withdraw %s but only %s is available after debt obligations." % (amount, check+amount)) if account.debts.all(): caller.msg("Cancelling payments would increase the amount available.") return return account.vault -= amount caller.db.currency = cash + amount account.save() caller.msg("You have withdrawn %s. New balance is %s." % (amount, account.vault)) self.inform_owner(account, "withdrawn", amount) return else: caller.msg("Unrecognized switch.") return
def func(self): """Implement the command""" caller = self.caller if not self.crafter: self.crafter = caller crafter = self.crafter try: dompc = PlayerOrNpc.objects.get(player=caller.player) assets = AssetOwner.objects.get(player=dompc) except PlayerOrNpc.DoesNotExist: # dominion not set up on player dompc = setup_dom_for_char(caller) assets = dompc.assets except AssetOwner.DoesNotExist: # assets not initialized on player dompc = setup_dom_for_char(caller, create_dompc=False) assets = dompc.assets recipes = crafter.player_ob.Dominion.assets.recipes.all() if not self.args and not self.switches: # display recipes and any crafting project we have unfinished materials = assets.materials.all() caller.msg("{wAvailable recipes:{n %s" % ", ".join(recipe.name for recipe in recipes)) caller.msg("{wYour materials:{n %s" % ", ".join(str(mat) for mat in materials)) project = caller.db.crafting_project if project: self.display_project(project) return # start a crafting project if not self.switches or "craft" in self.switches: try: recipe = recipes.get(name__iexact=self.lhs) except CraftingRecipe.DoesNotExist: caller.msg("No recipe found by the name %s." % self.lhs) return try: self.get_recipe_price(recipe) except ValueError: caller.msg("That recipe does not have a price defined.") return # proj = [id, name, desc, adorns, forgery, translation] proj = [recipe.id, "", "", {}, {}, {}, ""] caller.db.crafting_project = proj stmsg = "You have" if caller == crafter else "%s has" % crafter caller.msg("{w%s started to craft:{n %s." % (stmsg, recipe.name)) caller.msg( "{wTo finish it, use /finish after you gather the following:{n" ) caller.msg(recipe.display_reqs(dompc)) return if ("changename" in self.switches or "refine" in self.switches or "addadorn" in self.switches): targ = caller.search(self.lhs, location=caller) if not targ: return recipe = None try: recipe = targ.item_data.recipe except AttributeError: pass if not recipe: caller.msg("No recipe found for that item.") return if "changename" in self.switches: if not self.rhs: self.msg("Usage: /changename <object>=<new name>") return if not validate_name(self.rhs): caller.msg("That is not a valid name.") return if targ.tags.get("plot"): self.msg("It cannot be renamed.") return targ.aliases.clear() targ.name = self.rhs caller.msg("Changed name to %s." % targ) return # adding adorns post-creation if "addadorn" in self.switches: try: material = self.rhslist[0] amt = int(self.rhslist[1]) if amt < 1 and not caller.check_permstring("builders"): raise ValueError except (IndexError, ValueError, TypeError): caller.msg( "Usage: /addadorn <object>=<adornment>,<amount>") return if not recipe.allow_adorn: caller.msg( "This recipe does not allow for additional materials to be used." ) return try: mat = CraftingMaterialType.objects.get( name__iexact=material) except CraftingMaterialType.DoesNotExist: self.msg( "Cannot use %s as it does not appear to be a crafting material." % material) return # if caller isn't a builder, check and consume their materials if not caller.check_permstring("builders"): pmats = caller.player.Dominion.assets.materials try: pmat = pmats.get(type=mat) if pmat.amount < amt: caller.msg("You need %s of %s, and only have %s." % (amt, mat.name, pmat.amount)) return except CraftingMaterials.DoesNotExist: caller.msg("You do not have any of the material %s." % mat.name) return pmat.amount -= amt pmat.save() targ.item_data.add_adorn(mat, amt) caller.msg("%s is now adorned with %s of the material %s." % (targ, amt, mat)) return if "refine" in self.switches: base_cost = recipe.value / 4 caller.msg("The base cost of refining this recipe is %s." % base_cost) try: price = self.get_refine_price(base_cost) except ValueError: caller.msg("Price for refining not set.") return if price: caller.msg("The additional price for refining is %s." % price) action_points = 0 invest = 0 if self.rhs: try: invest = int(self.rhslist[0]) if len(self.rhslist) > 1: action_points = int(self.rhslist[1]) except ValueError: caller.msg( "Amount of silver/action points to invest must be a number." ) return if invest < 0 or action_points < 0: caller.msg("Amount must be positive.") return if not recipe: caller.msg( "This is not a crafted object that can be refined.") return if targ.item_data.quality_level and targ.item_data.quality_level >= 10: caller.msg("This object can no longer be improved.") return ability = get_ability_val(crafter, recipe) if ability < recipe.level: err = "You lack" if crafter == caller else "%s lacks" % crafter caller.msg( "%s the skill required to attempt to improve this." % err) return if not self.check_max_invest(recipe, invest): return cost = base_cost + invest + price # don't display a random number when they're prepping if caller.ndb.refine_targ != (targ, cost): diffmod = get_difficulty_mod(recipe, invest) else: diffmod = get_difficulty_mod(recipe, invest, action_points, ability) # difficulty gets easier by 1 each time we attempt it attempts = targ.item_data.get_refine_attempts_for_character( crafter) if attempts > 60: attempts = 60 diffmod += attempts if diffmod: self.msg( "Based on silver spent and previous attempts, the difficulty is adjusted by %s." % diffmod) if caller.ndb.refine_targ != (targ, cost): caller.ndb.refine_targ = (targ, cost) caller.msg( "The total cost would be {w%s{n. To confirm this, execute the command again." % cost) return if cost > caller.db.currency: caller.msg("This would cost %s, and you only have %s." % (cost, caller.db.currency)) return if action_points and not caller.player_ob.pay_action_points( action_points): self.msg("You do not have enough action points to refine.") return # pay for it caller.pay_money(cost) self.pay_owner( price, "%s has refined '%s', a %s, at your shop and you earn %s silver." % (caller, targ, recipe.name, price), ) roll = do_crafting_roll(crafter, recipe, diffmod, diffmult=0.75, room=caller.location) quality = get_quality_lvl(roll, recipe.difficulty) old = targ.item_data.quality_level or 0 attempts += 1 targ.item_data.set_refine_attempts_for_character( crafter, attempts) self.msg("The roll is %s, a quality level of %s." % (roll, QUALITY_LEVELS[quality])) if quality <= old: caller.msg( "You failed to improve %s; the quality will remain %s." % (targ, QUALITY_LEVELS[old])) return caller.msg("New quality level is %s." % QUALITY_LEVELS[quality]) change_quality(targ, quality) return proj = caller.db.crafting_project if not proj: caller.msg("You have no crafting project.") return if "name" in self.switches: if not self.args: caller.msg("Name it what?") return if not validate_name(self.args): caller.msg("That is not a valid name.") return proj[1] = self.args caller.db.crafting_project = proj caller.msg("Name set to %s." % self.args) return if "desc" in self.switches: if not self.args: caller.msg("Describe it how?") return if not self.can_apply_templates(self.caller, self.args): return proj[2] = self.args caller.db.crafting_project = proj caller.msg("Desc set to:\n%s" % self.args) return if "abandon" in self.switches: caller.msg( "You have abandoned this crafting project. You may now start another." ) caller.db.crafting_project = None return if "translated_text" in self.switches: if not (self.lhs and self.rhs): caller.msg("Usage: craft/translated_text <language>=<text>") return lhs = self.lhs.lower() if lhs not in self.caller.languages.known_languages: caller.msg("Nice try. You cannot speak %s." % self.lhs) return proj[5].update({lhs: self.rhs}) caller.db.crafting_project = proj self.display_project(proj) return if "altdesc" in self.switches: if not self.args: caller.msg( "Describe them how? This is only used for disguise recipes." ) return proj[6] = self.args caller.msg( "This is only used for disguise recipes. Alternate description set to:\n%s" % self.args) return if "adorn" in self.switches: if not (self.lhs and self.rhs): caller.msg("Usage: craft/adorn <material>=<amount>") return try: mat = CraftingMaterialType.objects.get(name__iexact=self.lhs) amt = int(self.rhs) except CraftingMaterialType.DoesNotExist: caller.msg("No material named %s." % self.lhs) return except CraftingMaterialType.MultipleObjectsReturned: caller.msg("More than one match. Please be more specific.") return except (TypeError, ValueError): caller.msg("Amount must be a number.") return if amt < 1: caller.msg("Amount must be positive.") return recipe = CraftingRecipe.objects.get(id=proj[0]) if not recipe.allow_adorn: caller.msg( "This recipe does not allow for additional materials to be used." ) return adorns = proj[3] or {} adorns[mat.id] = amt proj[3] = adorns caller.db.crafting_project = proj caller.msg( "Additional materials: %s" % ", ".join("%s: %s" % (CraftingMaterialType.objects.get(id=mat).name, amt) for mat, amt in adorns.items())) return if "forgery" in self.switches: self.msg("Temporarily disabled until I have time to revamp this.") return if "preview" in self.switches: if self.args: viewer = self.caller.player.search(self.args) if not viewer: return viewer.msg( "{c%s{n is sharing a preview of their crafting project with you." % self.caller) self.msg( "You share a preview of your crafting project with %s." % viewer) else: viewer = self.caller.player name = proj[1] or "[No Name Yet]" viewer.msg("{wPreview of {n%s {wdesc:{n\n%s" % (name, proj[2])) return # do rolls for our crafting. determine quality level, handle forgery stuff if "finish" in self.switches: if not proj[1]: caller.msg("You must give it a name first.") return if not proj[2]: caller.msg("You must write a description first.") return invest = 0 action_points = 0 if self.lhs: try: invest = int(self.lhslist[0]) if len(self.lhslist) > 1: action_points = int(self.lhslist[1]) except ValueError: caller.msg( "Silver/Action Points to invest must be a number.") return if invest < 0 or action_points < 0: caller.msg( "Silver/Action Points cannot be a negative number.") return # first, check if we have all the materials required mats = {} try: recipe = recipes.get(id=proj[0]) except CraftingRecipe.DoesNotExist: caller.msg("You lack the ability to finish that recipe.") return if not self.check_max_invest(recipe, invest): return if recipe.type == "disguise": if not proj[6]: caller.msg( "This kind of item requires craft/altdesc before it can be finished." ) return for mat in recipe.materials.all(): mats[mat.id] = mats.get(mat.id, 0) + mat.amount for adorn in proj[3]: mats[adorn] = mats.get(adorn, 0) + proj[3][adorn] # replace with forgeries for rep in proj[4].keys(): # rep is ID to replace forg = proj[4][rep] if rep in mats: amt = mats[rep] del mats[rep] mats[forg] = amt # check silver cost try: price = self.get_recipe_price(recipe) except ValueError: caller.msg("That recipe does not have a price defined.") return cost = recipe.additional_cost + invest + price if cost < 0 or price < 0: errmsg = "For %s at %s, recipe %s, cost %s, price %s" % ( caller, caller.location, recipe.id, cost, price, ) raise ValueError(errmsg) if not caller.check_permstring("builders"): if caller.db.currency < cost: caller.msg( "The recipe costs %s on its own, and you are trying to spend an additional %s." % (recipe.additional_cost, invest)) if price: caller.msg( "The additional price charged by the crafter for this recipe is %s." % price) caller.msg("You need %s silver total, and have only %s." % (cost, caller.db.currency)) return pmats = caller.player.Dominion.assets.materials # add up the total cost of the materials we're using for later realvalue = 0 for mat in mats: try: c_mat = CraftingMaterialType.objects.get(id=mat) except CraftingMaterialType.DoesNotExist: inform_staff( "Attempted to craft using material %s which does not exist." % mat) self.msg( "One of the materials required no longer seems to exist. Informing staff." ) return try: pmat = pmats.get(type=c_mat) if pmat.amount < mats[mat]: caller.msg("You need %s of %s, and only have %s." % (mats[mat], c_mat.name, pmat.amount)) return realvalue += c_mat.value * mats[mat] except CraftingMaterials.DoesNotExist: caller.msg("You do not have any of the material %s." % c_mat.name) return # check if they have enough action points if not caller.player_ob.pay_action_points(2 + action_points): self.msg( "You do not have enough action points left to craft that." ) return # pay the money caller.pay_money(cost) # we're still here, so we have enough materials. spend em all for mat in mats: cmat = CraftingMaterialType.objects.get(id=mat) pmat = pmats.get(type=cmat) pmat.amount -= mats[mat] pmat.save() else: realvalue = recipe.value # determine difficulty modifier if we tossed in more money ability = get_ability_val(crafter, recipe) diffmod = get_difficulty_mod(recipe, invest, action_points, ability) # do crafting roll roll = do_crafting_roll(crafter, recipe, diffmod, room=caller.location) # get type from recipe otype = recipe.type # create object if otype == "wieldable": obj, quality = create_weapon(recipe, roll, proj, caller) elif otype == "wearable": obj, quality = create_wearable(recipe, roll, proj, caller) elif otype == "place": obj, quality = create_place(recipe, roll, proj, caller) elif otype == "book": obj, quality = create_book(recipe, roll, proj, caller) elif otype == "container": obj, quality = create_container(recipe, roll, proj, caller) elif otype == "decorative_weapon": obj, quality = create_decorative_weapon( recipe, roll, proj, caller) elif otype == "wearable_container": obj, quality = create_wearable_container( recipe, roll, proj, caller) elif otype == "perfume": obj, quality = create_consumable(recipe, roll, proj, caller, PERFUME) elif otype == "disguise": obj, quality = create_mask(recipe, roll, proj, caller, proj[6]) else: obj, quality = create_generic(recipe, roll, proj, caller) # finish stuff universal to all crafted objects obj.desc = proj[2] obj.save() self.apply_templates_to(obj) obj.item_data.materials = mats obj.item_data.recipe = recipe.id obj.item_data.adorns = proj[3] obj.item_data.crafted_by = crafter obj.item_data.size = int(recipe.resultsdict.get("volume", 0)) self.pay_owner( price, "%s has crafted '%s', a %s, at your shop and you earn %s silver." % (caller, obj, recipe.name, price), ) try: if proj[5]: obj.item_data.translation = proj[5] except IndexError: pass cnoun = "You" if caller == crafter else crafter caller.msg("%s created %s." % (cnoun, obj.name)) quality = QUALITY_LEVELS[quality] caller.msg("It is of %s quality." % quality) caller.db.crafting_project = None return
def func(self): """Implement the command""" caller = self.caller args = self.args switches = self.switches apps = get_apps_manager(caller) if not apps: caller.msg( "Apps manager not found! Please inform the administrators.") return if not args and not switches: # '@app' # List all pending applications all_apps = apps.view_all_apps() if not all_apps: caller.msg("No applications found.") return # application[9] field is 'True' if pending/open pend_list = [app for app in all_apps.values() if app[9]] if not pend_list: caller.msg("No pending applications found.") return # app = [app_num, char_ob, email, date_submit, application_string, # gm_ob, date_answer, gm_notes, approval, pending] table = prettytable.PrettyTable( ["{w#", "{wCharacter", "{wEmail", "{wDate"]) for app in pend_list: table.add_row( [app[0], app[1].key.capitalize(), app[2], app[3]]) caller.msg("{wApplications for Characters pending approval:\n%s" % table) caller.msg("To view a particular application, @app <app number>") caller.msg("To view closed applications, use @app/old") return if args and not switches and not args.isdigit(): # '@app <character>' # List all pending apps for a particular character apps_for_char = apps.view_all_apps_for_char(args) if not apps_for_char: caller.msg("No applications found.") return pend_list = [ob for ob in apps_for_char if ob[9]] if not pend_list: caller.msg("No pending applications found.") return # app = [app_num, char_ob, email, date_submit, application_string, gm_ob, # date_answer, gm_notes, approval, pending] table = prettytable.PrettyTable( ["{w#", "{wCharacter", "{wEmail", "{wDate"]) for app in pend_list: table.add_row( [app[0], app[1].key.capitalize(), app[2], app[3]]) caller.msg("{wPending applications for %s:\n%s" % (args, table)) caller.msg("To view a specific application, @app <app number>") return if args and args.isdigit() and (not switches or 'old' in switches): # '@app <#> # List a given ticket by app = apps.view_app(int(args)) if not app: caller.msg("No application by that number for that character.") return email = app[2] alts = RosterEntry.objects.filter(current_account__email=email) caller.msg("{wCharacter:{n %s" % app[1].key.capitalize()) caller.msg("{wApp Email:{n %s" % email) if alts: caller.msg("{wCurrent characters:{n %s" % ", ".join(str(ob) for ob in alts)) caller.msg("{wDate Submitted:{n %s" % app[3]) caller.msg("{wApplication:{n %s" % app[4]) if not app[9]: caller.msg("{wGM:{n %s" % app[5]) caller.msg("{wDate Answered:{n %s" % app[6]) caller.msg("{wGM Notes:{n %s" % app[7]) caller.msg("{wApproved:{n %s" % app[8]) return if 'approve' in switches: # @app/approve <#>=<notes> # mark a character as approved, then send an email to the player if not self.lhs or not self.rhs or not self.lhs.isdigit(): caller.msg("Usage: @app/approve <#>=<notes>") return app = apps.view_app(int(self.lhs)) if apps.close_app(int(self.lhs), caller, self.rhs, True): caller.msg("Application successfully approved.") if app and app[1]: inform_staff( "{w%s has approved %s's application.{n" % (caller.key.capitalize(), app[1].key.capitalize())) try: entry = RosterEntry.objects.get( character__id=app[1].id, player__id=app[1].player_ob.id) active_roster = Roster.objects.get(name="Active") entry.roster = active_roster try: account = PlayerAccount.objects.get(email=app[2]) except PlayerAccount.DoesNotExist: account = PlayerAccount.objects.create(email=app[2]) entry.current_account = account entry.save() # clear cache so the character is moved correctly entry.character.flush_from_cache(force=True) entry.player.flush_from_cache(force=True) from datetime import datetime date = datetime.now() if not AccountHistory.objects.filter( entry=entry, account=account, end_date__isnull=True).exists(): AccountHistory.objects.create(entry=entry, account=account, start_date=date) # make sure all their Attributes are clean for new player from server.utils.arx_utils import post_roster_cleanup, reset_to_default_channels post_roster_cleanup(entry) reset_to_default_channels(entry.player) try: from commands.cmdsets.starting_gear import setup_gear_for_char if not entry.character: raise ValueError( "No character found for setup gear") setup_gear_for_char(entry.character) except ValueError: traceback.print_exc() except (RosterEntry.DoesNotExist, RosterEntry.MultipleObjectsReturned, Roster.DoesNotExist, Roster.MultipleObjectsReturned, AttributeError, ValueError, TypeError): print( "Error when attempting to mark closed application as active." ) traceback.print_exc() try: from world.dominion.setup_utils import setup_dom_for_char setup_dom_for_char(app[1]) except (ValueError, TypeError): # will throw an exception if Dominion already set up pass try: bb = BBoard.objects.get(db_key__iexact="Roster Changes") msg = "%s now has a new player and is on the active roster." % app[ 1] url = "http://play.arxmush.org" + app[1].get_absolute_url() msg += "\nCharacter page: %s" % url subject = "%s now active" % app[1] bb.bb_post(self.caller, msg, subject=subject, poster_name="Roster") except BBoard.DoesNotExist: self.msg("Board not found for posting announcement") return else: caller.msg("Application closure failed.") return if 'delete' in switches or 'del' in switches: try: apps.delete_app(caller, int(self.args)) return except (ValueError, TypeError): caller.msg("Could not delete an app for value of %s." % self.args) return if 'deny' in switches: # @app/deny <#>=<notes> # mark a character as declined, then send an email to the player if not self.lhs or not self.rhs or not self.lhs.isdigit(): caller.msg("Usage: @app/deny <#>=<notes>") return if apps.close_app(int(self.lhs), caller, self.rhs, False): caller.msg("Application successfully declined.") app = apps.view_app(int(self.lhs)) if app and app[1]: inform_staff( "{w%s has declined %s's application.{n" % (caller.key.capitalize(), app[1].key.capitalize())) return else: caller.msg("Application closure failed.") return if 'old' in switches: # List all non-pending applications all_apps = apps.view_all_apps() if not all_apps: caller.msg("No applications found.") return # application[9] field is 'True' if pending/open pend_list = [_app for _app in all_apps.values() if not _app[9]] pend_list.sort(key=lambda appl: appl[0]) if not pend_list: caller.msg("No closed applications found.") return if not self.args: pend_list = pend_list[-20:] else: try: pend_list = pend_list[-int(self.args):] except (TypeError, ValueError): caller.msg("Could not display entries for that range.") return # app = [app_num, char_ob, email, date_submit, application_string, gm_ob, # date_answer, gm_notes, approval, pending] table = prettytable.PrettyTable( ["{w#", "{wCharacter", "{wEmail", "{wDate", "{wApproved"]) for app in pend_list: table.add_row([ app[0], app[1].key.capitalize(), app[2], app[3][:9], str(app[8]) ]) caller.msg("{wOld/Closed applications for characters:\n%s" % table) caller.msg("To view a particular application, @app <app number>") return pass if 'oldchar' in switches: apps_for_char = apps.view_all_apps_for_char(args) if not apps_for_char: caller.msg("No applications found.") return pend_list = [ob for ob in apps_for_char if not ob[9]] if not pend_list: caller.msg("No closed applications found.") return # app = [app_num, char_ob, email, date_submit, application_string, gm_ob, # date_answer, gm_notes, approval, pending] table = prettytable.PrettyTable([ "{w#", "{wCharacter", "{wEmail", "{wDate", "{wGM", "{wApproved" ]) for app in pend_list: table.add_row([ app[0], app[1].key.capitalize(), app[2], app[3][:9], app[5].key, str(app[8]) ]) caller.msg("{wOld/Closed applications for %s:\n%s" % (args, table)) caller.msg("To view a particular application, @app <app number>") return if 'email' in switches: apps_for_email = apps.view_apps_for_email(args) if not apps_for_email: caller.msg("No applications found.") return table = prettytable.PrettyTable( ["{w#", "{wCharacter", "{wEmail", "{wDate"]) for app in apps_for_email: table.add_row( [app[0], app[1].key.capitalize(), app[2], app[3]]) caller.msg("{wApplications for %s:\n%s" % (args, table)) caller.msg("To view a particular application, @app <app number>") return if 'fixemail' in switches: try: if apps.fix_email(int(self.lhs), caller, self.rhs): caller.msg("App email changed to %s." % self.rhs) return except (TypeError, ValueError, AttributeError): caller.msg("Must provide an app # and an email address.") return if 'resend' in switches: try: apps.resend(int(self.lhs), caller) return except (ValueError, TypeError, AttributeError): caller.msg("Must provide a valid app #.") return caller.msg("Invalid switch for @app.")
def func(self): """Implement the command""" caller = self.caller try: dompc = PlayerOrNpc.objects.get(player=caller.player) AssetOwner.objects.get(player=dompc) except PlayerOrNpc.DoesNotExist: # dominion not set up on player setup_dom_for_char(caller) except AssetOwner.DoesNotExist: # assets not initialized on player setup_dom_for_char(caller, create_dompc=False) if not self.args and not self.switches: project = caller.db.startgear_project if project: caller.msg(self.display_project(project)) caller.msg("{wTo finish it, use /finish.") caller.msg( "You have the equivalent of {w%s{n silver remaining to spend on gear." % caller.db.startgear_val) return # start a crafting project if not self.switches: try: recipe = CraftingRecipe.objects.get(name__iexact=self.lhs) except CraftingRecipe.DoesNotExist: caller.msg("No recipe found by the name %s." % self.lhs) return # proj = [id, name, desc, adorns, altdesc] proj = [recipe.id, "", "", {}, ""] cost = recipe.value caller.msg("Its cost is {w%s{n." % cost) if cost > caller.db.startgear_val: caller.msg( "{rYou only have {w%s{r silver remaining for gear.{n" % caller.db.startgear_val) return caller.db.startgear_project = proj caller.msg("{wYou have started to craft:{n %s." % recipe.name) caller.msg("You will have {w%s{n remaining after finishing." % (caller.db.startgear_val - cost)) caller.msg( "{wTo finish it, use /finish after you set its name and description." ) caller.msg("{wTo abandon this, use /abandon.{n") return proj = caller.db.startgear_project if not proj and "refundremainder" not in self.switches: caller.msg("You have no crafting project.") return if "adorn" in self.switches: if not (self.lhs and self.rhs): caller.msg("Usage: craft/adorn <material>=<amount>") return try: mat = CraftingMaterialType.objects.get(name__iexact=self.lhs) amt = int(self.rhs) except CraftingMaterialType.DoesNotExist: caller.msg("No material named %s." % self.lhs) return except CraftingMaterialType.MultipleObjectsReturned: caller.msg("More than one match. Please be more specific.") return except (TypeError, ValueError): caller.msg("Amount must be a number.") return if amt < 1: caller.msg("Amount must be positive.") return recipe = CraftingRecipe.objects.get(id=proj[0]) if not recipe.allow_adorn: caller.msg( "This recipe does not allow for additional materials to be used." ) return cost = recipe.value adorns = proj[3] or {} adorns[mat.id] = amt for adorn_id in adorns: mat = CraftingMaterialType.objects.get(id=adorn_id) amt = adorns[adorn_id] cost += mat.value * amt caller.msg("The cost of your item is now %s." % cost) if cost > caller.db.startgear_val: caller.msg( "You cannot afford those adorns. Removing them all.") proj[3] = {} return proj[3] = adorns caller.db.crafting_project = proj caller.msg( "Additional materials: %s" % ", ".join("%s: %s" % (CraftingMaterialType.objects.get(id=mat).name, amt) for mat, amt in adorns.items())) return if "name" in self.switches: if not self.args: caller.msg("Name it what?") return if not arx_utils.validate_name(self.args): caller.msg("That is not a valid name.") return proj[1] = self.args caller.db.startgear_project = proj caller.msg("Name set to %s." % self.args) return if "desc" in self.switches: if not self.args: caller.msg("Name it what?") return proj[2] = self.args caller.db.startgear_project = proj caller.msg("Desc set to:\n%s" % self.args) return if "altdesc" in self.switches: if not self.args: caller.msg( "Describe them how? This is only used for disguise recipes." ) return proj[4] = self.args caller.msg( "This is only used for disguise recipes. Alternate description set to:\n%s" % self.args) return if "abandon" in self.switches or "abort" in self.switches: caller.msg( "You have abandoned this crafting project. You may now start another." ) caller.attributes.remove("startgear_project") return # do rolls for our crafting. determine quality level, handle forgery stuff if "finish" in self.switches: if not proj[1]: caller.msg("You must give it a name first.") return if not proj[2]: caller.msg("You must write a description first.") return # first, check if we have all the materials required mats = {} recipe = CraftingRecipe.objects.get(id=proj[0]) cost = recipe.value for mat in recipe.required_materials.all(): mats[mat.id] = mats.get(mat.type_id, 0) + mat.amount for adorn in proj[3]: mats[adorn] = mats.get(adorn, 0) + proj[3][adorn] mat = CraftingMaterialType.objects.get(id=adorn) cost += mat.value * proj[3][adorn] if caller.db.startgear_val < cost: caller.msg( "You need %s silver to finish the recipe, and have only %s." % (cost, caller.db.startgear_val)) return caller.db.startgear_val -= cost # quality will always be average roll = 0 # get type from recipe otype = recipe.type # create object crafter = caller if otype == "wieldable": obj, quality = create_weapon(recipe, roll, proj, caller, crafter) elif otype == "wearable": obj, quality = create_wearable(recipe, roll, proj, caller, crafter) elif otype == "place": obj, quality = create_place(recipe, roll, proj, caller, crafter) elif otype == "container": obj, quality = create_container(recipe, roll, proj, caller, crafter) elif otype == "decorative_weapon": obj, quality = create_decorative_weapon( recipe, roll, proj, caller, crafter) elif otype == "wearable_container": obj, quality = create_wearable_container( recipe, roll, proj, caller, crafter) elif otype == "perfume": obj, quality = create_consumable(recipe, roll, proj, caller, PERFUME, crafter) elif otype == "disguise": obj, quality = create_mask(recipe, roll, proj, caller, proj[6], crafter) else: obj, quality = create_generic(recipe, roll, proj, caller, crafter) # finish stuff universal to all crafted objects obj.desc = proj[2] obj.save() for mat_id, amount in proj[3].items(): obj.item_data.add_adorn(mat_id, amount) caller.msg("You created %s." % obj.name) caller.attributes.remove("startgear_project") return if "refundremainder" in self.switches: money = caller.db.currency or 0.0 refund = caller.db.startgear_val money += refund caller.attributes.remove("startgear_val") caller.db.currency = money caller.msg("You receive %s silver coins." % refund) caller.cmdset.delete(StartingGearCmdSet) return caller.msg("Invalid switch.")
def func(self): """Implement the command""" from django.db.models import Q caller = self.caller filters = None if self.args and (not self.switches or 'known' in self.switches): filters = Q(name__iexact=self.args) | Q( skill__iexact=self.args) | Q(ability__iexact=self.args) recipes = CraftingRecipe.objects.filter( known_by__player__player=caller.player) unknown = CraftingRecipe.objects.exclude( known_by__player__player=caller.player).order_by("additional_cost") if filters: recipes = recipes.filter(filters) unknown = unknown.filter(filters) recipes = list(recipes) can_learn = [ob for ob in unknown if ob.access(caller, 'learn')] try: dompc = PlayerOrNpc.objects.get(player=caller.player) except PlayerOrNpc.DoesNotExist: dompc = setup_dom_for_char(caller) if not self.switches: caller.msg("Recipes you know or can learn:") visible = recipes + can_learn self.display_recipes(visible) return if 'known' in self.switches: self.msg("Recipes you know:") self.display_recipes(recipes) return if 'learn' in self.switches: match = None if self.args: match = [ ob for ob in can_learn if ob.name.lower() == self.args.lower() ] if not match: caller.msg("No recipe by that name.") caller.msg("\nRecipes you can learn:") self.display_recipes(can_learn) return match = match[0] cost = match.additional_cost if cost > caller.db.currency: caller.msg("It costs %s to learn %s, and you only have %s." % (cost, match.name, caller.db.currency)) return caller.pay_money(cost) dompc.assets.recipes.add(match) if cost: coststr = " for %s silver" % cost else: coststr = "" caller.msg("You have learned %s%s." % (match.name, coststr)) return if 'info' in self.switches: match = None info = list(can_learn) + list(recipes) if self.args: match = [ ob for ob in info if ob.name.lower() == self.args.lower() ] if not match: caller.msg("No recipe by that name.") caller.msg("Recipes you can get /info on:") self.display_recipes(info) return match = match[0] display = match.display_reqs(dompc, full=True) caller.msg(display, options={'box': True}) return if 'teach' in self.switches: match = None can_teach = [ob for ob in recipes if ob.access(caller, 'teach')] if self.rhs: match = [ ob for ob in can_teach if ob.name.lower() == self.rhs.lower() ] if not match: caller.msg("Recipes you can teach:") self.display_recipes(can_teach) if self.rhs: caller.msg("You entered: %s." % self.rhs) return recipe = match[0] character = caller.search(self.lhs) if not character: return if not recipe.access(character, 'learn'): caller.msg("They cannot learn %s." % recipe.name) return try: dompc = PlayerOrNpc.objects.get(player=character.player) except PlayerOrNpc.DoesNotExist: dompc = setup_dom_for_char(character) if recipe in dompc.assets.recipes.all(): caller.msg("They already know %s." % recipe.name) return dompc.assets.recipes.add(recipe) caller.msg("Taught %s %s." % (character, recipe.name))
def func(self): """Execute command.""" caller = self.caller usemats = True material = None if self.cmdstring == "buy" and not ("economic" in self.switches or "social" in self.switches or "military" in self.switches): # allow for buy/economic, etc. buy switch precludes that, so we # only add it if we don't have the above switches self.switches.append("buy") if self.cmdstring == "sell": # having other switches is misleading. They could think they can sell # other things. if self.switches: caller.msg("Use market/sell or just 'sell' as the command.") return self.switches.append("sell") materials = CraftingMaterialType.objects.filter( value__gte=0).order_by("value") if not caller.check_permstring("builders"): materials = materials.exclude(contraband=True) if not self.args: mult = get_cost_multipler() mtable = prettytable.PrettyTable( ["{wMaterial", "{wCategory", "{wCost"]) for mat in materials: mtable.add_row([mat.name, mat.category, str(mat.value * mult)]) # add other items by hand for mat in other_items: mtable.add_row([mat, other_items[mat][1], other_items[mat][0]]) caller.msg("\n{w" + "=" * 60 + "{n\n%s" % mtable) pmats = OwnedMaterial.objects.filter( owner__player__player=caller.player) if pmats: caller.msg("\n{wYour materials:{n %s" % ", ".join(str(ob) for ob in pmats)) return if not ("economic" in self.switches or "buyeconomic" in self.switches or "social" in self.switches or "military" in self.switches): try: material = materials.get(name__icontains=self.lhs) except CraftingMaterialType.DoesNotExist: if self.lhs not in other_items: caller.msg("No material found for name %s." % self.lhs) return material = OtherMaterial(self.lhs) usemats = False except CraftingMaterialType.MultipleObjectsReturned: try: material = materials.get(name__iexact=self.lhs) except ( CraftingMaterialType.DoesNotExist, CraftingMaterialType.MultipleObjectsReturned, ): caller.msg("Unable to get a unique match for that.") return if "buy" in self.switches: if not usemats: amt = 1 else: try: amt = int(self.rhs) except (ValueError, TypeError): caller.msg("Amount must be a number.") return if amt < 1: caller.msg("Amount must be a positive number") return cost = material.value * amt * get_cost_multipler() try: dompc = caller.player_ob.Dominion except AttributeError: dompc = setup_utils.setup_dom_for_char(caller) # use silver if cost > caller.db.currency: caller.msg( "That would cost %s silver coins, and you only have %s." % (cost, caller.db.currency)) return caller.pay_money(cost) paystr = "%s silver" % cost if usemats: try: mat = dompc.assets.owned_materials.get(type=material) mat.amount += amt mat.save() except OwnedMaterial.DoesNotExist: dompc.assets.owned_materials.create(type=material, amount=amt) else: material.create(caller) caller.msg("You buy %s %s for %s." % (amt, material, paystr)) return if "sell" in self.switches: try: amt = int(self.rhs) except (ValueError, TypeError): caller.msg("Amount must be a number.") return if amt < 1: caller.msg("Must be a positive number.") return if not usemats: caller.msg("The market will only buy raw materials.") return try: dompc = PlayerOrNpc.objects.get(player=caller.player) except PlayerOrNpc.DoesNotExist: dompc = setup_utils.setup_dom_for_char(caller) try: mat = dompc.assets.owned_materials.get(type=material) except OwnedMaterial.DoesNotExist: caller.msg("You don't have any of %s." % material.name) return if mat.amount < amt: caller.msg("You want to sell %s %s, but only have %s." % (amt, material, mat.amount)) return mat.amount -= amt mat.save() money = caller.db.currency or 0.0 sale = amt * material.value / 20 money += sale caller.db.currency = money caller.msg("You have sold %s %s for %s silver coins." % (amt, material.name, sale)) return if "info" in self.switches: msg = "{wInformation on %s:{n %s\n" % (material.name, material.desc) price = material.value * get_cost_multipler() msg += "{wPrice in silver: {c%s{n\n" % price cost = price / 250 if price % 250: cost += 1 msg += "{wPrice in economic resources: {c%s{n" % cost caller.msg(msg) return if ("economic" in self.switches or "military" in self.switches or "social" in self.switches): try: assets = caller.player_ob.Dominion.assets amt = int(self.args) if amt <= 0: raise ValueError except (TypeError, ValueError): caller.msg("Must specify a positive number.") return cost = 500 * amt * get_cost_multipler() if cost > caller.db.currency: caller.msg("That would cost %s and you have %s." % (cost, caller.db.currency)) return caller.pay_money(cost) if "economic" in self.switches: assets.economic += amt elif "social" in self.switches: assets.social += amt elif "military" in self.switches: assets.military += amt assets.save() caller.msg("You have bought %s resources for %s." % (amt, cost)) return caller.msg("Invalid switch.") return
def func(self): """Implement the command""" from django.db.models import Q caller = self.caller all_recipes = CraftingRecipe.objects.all() recipes = all_recipes.filter(known_by__player__player=caller.player) unknown = all_recipes.exclude(known_by__player__player=caller.player) if self.args and (not self.switches or 'known' in self.switches): filters = Q(name__iexact=self.args) | Q( skill__iexact=self.args) | Q(ability__iexact=self.args) recipes = recipes.filter(filters) unknown = unknown.filter(filters) recipes = list(recipes) can_learn = [ob for ob in unknown if ob.access(caller, 'learn')] try: dompc = PlayerOrNpc.objects.get(player=caller.player) except PlayerOrNpc.DoesNotExist: dompc = setup_dom_for_char(caller) if not self.switches: visible = recipes + can_learn self.display_recipes(visible) return if 'known' in self.switches: self.display_recipes(recipes) return if 'learn' in self.switches or 'cost' in self.switches: match = None if self.args: match = [ ob for ob in can_learn if ob.name.lower() == self.args.lower() ] if not match: learn_msg = ("You cannot learn '%s'. " % self.lhs) if self.lhs else "" caller.msg("%sRecipes you can learn:" % learn_msg) self.display_recipes(can_learn) return match = match[0] cost = 0 if caller.check_permstring( 'builders') else match.additional_cost cost_msg = "It will cost %s for you to learn %s." % ( cost or "nothing", match.name) if 'cost' in self.switches: return caller.msg(cost_msg) elif cost > caller.currency: return caller.msg("You have %s silver. %s" % (caller.currency, cost_msg)) caller.pay_money(cost) dompc.assets.recipes.add(match) coststr = (" for %s silver" % cost) if cost else "" caller.msg("You have learned %s%s." % (match.name, coststr)) return if 'info' in self.switches: match = None info = list(can_learn) + list(recipes) if self.args: match = [ ob for ob in info if ob.name.lower() == self.args.lower() ] if not match: caller.msg( "No recipe by that name. Recipes you can get /info on:") self.display_recipes(info) return match = match[0] display = match.display_reqs(dompc, full=True) caller.msg(display, options={'box': True}) return if 'teach' in self.switches: match = None can_teach = [ob for ob in recipes if ob.access(caller, 'teach')] if self.rhs: match = [ ob for ob in can_teach if ob.name.lower() == self.rhs.lower() ] if not match: teach_msg = ("You cannot teach '%s'. " % self.rhs) if self.rhs else "" caller.msg("%sRecipes you can teach:" % teach_msg) self.display_recipes(can_teach) return recipe = match[0] character = caller.search(self.lhs) if not character: return if not recipe.access(character, 'learn'): caller.msg("They cannot learn %s." % recipe.name) return try: dompc = PlayerOrNpc.objects.get(player=character.player) except PlayerOrNpc.DoesNotExist: dompc = setup_dom_for_char(character) if recipe in dompc.assets.recipes.all(): caller.msg("They already know %s." % recipe.name) return dompc.assets.recipes.add(recipe) caller.msg("Taught %s %s." % (character, recipe.name))