package com.planet_ink.coffee_mud.Races; import com.planet_ink.coffee_mud.Libraries.interfaces.*; import com.planet_ink.coffee_mud.Libraries.interfaces.AbilityMapper.AbilityMapping; import com.planet_ink.coffee_mud.core.interfaces.*; import com.planet_ink.coffee_mud.core.*; import com.planet_ink.coffee_mud.core.collections.*; import com.planet_ink.coffee_mud.Abilities.interfaces.*; import com.planet_ink.coffee_mud.Areas.interfaces.*; import com.planet_ink.coffee_mud.Behaviors.interfaces.*; import com.planet_ink.coffee_mud.CharClasses.interfaces.*; import com.planet_ink.coffee_mud.Commands.interfaces.*; import com.planet_ink.coffee_mud.Common.interfaces.*; import com.planet_ink.coffee_mud.Exits.interfaces.*; import com.planet_ink.coffee_mud.Items.interfaces.*; import com.planet_ink.coffee_mud.Locales.interfaces.*; import com.planet_ink.coffee_mud.MOBS.interfaces.*; import com.planet_ink.coffee_mud.Races.interfaces.*; import java.lang.ref.WeakReference; import java.util.*; /* Copyright 2001-2016 Bo Zimmerman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ @SuppressWarnings({"unchecked","rawtypes"}) public class StdRace implements Race { @Override public String ID() { return "StdRace"; } protected static final int[] breatheAnythingArray = new int[0]; protected static final int[] breatheAirArray = new int[] { RawMaterial.RESOURCE_AIR }; protected static final int[] breatheWaterArray = new int[] { RawMaterial.RESOURCE_FRESHWATER, RawMaterial.RESOURCE_SALTWATER }; protected static final int[] breatheAirWaterArray = new int[] { RawMaterial.RESOURCE_FRESHWATER, RawMaterial.RESOURCE_SALTWATER, RawMaterial.RESOURCE_AIR }; protected static final List empty = new ReadOnlyVector(1); // an ey ea he ne ar ha to le fo no gi mo wa ta wi private static final int[] parts = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; protected static final SearchIDList emptyIDs = new CMUniqSortSVec(1); private final int[] agingChart = { 0, 1, 3, 15, 35, 53, 70, 74, 78 }; protected String baseStatChgDesc = null; protected String sensesChgDesc = null; protected String dispChgDesc = null; protected String abilitiesDesc = null; protected String languagesDesc = null; protected Weapon naturalWeapon = null; protected boolean mappedCulturalAbilities = false; protected List<Item> outfitChoices = null; protected List<Weapon> naturalWeaponChoices = null; protected Set<String> naturalAbilImmunities = new HashSet<String>(); protected Map<Integer,SearchIDList<Ability>> racialAbilityMap = null; protected Map<Integer,SearchIDList<Ability>> racialEffectMap = null; private final static String localizedStaticName = CMLib.lang().L("StdRace"); @Override public String name() { return localizedStaticName; } @Override public int shortestMale() { return 24; } @Override public int shortestFemale() { return 24; } @Override public int heightVariance() { return 5; } @Override public int lightestWeight() { return 60; } @Override public int weightVariance() { return 10; } @Override public long forbiddenWornBits() { return 0; } private final static String localizedStaticRacialCat = CMLib.lang().L("Unknown"); @Override public String racialCategory() { return localizedStaticRacialCat; } @Override public boolean isGeneric() { return false; } @Override public boolean classless() { return false; } @Override public boolean leveless() { return false; } @Override public boolean expless() { return false; } @Override public int[] bodyMask() { return parts; } @Override public int[] getAgingChart() { return agingChart; } @Override public int[] getBreathables() { return breatheAirArray; } @Override public boolean useRideClass() { return false; } protected int practicesAtFirstLevel() { return 0; } protected int trainsAtFirstLevel() { return 0; } protected String[] racialEffectNames() { return null; } protected int[] racialEffectLevels() { return null; } protected String[] racialEffectParms() { return null; } protected String[] racialAbilityNames() { return null; } protected int[] racialAbilityLevels() { return null; } protected int[] racialAbilityProficiencies() { return null; } protected boolean[] racialAbilityQuals() { return null; } protected String[] culturalAbilityNames() { return null; } @Override public String[] abilityImmunities() { return CMParms.toStringArray(this.naturalAbilImmunities); } protected int[] culturalAbilityProficiencies() { return null; } protected boolean uncharmable() { return false; } protected boolean destroyBodyAfterUse() { return false; } @Override public int availabilityCode() { return Area.THEME_FANTASY | Area.THEME_SKILLONLYMASK; } @Override public CMObject newInstance() { return this; } @Override public void initializeClass() { } public boolean fertile() { return true; } @Override public CMObject copyOf() { try { final StdRace E=(StdRace)this.clone(); return E; } catch(final CloneNotSupportedException e) { return this; } } public Race healthBuddy() { return this; } @Override public boolean canBreedWith(Race R) { if(!fertile()) return false; if(R==null) return false; if(ID().equals("Human")||R.ID().equals("Human")) return true; return ID().equals(R.ID()); } @Override public void affectPhyStats(Physical affected, PhyStats affectableStats) { } @Override public void affectCharStats(MOB affectedMob, CharStats charStats) { } @Override public String makeMobName(char gender, int age) { switch(age) { case Race.AGE_INFANT: { switch(gender) { case 'M': case 'm': return L("boy infant @x1", name().toLowerCase()); case 'F': case 'f': return L("girl infant @x1", name().toLowerCase()); default: return L("baby @x1", name().toLowerCase()); } } case Race.AGE_TODDLER: { switch(gender) { case 'M': case 'm': return L("baby boy @x1", name().toLowerCase()); case 'F': case 'f': return L("baby girl @x1", name().toLowerCase()); default: return L("baby @x1", name().toLowerCase()); } } case Race.AGE_CHILD: { switch(gender) { case 'M': case 'm': return L("little boy @x1", name().toLowerCase()); case 'F': case 'f': return L("little girl @x1", name().toLowerCase()); default: return L("young @x1", name().toLowerCase()); } } case Race.AGE_YOUNGADULT: case Race.AGE_MATURE: case Race.AGE_MIDDLEAGED: default: { switch(gender) { case 'M': case 'm': return L("male @x1", name().toLowerCase()); case 'F': case 'f': return L("female @x1", name().toLowerCase()); default: return name().toLowerCase(); } } case Race.AGE_OLD: case Race.AGE_VENERABLE: case Race.AGE_ANCIENT: { switch(gender) { case 'M': case 'm': return L("old male @x1",name().toLowerCase()); case 'F': case 'f': return L("old female @x1",name().toLowerCase()); default: return L("old @x1",name().toLowerCase()); } } } } @Override public void agingAffects(MOB mob, CharStats baseStats, CharStats charStats) { if((baseStats.getStat(CharStats.STAT_AGE)>0) &&(!CMSecurity.isAllowed(mob,mob.location(),CMSecurity.SecFlag.IMMORT)) &&(!CMSecurity.isDisabled(CMSecurity.DisFlag.ALL_AGEING))) { switch(baseStats.ageCategory()) { case -1: break; case Race.AGE_INFANT: case Race.AGE_TODDLER: charStats.setStat(CharStats.STAT_SAVE_MIND,charStats.getStat(CharStats.STAT_SAVE_MIND)-10); charStats.setStat(CharStats.STAT_MAX_STRENGTH_ADJ,charStats.getStat(CharStats.STAT_MAX_STRENGTH_ADJ)-2); charStats.setStat(CharStats.STAT_MAX_INTELLIGENCE_ADJ,charStats.getStat(CharStats.STAT_MAX_INTELLIGENCE_ADJ)-4); charStats.setStat(CharStats.STAT_MAX_WISDOM_ADJ,charStats.getStat(CharStats.STAT_MAX_WISDOM_ADJ)-4); break; case Race.AGE_CHILD: charStats.setStat(CharStats.STAT_SAVE_MIND,charStats.getStat(CharStats.STAT_SAVE_MIND)-5); charStats.setStat(CharStats.STAT_MAX_STRENGTH_ADJ,charStats.getStat(CharStats.STAT_MAX_STRENGTH_ADJ)-1); charStats.setStat(CharStats.STAT_MAX_INTELLIGENCE_ADJ,charStats.getStat(CharStats.STAT_MAX_INTELLIGENCE_ADJ)-2); charStats.setStat(CharStats.STAT_MAX_WISDOM_ADJ,charStats.getStat(CharStats.STAT_MAX_WISDOM_ADJ)-2); break; case Race.AGE_YOUNGADULT: case Race.AGE_MATURE: break; case Race.AGE_MIDDLEAGED: charStats.setStat(CharStats.STAT_SAVE_MIND,charStats.getStat(CharStats.STAT_SAVE_MIND)+5); charStats.setStat(CharStats.STAT_MAX_STRENGTH_ADJ,charStats.getStat(CharStats.STAT_MAX_STRENGTH_ADJ)-1); charStats.setStat(CharStats.STAT_MAX_CONSTITUTION_ADJ,charStats.getStat(CharStats.STAT_MAX_CONSTITUTION_ADJ)-1); charStats.setStat(CharStats.STAT_MAX_DEXTERITY_ADJ,charStats.getStat(CharStats.STAT_MAX_DEXTERITY_ADJ)-1); charStats.setStat(CharStats.STAT_MAX_INTELLIGENCE_ADJ,charStats.getStat(CharStats.STAT_MAX_INTELLIGENCE_ADJ)+1); charStats.setStat(CharStats.STAT_MAX_WISDOM_ADJ,charStats.getStat(CharStats.STAT_MAX_WISDOM_ADJ)+1); charStats.setStat(CharStats.STAT_MAX_CHARISMA_ADJ,charStats.getStat(CharStats.STAT_MAX_CHARISMA_ADJ)+1); break; case Race.AGE_OLD: charStats.setStat(CharStats.STAT_SAVE_MIND,charStats.getStat(CharStats.STAT_SAVE_MIND)+10); charStats.setStat(CharStats.STAT_SAVE_UNDEAD,charStats.getStat(CharStats.STAT_SAVE_UNDEAD)-5); charStats.setStat(CharStats.STAT_MAX_STRENGTH_ADJ,charStats.getStat(CharStats.STAT_MAX_STRENGTH_ADJ)-2); charStats.setStat(CharStats.STAT_MAX_CONSTITUTION_ADJ,charStats.getStat(CharStats.STAT_MAX_CONSTITUTION_ADJ)-2); charStats.setStat(CharStats.STAT_MAX_DEXTERITY_ADJ,charStats.getStat(CharStats.STAT_MAX_DEXTERITY_ADJ)-2); charStats.setStat(CharStats.STAT_MAX_INTELLIGENCE_ADJ,charStats.getStat(CharStats.STAT_MAX_INTELLIGENCE_ADJ)+2); charStats.setStat(CharStats.STAT_MAX_WISDOM_ADJ,charStats.getStat(CharStats.STAT_MAX_WISDOM_ADJ)+2); charStats.setStat(CharStats.STAT_MAX_CHARISMA_ADJ,charStats.getStat(CharStats.STAT_MAX_CHARISMA_ADJ)+2); break; case Race.AGE_VENERABLE: charStats.setStat(CharStats.STAT_SAVE_MIND,charStats.getStat(CharStats.STAT_SAVE_MIND)+15); charStats.setStat(CharStats.STAT_SAVE_UNDEAD,charStats.getStat(CharStats.STAT_SAVE_UNDEAD)-25); charStats.setStat(CharStats.STAT_MAX_STRENGTH_ADJ,charStats.getStat(CharStats.STAT_MAX_STRENGTH_ADJ)-3); charStats.setStat(CharStats.STAT_MAX_CONSTITUTION_ADJ,charStats.getStat(CharStats.STAT_MAX_CONSTITUTION_ADJ)-3); charStats.setStat(CharStats.STAT_MAX_DEXTERITY_ADJ,charStats.getStat(CharStats.STAT_MAX_DEXTERITY_ADJ)-3); charStats.setStat(CharStats.STAT_MAX_INTELLIGENCE_ADJ,charStats.getStat(CharStats.STAT_MAX_INTELLIGENCE_ADJ)+3); charStats.setStat(CharStats.STAT_MAX_WISDOM_ADJ,charStats.getStat(CharStats.STAT_MAX_WISDOM_ADJ)+3); charStats.setStat(CharStats.STAT_MAX_CHARISMA_ADJ,charStats.getStat(CharStats.STAT_MAX_CHARISMA_ADJ)+3); break; case Race.AGE_ANCIENT: { final int[] chart=getAgingChart(); final int diff=chart[Race.AGE_ANCIENT]-chart[Race.AGE_VENERABLE]; final int age=baseStats.getStat(CharStats.STAT_AGE)-chart[Race.AGE_ANCIENT]; int num=(diff>0)?(int)Math.abs(Math.floor(CMath.div(age,diff)))-1:1; if(num==0) num=1; if(num>16) num=16; charStats.setStat(CharStats.STAT_SAVE_MIND,charStats.getStat(CharStats.STAT_SAVE_MIND)+20+(5*num)); charStats.setStat(CharStats.STAT_SAVE_UNDEAD,charStats.getStat(CharStats.STAT_SAVE_UNDEAD)-50+15+(5*num)); charStats.setStat(CharStats.STAT_MAX_STRENGTH_ADJ,charStats.getStat(CharStats.STAT_MAX_STRENGTH_ADJ)-(3+(1*num))); charStats.setStat(CharStats.STAT_MAX_CONSTITUTION_ADJ,charStats.getStat(CharStats.STAT_MAX_CONSTITUTION_ADJ)-(3+(num))); charStats.setStat(CharStats.STAT_MAX_DEXTERITY_ADJ,charStats.getStat(CharStats.STAT_MAX_DEXTERITY_ADJ)-(3+(num))); charStats.setStat(CharStats.STAT_MAX_INTELLIGENCE_ADJ,charStats.getStat(CharStats.STAT_MAX_INTELLIGENCE_ADJ)+(3+(num))); charStats.setStat(CharStats.STAT_MAX_WISDOM_ADJ,charStats.getStat(CharStats.STAT_MAX_WISDOM_ADJ)+(3+(num))); charStats.setStat(CharStats.STAT_MAX_CHARISMA_ADJ,charStats.getStat(CharStats.STAT_MAX_CHARISMA_ADJ)+(3+(num))); break; } } } } @Override public void affectCharState(MOB affectedMob, CharState affectableMaxState) { } @Override public boolean okMessage(final Environmental myHost, final CMMsg msg) { if((msg.target()==myHost) &&(msg.tool() instanceof Ability) &&(myHost instanceof MOB)) { if(uncharmable() &&(CMath.bset(((Ability)msg.tool()).flags(),Ability.FLAG_CHARMING))) { msg.source().location().show(msg.source(),myHost,CMMsg.MSG_OK_VISUAL,L("<T-NAME> seem(s) unaffected by the charm magic from <S-NAMESELF>.")); return false; } else if(naturalAbilImmunities.contains(msg.tool().ID())) { return false; } } return true; } @Override public void executeMsg(final Environmental myHost, final CMMsg msg) { // the sex rules if(!(myHost instanceof MOB)) return; final MOB myChar=(MOB)myHost; if((msg.tool() instanceof Social) &&(msg.amITarget(myChar)||(msg.source()==myChar)) &&(myChar.location()==msg.source().location()) &&(msg.sourceMinor()!=CMMsg.TYP_CHANNEL) &&(msg.tool().Name().startsWith("MATE ") ||msg.tool().Name().startsWith("SEX "))) { if(msg.tool().Name().endsWith("SELF")) { if((msg.source()==myChar) &&(fertile()) &&(msg.source().fetchWornItems(Wearable.WORN_LEGS|Wearable.WORN_WAIST,(short)-2048,(short)0).size()==0)) { if(msg.source().maxState().getFatigue()>Long.MIN_VALUE/2) msg.source().curState().adjFatigue(CharState.FATIGUED_MILLIS,msg.source().maxState()); if(myChar.maxState().getFatigue()>Long.MIN_VALUE/2) myChar.curState().adjFatigue(CharState.FATIGUED_MILLIS,myChar.maxState()); final Ability A=CMClass.getAbility("Spell_Blindness"); if(A!=null) A.invoke(myChar,myChar,true,myChar.phyStats().level()); } } else if((msg.target()==myChar) &&(msg.tool().Name().endsWith("<T-NAME>"))) { final boolean srcExhausted=((msg.source().curState().getMovement()<(msg.source().maxState().getMovement()/2)) ||(msg.source().curState().getFatigue()>=CharState.FATIGUED_MILLIS)); final boolean meExhausted=((myChar.curState().getMovement()<(myChar.maxState().getMovement()/2)) ||(myChar.curState().getFatigue()>=CharState.FATIGUED_MILLIS)); if((myChar.charStats().getStat(CharStats.STAT_GENDER)==('F')) &&(msg.source().charStats().getStat(CharStats.STAT_GENDER)==('M')) &&(myChar.fetchWornItems(Wearable.WORN_LEGS|Wearable.WORN_WAIST,(short)-2048,(short)0).size()==0) &&(msg.source().fetchWornItems(Wearable.WORN_LEGS|Wearable.WORN_WAIST,(short)-2048,(short)0).size()==0) &&(msg.source().charStats().getMyRace().canBreedWith(this)) &&((msg.source().charStats().getStat(CharStats.STAT_AGE)==0) ||((msg.source().charStats().ageCategory()>Race.AGE_CHILD) &&(msg.source().charStats().ageCategory()<Race.AGE_OLD))) &&((myChar.charStats().getStat(CharStats.STAT_AGE)==0) ||((myChar.charStats().ageCategory()>Race.AGE_CHILD) &&(myChar.charStats().ageCategory()<Race.AGE_OLD)))) { if(srcExhausted) msg.source().tell(L("You are exhausted!")); else { if(msg.source().maxState().getFatigue()>Long.MIN_VALUE/2) msg.source().curState().adjFatigue(CharState.FATIGUED_MILLIS,msg.source().maxState()); msg.source().curState().adjMovement(-msg.source().maxState().getMovement()/2, msg.source().maxState()); } if(meExhausted) myChar.tell(L("You are exhausted!")); else { if(myChar.maxState().getFatigue()>Long.MIN_VALUE/2) myChar.curState().adjFatigue(CharState.FATIGUED_MILLIS,myChar.maxState()); myChar.curState().adjMovement(-myChar.maxState().getMovement()/2, myChar.maxState()); } if(!srcExhausted && !meExhausted && (CMLib.dice().rollPercentage()<10)) { final Ability A=CMClass.getAbility("Pregnancy"); if((A!=null) &&(myChar.fetchAbility(A.ID())==null) &&(myChar.fetchEffect(A.ID())==null)) A.invoke(msg.source(),myChar,true,0); } } } } } @Override public String arriveStr() { return "arrives"; } @Override public String leaveStr() { return "leaves"; } @Override public void level(MOB mob, List<String> gainedAbilityIDs) { } @Override public int adjustExperienceGain(MOB host, MOB mob, MOB victim, int amount) { return amount; } @Override public int getTickStatus() { return Tickable.STATUS_NOT; } @Override public boolean tick(Tickable myChar, int tickID) { return true; } public String L(final String str, final String ... xs) { return CMLib.lang().fullSessionTranslation(str, xs); } @Override public void startRacing(MOB mob, boolean verifyOnly) { if((!mappedCulturalAbilities) &&(culturalAbilityNames()!=null)) { for(int a=0;a<culturalAbilityNames().length;a++) CMLib.ableMapper().addCharAbilityMapping(ID(),0,culturalAbilityNames()[a],false); mappedCulturalAbilities=true; } for(final Ability A : racialAbilities(mob)) { if((A!=null) &&((A.classificationCode()&Ability.ALL_ACODES)==Ability.ACODE_LANGUAGE)) { A.autoInvocation(mob, false); A.invoke(mob,mob,false,0); break; } } if(!verifyOnly) { if(mob.basePhyStats().level()<=1) { mob.setPractices(mob.getPractices()+practicesAtFirstLevel()); mob.setTrains(mob.getTrains()+trainsAtFirstLevel()); } setHeightWeight(mob.basePhyStats(),(char)mob.baseCharStats().getStat(CharStats.STAT_GENDER)); final boolean isChild=CMLib.flags().isChild(mob); final boolean isAnimal=CMLib.flags().isAnimalIntelligence(mob); final boolean isMonster=mob.isMonster(); if((culturalAbilityNames()!=null)&&(culturalAbilityProficiencies()!=null) &&(culturalAbilityNames().length==culturalAbilityProficiencies().length)) { for(int a=0;a<culturalAbilityNames().length;a++) { Ability A=CMClass.getAbility(culturalAbilityNames()[a]); if(A!=null) { A.setProficiency(culturalAbilityProficiencies()[a]); mob.addAbility(A); A.autoInvocation(mob, false); if(((A.classificationCode()&Ability.ALL_ACODES)==Ability.ACODE_LANGUAGE) &&(isMonster) &&(!isChild)) { if(A.proficiency()>0) A.setProficiency(100); A.invoke(mob,mob,false,0); if(isChild && (!isAnimal)) { A=mob.fetchAbility("Common"); if(A==null){ A=CMClass.getAbility("Common"); if(A!=null)mob.addAbility(A);} if(A!=null) A.setProficiency(100); } } } } } } } @Override public Weapon myNaturalWeapon() { if(naturalWeapon==null) naturalWeapon=CMClass.getWeapon("Natural"); return naturalWeapon; } @Override public List<Item> outfit(MOB myChar) { return outfitChoices; } @Override public String healthText(MOB viewer, MOB mob) { return CMLib.combat().standardMobCondition(viewer,mob); } protected Weapon funHumanoidWeapon() { if(naturalWeaponChoices==null) { naturalWeaponChoices=new Vector<Weapon>(); for(int i=1;i<11;i++) { naturalWeapon=CMClass.getWeapon("StdWeapon"); if(naturalWeapon==null) continue; naturalWeapon.setMaterial(RawMaterial.RESOURCE_LEATHER); switch (i) { case 1: case 2: case 3: naturalWeapon.setName(L("a quick punch")); naturalWeapon.setWeaponDamageType(Weapon.TYPE_BASHING); break; case 4: naturalWeapon.setName(L("fingernails and teeth")); naturalWeapon.setWeaponDamageType(Weapon.TYPE_PIERCING); break; case 5: naturalWeapon.setName(L("an elbow")); naturalWeapon.setWeaponDamageType(Weapon.TYPE_BASHING); break; case 6: naturalWeapon.setName(L("a backhand")); naturalWeapon.setWeaponDamageType(Weapon.TYPE_BASHING); break; case 7: naturalWeapon.setName(L("a strong jab")); naturalWeapon.setWeaponDamageType(Weapon.TYPE_BASHING); break; case 8: naturalWeapon.setName(L("a stinging punch")); naturalWeapon.setWeaponDamageType(Weapon.TYPE_BASHING); break; case 9: if(bodyMask()[Race.BODY_LEG]>0) naturalWeapon.setName(L("a knee")); else if(bodyMask()[Race.BODY_GILL]>0) naturalWeapon.setName(L("a fin")); else naturalWeapon.setName(L("a limb")); naturalWeapon.setWeaponDamageType(Weapon.TYPE_BASHING); break; case 10: naturalWeapon.setName(L("a head butt")); naturalWeapon.setWeaponDamageType(Weapon.TYPE_BASHING); break; } naturalWeapon.setUsesRemaining(1000); naturalWeaponChoices.add(naturalWeapon); } } if(naturalWeaponChoices.size()>0) return naturalWeaponChoices.get(CMLib.dice().roll(1,naturalWeaponChoices.size(),-1)); return CMClass.getWeapon("Natural"); } @Override public List<RawMaterial> myResources() { return new Vector<RawMaterial>(); } @Override public void setHeightWeight(PhyStats stats, char gender) { int weightModifier=0; if(weightVariance()>0) weightModifier=CMLib.dice().roll(1,weightVariance(),0); stats.setWeight(lightestWeight()+weightModifier); int heightModifier=0; if(heightVariance()>0) { if(weightModifier>0) { final double variance=CMath.div(weightModifier,weightVariance()); heightModifier=(int)Math.round(CMath.mul(heightVariance(),variance)); } else heightModifier=CMLib.dice().roll(1,heightVariance(),0); } if (gender == 'M') stats.setHeight(shortestMale()+heightModifier); else stats.setHeight(shortestFemale()+heightModifier); } @Override public int compareTo(CMObject o) { return CMClass.classID(this).compareToIgnoreCase(CMClass.classID(o)); } protected RawMaterial makeResource(String name, int type) { final PhysicalAgent A = CMLib.materials().makeResource(type,ID(),true,name); if(A instanceof RawMaterial) return (RawMaterial)A; return null; } @Override public DeadBody getCorpseContainer(MOB mob, Room room) { if(room==null) room=mob.location(); final DeadBody bodyI=(DeadBody)CMClass.getItem("Corpse"); if(mob.isMonster()) { if((mob.amFollowing()!=null) &&((!mob.amFollowing().isMonster())||(!mob.amUltimatelyFollowing().isMonster()))) bodyI.setSavedMOB((MOB)mob.copyOf()); } bodyI.setCharStats((CharStats)mob.baseCharStats().copyOf()); bodyI.basePhyStats().setLevel(mob.basePhyStats().level()); bodyI.basePhyStats().setWeight(mob.basePhyStats().weight()); bodyI.setIsPlayerCorpse(!mob.isMonster()); bodyI.setTimeOfDeath(System.currentTimeMillis()); bodyI.setMobPKFlag(mob.isAttributeSet(MOB.Attrib.PLAYERKILL)); bodyI.setName(L("the body of @x1",mob.Name().replace('\'','`'))); bodyI.setMobName(mob.Name().replace('\'','`')); bodyI.setMobHash(mob.hashCode()); bodyI.setMobDescription(mob.description().replace('\'','`')); bodyI.setDisplayText(L("the body of @x1 lies here.",mob.Name().replace('\'','`'))); final Ability ageA=mob.fetchEffect("Age"); if(ageA!=null) bodyI.addNonUninvokableEffect(ageA); if(room!=null) { final ItemPossessor.Expire expireCode; if(mob.isMonster() && (mob.playerStats()==null)) expireCode=ItemPossessor.Expire.Monster_Body; else expireCode=ItemPossessor.Expire.Player_Body; room.addItem(bodyI,expireCode); } bodyI.setIsDestroyAfterLooting(destroyBodyAfterUse()); bodyI.recoverPhyStats(); for(final Enumeration<Ability> a=mob.effects();a.hasMoreElements();) { final Ability A=a.nextElement(); if((A!=null)&&(A instanceof DiseaseAffect)) { if((CMath.bset(((DiseaseAffect)A).spreadBitmap(),DiseaseAffect.SPREAD_CONSUMPTION)) ||(CMath.bset(((DiseaseAffect)A).spreadBitmap(),DiseaseAffect.SPREAD_CONTACT))) bodyI.addNonUninvokableEffect((Ability)A.copyOf()); } } final Vector<Item> items=new Vector<Item>(); CMLib.beanCounter().getTotalAbsoluteNativeValue(mob); // converts mob.get-Money(); if(mob.getMoneyVariation()>0.0) CMLib.beanCounter().addMoney(mob, Math.random()*mob.getMoneyVariation()); else if(mob.getMoneyVariation()<0.0) CMLib.beanCounter().subtractMoney(mob, -(Math.random()*mob.getMoneyVariation())); final Hashtable<Item,Container> containerMap=new Hashtable<Item,Container>(); final Hashtable<Item,Container> itemMap=new Hashtable<Item,Container>(); final LinkedList<Item> itemsToGo=new LinkedList<Item>(); for(int i=0;i<mob.numItems();i++) { final Item thisItem=mob.getItem(i); if(thisItem != null) itemsToGo.add(thisItem); } for(Item thisItem : itemsToGo) { if((thisItem!=null)&&(thisItem.isSavable())) { if(mob.isMonster()) { Item newItem=CMLib.utensils().isRuinedLoot(mob,thisItem); if(newItem==null) continue; if(newItem==thisItem) newItem=(Item)thisItem.copyOf(); if(newItem != null) { if(newItem instanceof Container) itemMap.put(thisItem,(Container)newItem); if(thisItem.container()!=null) containerMap.put(thisItem,thisItem.container()); newItem.setContainer(null); newItem.setExpirationDate( System.currentTimeMillis() + CMProps.getIntVar( CMProps.Int.EXPIRE_MONSTER_EQ )* TimeManager.MILI_HOUR ); newItem.recoverPhyStats(); thisItem=newItem; } } else mob.delItem(thisItem); thisItem.unWear(); if(thisItem.container()==null) thisItem.setContainer(bodyI); if(room!=null) room.addItem(thisItem); items.addElement(thisItem); } else if(thisItem!=null) mob.delItem(thisItem); } itemsToGo.clear(); final Item dropItem=CMLib.catalog().getDropItem(mob,false); if(dropItem!=null) { dropItem.unWear(); if(dropItem.container()==null) dropItem.setContainer(bodyI); if(room!=null) room.addItem(dropItem); items.addElement(dropItem); } for(final Enumeration e=itemMap.keys();e.hasMoreElements();) { final Item oldItem=(Item)e.nextElement(); final Item newItem=itemMap.get(oldItem); final Item oldContainer=containerMap.get(oldItem); if((oldContainer!=null)&&(newItem!=null)) newItem.setContainer(itemMap.get(oldContainer)); } if(destroyBodyAfterUse()) { for(Item I : myResources()) { if(I!=null) { I=(Item)I.copyOf(); I.setContainer(bodyI); if(room!=null) room.addItem(I,ItemPossessor.Expire.Monster_EQ); } } } else if((room != null)&&(room.isContent(bodyI))) { // remove duplicate already looted corpses for(int i=0;i<room.numItems();i++) { final Item thisItem=mob.getItem(i); if((thisItem instanceof DeadBody) &&(thisItem.Name().equals(bodyI.Name())) &&(((DeadBody)thisItem).getContents().size()==0)) { thisItem.destroy(); break; } } } return bodyI; } @Override public int numRacialEffects(MOB mob) { return racialEffectsList(mob).size(); } @Override public ChameleonList<Ability> racialEffects(final MOB mob) { final StdRace myRace = this; final List<Ability> myList=racialEffectsList(mob); final List<Ability> finalV=new Vector<Ability>(myList.size()); for(final Ability A : myList) { A.makeNonUninvokable(); A.setSavable(false); // must come AFTER the above A.setAffectedOne(mob); finalV.add(A); } final ChameleonList<Ability> finalFinalV; if(mob==null) { finalFinalV = new ChameleonList<Ability>(finalV, new ChameleonList.Signaler<Ability>(myList) { @Override public boolean isDeprecated() { return false; } @Override public void rebuild(final ChameleonList<Ability> me) { } }); } else { finalFinalV = new ChameleonList(finalV, new ChameleonList.Signaler<Ability>(myList) { @Override public boolean isDeprecated() { if((mob.amDestroyed()) ||(mob.charStats().getMyRace() != myRace) || (racialEffectsList(mob) != oldReferenceListRef.get())) return true; return false; } @Override public void rebuild(final ChameleonList<Ability> me) { if(mob.amDestroyed()) oldReferenceListRef=new WeakReference<List<Ability>>(empty); else { final ChameleonList<Ability> newList = mob.charStats().getMyRace().racialEffects(mob); me.changeMeInto(newList); } } }); } return finalFinalV; } public final List<Ability> racialEffectsList(final MOB mob) { if(racialEffectNames()==null) return empty; if((racialEffectMap==null) &&(racialEffectNames()!=null) &&(racialEffectLevels()!=null) &&(racialEffectParms()!=null)) racialEffectMap=new Hashtable<Integer,SearchIDList<Ability>>(); if(racialEffectMap==null) return empty; final Integer level; if(mob!=null) level=Integer.valueOf(mob.phyStats().level()); else level=Integer.valueOf(Integer.MAX_VALUE); if(racialEffectMap.containsKey(level)) return racialEffectMap.get(level); final CMUniqSortSVec<Ability> finalV = new CMUniqSortSVec<Ability>(); for(int v=0;v<racialEffectLevels().length;v++) { if((racialEffectLevels()[v]<=level.intValue()) &&(racialEffectNames().length>v) &&(racialEffectParms().length>v)) { final Ability A=CMClass.getAbility(racialEffectNames()[v]); if(A!=null) { // mob was set to null here to make the cache map actually relevant .. see caching below A.setProficiency(CMLib.ableMapper().getMaxProficiency((MOB)null,true,A.ID())); A.setMiscText(racialEffectParms()[v]); A.makeNonUninvokable(); A.setSavable(false); // must go AFTER the ablve finalV.add(A); } } } finalV.trimToSize(); racialEffectMap.put(level, finalV); return finalV; } @Override public Race makeGenRace() { final Race GR=(Race)CMClass.getRace("GenRace").copyOf(); GR.setRacialParms("<RACE><ID>"+ID()+"</ID><NAME>"+name()+"</NAME></RACE>"); GR.setStat("CAT",racialCategory()); GR.setStat("BWEIGHT",""+lightestWeight()); GR.setStat("VWEIGHT",""+weightVariance()); GR.setStat("MHEIGHT",""+shortestMale()); GR.setStat("FHEIGHT",""+shortestFemale()); GR.setStat("WEAR",""+forbiddenWornBits()); GR.setStat("AVAIL",""+availabilityCode()); GR.setStat("VHEIGHT",""+heightVariance()); GR.setStat("PLAYER",""+CMProps.getIntVar(CMProps.Int.MUDTHEME)); GR.setStat("LEAVE",leaveStr()); GR.setStat("ARRIVE",arriveStr()); GR.setStat("HEALTHRACE",CMClass.classID(this)); GR.setStat("EVENTRACE",CMClass.classID(this)); GR.setStat("BODYKILL",""+destroyBodyAfterUse()); GR.setStat("HELP",""+CMLib.help().getHelpText(name(),null,false)); GR.setStat("AGING",CMParms.toListString(getAgingChart())); GR.setStat("CANRIDE", ""+useRideClass()); for(int i=0;i<Race.BODYPARTSTR.length;i++) GR.bodyMask()[i]=bodyMask()[i]; Weapon W=myNaturalWeapon(); final Weapon NW=CMClass.getWeapon("Natural"); if((W!=null)&&(W!=NW)) { if(!W.isGeneric()) { final Weapon W2=CMClass.getWeapon("GenWeapon"); W2.setName(W.name()); W2.setWeaponClassification(W.weaponClassification()); W2.setWeaponDamageType(W.weaponDamageType()); W2.basePhyStats().setDamage(W.phyStats().damage()); W2.basePhyStats().setAttackAdjustment(W.phyStats().attackAdjustment()); W2.recoverPhyStats(); W2.text(); W=W2; } GR.setStat("WEAPONCLASS",W.ID()); GR.setStat("WEAPONXML",W.text()); } GR.setStat("WEAPONRACE",getClass().getName()); final PhyStats RS=(PhyStats)CMClass.getCommon("DefaultPhyStats"); RS.setAllValues(0); final MOB fakeMOB=CMClass.getFactoryMOB(); affectPhyStats(fakeMOB,RS); RS.setRejuv(PhyStats.NO_REJUV); GR.setStat("ESTATS",CMLib.coffeeMaker().getPhyStatsStr(RS)); final CharStats S1=(CharStats)CMClass.getCommon("DefaultCharStats"); S1.setAllValues(0); final CharStats S2=(CharStats)CMClass.getCommon("DefaultCharStats"); S2.setAllValues(10); final CharStats S3=(CharStats)CMClass.getCommon("DefaultCharStats"); S3.setAllValues(11); final CharStats SETSTAT=(CharStats)CMClass.getCommon("DefaultCharStats"); SETSTAT.setAllValues(0); final CharStats ADJSTAT=(CharStats)CMClass.getCommon("DefaultCharStats"); ADJSTAT.setAllValues(0); affectCharStats(fakeMOB,S1); affectCharStats(fakeMOB,S2); affectCharStats(fakeMOB,S3); for(final int i: CharStats.CODES.ALLCODES()) { if(i!=CharStats.STAT_AGE) { if(CharStats.CODES.isBASE(i)) { final int max = CharStats.CODES.toMAXBASE(i); if((S2.getStat(i)==S3.getStat(i)) &&(S1.getStat(max)!=0)) { SETSTAT.setStat(i,S2.getStat(i)); S1.setStat(max,0); S2.setStat(max,0); S3.setStat(max,0); } else ADJSTAT.setStat(i,S1.getStat(i)); } else ADJSTAT.setStat(i,S1.getStat(i)); } } GR.setStat("ASTATS",CMLib.coffeeMaker().getCharStatsStr(ADJSTAT)); GR.setStat("CSTATS",CMLib.coffeeMaker().getCharStatsStr(SETSTAT)); final CharState CS=(CharState)CMClass.getCommon("DefaultCharState"); CS.setAllValues(0); affectCharState(fakeMOB,CS); GR.setStat("ASTATE",CMLib.coffeeMaker().getCharStateStr(CS)); //CharState STARTCS=(CharState)CMClass.getCommon("DefaultCharState"); STARTCS.setAllValues(0); //startRacing(fakeMOB,falsed); //GR.setStat("STARTASTATE",CMLib.coffeeMaker().getCharStateStr(STARTCS)); GR.setStat("DISFLAGS",""+((classless()?Race.GENFLAG_NOCLASS:0) |(leveless()?Race.GENFLAG_NOLEVELS:0) |(uncharmable()?Race.GENFLAG_NOCHARM:0) |(fertile()?0:Race.GENFLAG_NOFERTILE) |(expless()?Race.GENFLAG_NOEXP:0))); List<RawMaterial> rscs=myResources(); if(rscs==null) rscs=new Vector<RawMaterial>(); String txt=null; Item I; GR.setStat("NUMRSC",""+rscs.size()); for(int i=0;i<rscs.size();i++) { I=rscs.get(i); I.recoverPhyStats(); txt=I.text(); GR.setStat("GETRSCID"+i,I.ID()); GR.setStat("GETRSCPARM"+i,txt); } List<Item> outfit=outfit(null); if(outfit==null) outfit=new Vector<Item>(); GR.setStat("NUMOFT",""+outfit.size()); for(int i=0;i<outfit.size();i++) GR.setStat("GETOFTID"+i,outfit.get(i).ID()); for(int i=0;i<outfit.size();i++) GR.setStat("GETOFTPARM"+i,outfit.get(i).text()); GR.setStat("NUMRABLE",""); if(racialAbilityNames()!=null) { GR.setStat("NUMRABLE",""+racialAbilityNames().length); for(int i=0;i<racialAbilityNames().length;i++) { GR.setStat("GETRABLE"+i,racialAbilityNames()[i]); GR.setStat("GETRABLELVL"+i,""+racialAbilityLevels()[i]); GR.setStat("GETRABLEQUAL"+i,""+racialAbilityQuals()[i]); GR.setStat("GETRABLEPROF"+i,""+racialAbilityProficiencies()[i]); } } GR.setStat("NUMIABLE",""); { int i=0; for(final String ableID : this.naturalAbilImmunities) GR.setStat("GETIABLE"+(i++),ableID); } GR.setStat("NUMCABLE",""); if(culturalAbilityNames()!=null) { GR.setStat("NUMCABLE",""+culturalAbilityNames().length); for(int i=0;i<culturalAbilityNames().length;i++) { GR.setStat("GETCABLE"+i,culturalAbilityNames()[i]); GR.setStat("GETCABLEPROF"+i,""+culturalAbilityProficiencies()[i]); } } GR.setStat("NUMREFF",""); if(racialEffectNames()!=null) { GR.setStat("NUMREFF",""+racialEffectNames().length); for(int i=0;i<racialEffectNames().length;i++) { GR.setStat("GETREFF"+i,racialEffectNames()[i]); GR.setStat("GETREFFLVL"+i,""+racialEffectLevels()[i]); GR.setStat("GETREFFPARM"+i,racialEffectParms()[i]); } } fakeMOB.destroy(); return GR; } @Override public Race mixRace(Race race, String newRaceID, String newRaceName) { final Race GR=(Race)CMClass.getRace("GenRace").copyOf(); Race race1=this; Race race2=race; GR.setRacialParms("<RACE><ID>"+newRaceID+"</ID><NAME>"+newRaceName+"</NAME></RACE>"); if(!race1.isGeneric()) race1=race1.makeGenRace(); if(!race2.isGeneric()) race2=race2.makeGenRace(); final Race nonHuman=(race1.ID().equals("Human"))?race2:race1; final Race otherRace=(nonHuman==race1)?race2:race1; GR.setStat("CAT",nonHuman.racialCategory()); GR.setStat("BWEIGHT",""+((race1.lightestWeight()+race2.lightestWeight())/2)); GR.setStat("VWEIGHT",""+((race1.weightVariance()+race2.weightVariance())/2)); GR.setStat("MHEIGHT",""+((race1.shortestMale()+race2.shortestMale())/2)); GR.setStat("FHEIGHT",""+((race1.shortestFemale()+race2.shortestFemale())/2)); GR.setStat("VHEIGHT",""+((race1.heightVariance()+race2.heightVariance())/2)); GR.setStat("PLAYER",""+CMProps.getIntVar(CMProps.Int.MUDTHEME)); GR.setStat("LEAVE",nonHuman.leaveStr()); GR.setStat("ARRIVE",nonHuman.arriveStr()); GR.setStat("HEALTHRACE",otherRace.getStat("HEALTHRACE")); GR.setStat("EVENTRACE",otherRace.getStat("EVENTRACE")); GR.setStat("WEAPONRACE",otherRace.getStat("WEAPONRACE")); final int[] aging=race1.getAgingChart().clone(); for(int i=0;i<aging.length;i++) { if((aging[i]==Race.YEARS_AGE_LIVES_FOREVER)&&(race2.getAgingChart()[i]==Race.YEARS_AGE_LIVES_FOREVER)) { } else if(((aging[i]==Race.YEARS_AGE_LIVES_FOREVER)&&(race2.getAgingChart()[i]==0)) ||((aging[i]==0)&&(race2.getAgingChart()[i]==Race.YEARS_AGE_LIVES_FOREVER))) aging[i]=0; else if((aging[i]==Race.YEARS_AGE_LIVES_FOREVER)) aging[i]=race2.getAgingChart()[i]*2; else if((race2.getAgingChart()[i]==Race.YEARS_AGE_LIVES_FOREVER)) aging[i]=aging[i]*2; else { aging[i]+=race2.getAgingChart()[i]; aging[i]=aging[i]/2; } } for(int i=aging.length-2;i>=0;i--) { if(aging[i]>aging[i+1]) aging[i]=aging[i+1]; } final long race1worn=CMath.s_long(otherRace.getStat("WEAR")); final long race2worn=CMath.s_long(nonHuman.getStat("WEAR")); long finalWear=0; boolean toggle=false; for(final long wornCode : Wearable.CODES.ALL()) { if(wornCode != Wearable.IN_INVENTORY) { if((!CMath.bset(race1worn,wornCode))&&(!CMath.bset(race2worn,wornCode))) { } else if(CMath.bset(race1worn,wornCode)&&CMath.bset(race2worn,wornCode)) finalWear=finalWear|wornCode; else if(CMath.bset(race1worn,wornCode)) finalWear=finalWear|wornCode; else if(toggle) { finalWear=finalWear|wornCode; toggle=!toggle; } } } GR.setStat("WEAR",""+finalWear); Weapon W=otherRace.myNaturalWeapon(); if(W==null) W=nonHuman.myNaturalWeapon(); if(W!=null) { GR.setStat("WEAPONCLASS",W.ID()); GR.setStat("WEAPONXML",W.text()); } GR.setStat("BODYKILL",""+otherRace.getStat("BODYKILL")); GR.setStat("CANRIDE",""+otherRace.getStat("CANRIDE")); GR.setStat("AGING",CMParms.toListString(aging)); for(int i=0;i<Race.BODYPARTSTR.length;i++) { if((race1.bodyMask()[i]>0)&&(race2.bodyMask()[i]>0)) GR.bodyMask()[i]=((race1.bodyMask()[i]+race2.bodyMask()[i])/2); else if((race1.bodyMask()[i]<=0)&&(race2.bodyMask()[i]>=0)) GR.bodyMask()[i]=race2.bodyMask()[i]; else GR.bodyMask()[i]=race1.bodyMask()[i]; } final PhyStats RS1=(PhyStats)CMClass.getCommon("DefaultPhyStats"); RS1.setAllValues(0); CMLib.coffeeMaker().setPhyStats(RS1,race1.getStat("ESTATS")); final PhyStats RS2=(PhyStats)CMClass.getCommon("DefaultPhyStats"); RS2.setAllValues(0); CMLib.coffeeMaker().setPhyStats(RS2,race2.getStat("ESTATS")); final PhyStats RS=(PhyStats)CMClass.getCommon("DefaultPhyStats"); RS.setAbility((RS1.ability()+RS2.ability())/2); RS.setArmor((RS2.armor()+RS2.armor())/2); RS.setAttackAdjustment((RS1.attackAdjustment()+RS2.attackAdjustment())/2); RS.setSensesMask(RS1.sensesMask()|RS2.sensesMask()); RS.setDisposition(RS1.disposition()); RS.setDamage((RS1.damage()+RS2.damage())/2); RS.setHeight((RS1.height()+RS2.height())/2); RS.setSpeed((RS1.speed()+RS2.speed())/2.0); RS.setWeight((RS1.weight()+RS2.weight())/2); RS.setRejuv(PhyStats.NO_REJUV); GR.setStat("ESTATS",CMLib.coffeeMaker().getPhyStatsStr(RS)); final CharStats SETSTAT1=(CharStats)CMClass.getCommon("DefaultCharStats"); SETSTAT1.setAllValues(0); CMLib.coffeeMaker().setCharStats(SETSTAT1,race1.getStat("CSTATS")); final CharStats SETSTAT2=(CharStats)CMClass.getCommon("DefaultCharStats"); SETSTAT2.setAllValues(0); CMLib.coffeeMaker().setCharStats(SETSTAT2,race2.getStat("CSTATS")); final CharStats SETSTAT=(CharStats)CMClass.getCommon("DefaultCharStats"); SETSTAT.setAllValues(0); final CharStats ADJSTAT1=(CharStats)CMClass.getCommon("DefaultCharStats"); ADJSTAT1.setAllValues(0); CMLib.coffeeMaker().setCharStats(ADJSTAT1,race1.getStat("ASTATS")); final CharStats ADJSTAT2=(CharStats)CMClass.getCommon("DefaultCharStats"); ADJSTAT2.setAllValues(0); CMLib.coffeeMaker().setCharStats(ADJSTAT2,race2.getStat("ASTATS")); final CharStats ADJSTAT=(CharStats)CMClass.getCommon("DefaultCharStats"); ADJSTAT.setAllValues(0); for(final int i: CharStats.CODES.ALLCODES()) { if(CharStats.CODES.isBASE(i)) { SETSTAT.setStat(i,(SETSTAT1.getStat(i)+SETSTAT2.getStat(i))/2); final int newStat=((ADJSTAT1.getStat(i)+ADJSTAT2.getStat(i))/2); if(newStat>5) ADJSTAT.setStat(i,5); else ADJSTAT.setStat(i,newStat); } else if((i!=CharStats.STAT_GENDER)&&(i!=CharStats.STAT_AGE)) ADJSTAT.setStat(i,(ADJSTAT1.getStat(i)+ADJSTAT2.getStat(i))/2); } GR.setStat("ASTATS",CMLib.coffeeMaker().getCharStatsStr(ADJSTAT)); GR.setStat("CSTATS",CMLib.coffeeMaker().getCharStatsStr(SETSTAT)); final CharState CS1=(CharState)CMClass.getCommon("DefaultCharState"); CS1.setAllValues(0); CMLib.coffeeMaker().setCharState(CS1,race1.getStat("ASTATE")); final CharState CS2=(CharState)CMClass.getCommon("DefaultCharState"); CS2.setAllValues(0); CMLib.coffeeMaker().setCharState(CS2,race2.getStat("ASTATE")); final CharState CS=(CharState)CMClass.getCommon("DefaultCharState"); CS.setAllValues(0); CS.setFatigue((CS1.getFatigue()+CS2.getFatigue())/2); CS.setHitPoints((CS1.getHitPoints()+CS2.getHitPoints())/2); CS.setHunger((CS1.getHunger()+CS2.getHunger())/2); CS.setMana((CS1.getMana()+CS2.getMana())/2); CS.setMovement((CS1.getMovement()+CS2.getMovement())/2); CS.setThirst((CS1.getThirst()+CS2.getThirst())/2); GR.setStat("ASTATE",CMLib.coffeeMaker().getCharStateStr(CS)); final CharState STARTCS1=(CharState)CMClass.getCommon("DefaultCharState"); STARTCS1.setAllValues(0); CMLib.coffeeMaker().setCharState(STARTCS1,race1.getStat("STARTASTATE")); final CharState STARTCS2=(CharState)CMClass.getCommon("DefaultCharState"); STARTCS2.setAllValues(0); CMLib.coffeeMaker().setCharState(STARTCS1,race2.getStat("STARTASTATE")); final CharState STARTCS=(CharState)CMClass.getCommon("DefaultCharState"); STARTCS.setAllValues(0); STARTCS.setFatigue((STARTCS1.getFatigue()+STARTCS2.getFatigue())/2); STARTCS.setHitPoints((STARTCS1.getHitPoints()+STARTCS2.getHitPoints())/2); STARTCS.setHunger((STARTCS1.getHunger()+STARTCS2.getHunger())/2); STARTCS.setMana((STARTCS1.getMana()+STARTCS2.getMana())/2); STARTCS.setMovement((STARTCS1.getMovement()+STARTCS2.getMovement())/2); STARTCS.setThirst((STARTCS1.getThirst()+STARTCS2.getThirst())/2); GR.setStat("STARTASTATE",CMLib.coffeeMaker().getCharStateStr(STARTCS)); GR.setStat("DISFLAGS",""+(CMath.s_int(race1.getStat("DISFLAGS"))|CMath.s_int(race2.getStat("DISFLAGS")))); final List<RawMaterial> rscs=nonHuman.myResources(); GR.setStat("NUMRSC",""+rscs.size()); for(int i=0;i<rscs.size();i++) GR.setStat("GETRSCID"+i,((Item)rscs.get(i)).ID()); for(int i=0;i<rscs.size();i++) GR.setStat("GETRSCPARM"+i,((Item)rscs.get(i)).text()); GR.setStat("NUMOFT",""); final Race outfitRace=(nonHuman.outfit(null)!=null)?nonHuman:otherRace; final List<Item> outfit=outfitRace.outfit(null); if((outfit!=null)&&(outfit.size()>0)) { GR.setStat("NUMOFT",""+outfit.size()); for(int i=0;i<outfit.size();i++) GR.setStat("GETOFTID"+i,outfit.get(i).ID()); for(int i=0;i<outfit.size();i++) GR.setStat("GETOFTPARM"+i,outfit.get(i).text()); } race1.racialAbilities(null); race2.racialAbilities(null); final List<AbilityMapping> dvata1=CMLib.ableMapper().getUpToLevelListings(race1.ID(),Integer.MAX_VALUE,true,false); final List<AbilityMapping> dvata2=CMLib.ableMapper().getUpToLevelListings(race2.ID(),Integer.MAX_VALUE,true,false); // kill half of them. for(int i=1;i<dvata1.size();i++) dvata1.remove(i); for(int i=1;i<dvata2.size();i++) dvata2.remove(i); if((dvata1.size()+dvata2.size())>0) GR.setStat("NUMRABLE",""+(dvata1.size()+dvata2.size())); else GR.setStat("NUMRABLE",""); for(int i=0;i<dvata1.size();i++) { GR.setStat("GETRABLE"+i,dvata1.get(i).abilityID()); GR.setStat("GETRABLELVL"+i,""+CMLib.ableMapper().getQualifyingLevel(race1.ID(),false,dvata1.get(i).abilityID())); GR.setStat("GETRABLEQUAL"+i,""+(!CMLib.ableMapper().getDefaultGain(race1.ID(),false,dvata1.get(i).abilityID()))); GR.setStat("GETRABLEPROF"+i,""+CMLib.ableMapper().getDefaultProficiency(race1.ID(),false,dvata1.get(i).abilityID())); } for(int i=0;i<dvata2.size();i++) { GR.setStat("GETRABLE"+(i+dvata1.size()),dvata2.get(i).abilityID()); GR.setStat("GETRABLELVL"+(i+dvata1.size()),""+CMLib.ableMapper().getQualifyingLevel(race2.ID(),false,dvata2.get(i).abilityID())); GR.setStat("GETRABLEQUAL"+(i+dvata1.size()),""+(!CMLib.ableMapper().getDefaultGain(race2.ID(),false,dvata2.get(i).abilityID()))); GR.setStat("GETRABLEPROF"+(i+dvata1.size()),""+CMLib.ableMapper().getDefaultProficiency(race2.ID(),false,dvata2.get(i).abilityID())); } final List<Ability> data=new Vector<Ability>(); boolean skip=false; for(final Ability A : race1.racialEffects(null)) { if(!skip) data.add(A); skip=!skip; } for(final Ability A : race2.racialEffects(null)) { if(!skip) data.add(A); skip=!skip; } if(data.size()>0) GR.setStat("NUMREFF",""+data.size()); else GR.setStat("NUMREFF",""); for(int i=0;i<data.size();i++) { final Ability A=data.get(i); GR.setStat("GETREFF"+i,A.ID()); GR.setStat("GETREFFLVL"+i,""+CMLib.ableMapper().getQualifyingLevel(race1.ID(),false,A.ID())); GR.setStat("GETREFFPARM"+i,""+CMLib.ableMapper().getDefaultProficiency(race1.ID(),false,A.ID())); } final List<String> imms=new XVector<String>(); skip=false; for(final String A : race1.abilityImmunities()) { if(!skip) imms.add(A); skip=!skip; } for(final String A : race2.abilityImmunities()) { if(!skip) imms.add(A); skip=!skip; } GR.setStat("NUMIABLE",""+data.size()); for(int i=0;i<imms.size();i++) { final String AID=imms.get(i); GR.setStat("GETIABLE"+i,AID); } return GR; } @Override public PairVector<String,Integer> culturalAbilities() { final PairVector<String,Integer> ables=new PairVector<String,Integer>(); if((culturalAbilityNames()!=null) &&(culturalAbilityProficiencies()!=null)) { for(int i=0;i<culturalAbilityNames().length;i++) ables.addElement(culturalAbilityNames()[i],Integer.valueOf(culturalAbilityProficiencies()[i])); } return ables; } @Override public SearchIDList<Ability> racialAbilities(MOB mob) { if((racialAbilityMap==null) &&(racialAbilityNames()!=null) &&(racialAbilityLevels()!=null) &&(racialAbilityProficiencies()!=null) &&(racialAbilityQuals()!=null)) { CMLib.ableMapper().delCharMappings(ID()); // necessary for a "clean start" racialAbilityMap=new Hashtable<Integer,SearchIDList<Ability>>(); for(int i=0;i<racialAbilityNames().length;i++) { CMLib.ableMapper().addDynaAbilityMapping( ID(), racialAbilityLevels()[i], racialAbilityNames()[i], racialAbilityProficiencies()[i], "", !racialAbilityQuals()[i], false, ""); } } if(racialAbilityMap==null) return emptyIDs; Integer level=null; if(mob!=null) { level=Integer.valueOf(mob.phyStats().level()); if(level.intValue() == 0) // for mudstarting situations level = Integer.valueOf(mob.basePhyStats().level()); } else level=Integer.valueOf(Integer.MAX_VALUE); if(racialAbilityMap.containsKey(level)) return racialAbilityMap.get(level); if(!CMProps.getBoolVar(CMProps.Bool.MUDSTARTED)) return emptyIDs; final List<AbilityMapper.AbilityMapping> V=CMLib.ableMapper().getUpToLevelListings(ID(),level.intValue(),true,(mob!=null)); final CMUniqSortSVec<Ability> finalV=new CMUniqSortSVec<Ability>(V.size()); for(final AbilityMapper.AbilityMapping able : V) { final Ability A=CMClass.getAbility(able.abilityID()); if(A!=null) { A.setProficiency(CMLib.ableMapper().getDefaultProficiency(ID(),false,A.ID())); A.setSavable(false); A.setMiscText(CMLib.ableMapper().getDefaultParm(ID(),false,A.ID())); finalV.add(A); } } finalV.trimToSize(); finalV.setReadOnly(true); racialAbilityMap.put(level,finalV); return finalV; } @Override public String getStatAdjDesc() { makeStatChgDesc(); return baseStatChgDesc; } @Override public String getSensesChgDesc() { makeStatChgDesc(); return sensesChgDesc; } @Override public String getDispositionChgDesc() { makeStatChgDesc(); return dispChgDesc; } @Override public String getTrainAdjDesc() { if(trainsAtFirstLevel()>0) return "trains+"+trainsAtFirstLevel(); if(trainsAtFirstLevel()<0) return "trains"+trainsAtFirstLevel(); return ""; } @Override public String getPracAdjDesc() { if(practicesAtFirstLevel()>0) return "practices+"+practicesAtFirstLevel(); if(practicesAtFirstLevel()<0) return "practices"+practicesAtFirstLevel(); return ""; } @Override public String getAbilitiesDesc() { makeStatChgDesc(); return abilitiesDesc; } @Override public String getLanguagesDesc() { makeStatChgDesc(); return languagesDesc; } @Override public String racialParms() { return ""; } @Override public void setRacialParms(String parms) { } protected void clrStatChgDesc() { baseStatChgDesc=null; dispChgDesc=null; sensesChgDesc=null; abilitiesDesc = null; languagesDesc = null; } protected void makeStatChgDesc() { if((baseStatChgDesc == null) ||(dispChgDesc==null) ||(sensesChgDesc==null)) { StringBuilder str=new StringBuilder(""); final Session sess = (Session)CMClass.getCommon("DefaultSession"); final MOB mob=CMClass.getMOB("StdMOB"); mob.setSession(sess); mob.baseCharStats().setMyRace(this); startRacing(mob,false); mob.recoverCharStats(); mob.recoverPhyStats(); mob.recoverMaxState(); final MOB mob2=CMClass.getMOB("StdMOB"); mob2.setSession(sess); mob2.baseCharStats().setMyRace(new StdRace()); mob2.recoverCharStats(); mob2.recoverPhyStats(); mob2.recoverMaxState(); for(final int c: CharStats.CODES.ALLCODES()) { final int oldStat=mob2.charStats().getStat(c); final int newStat=mob.charStats().getStat(c); if(oldStat>newStat) str.append(CharStats.CODES.DESC(c).toLowerCase()+"-"+(oldStat-newStat)+", "); else if(newStat>oldStat) str.append(CharStats.CODES.DESC(c).toLowerCase()+"+"+(newStat-oldStat)+", "); } dispChgDesc=CMLib.flags().getDispositionStateList(mob); sensesChgDesc=CMLib.flags().getSensesStateList(mob); mob.destroy(); mob2.destroy(); baseStatChgDesc=str.toString(); if(baseStatChgDesc.endsWith(", ")) baseStatChgDesc=baseStatChgDesc.substring(0,baseStatChgDesc.length()-2); final StringBuilder astr=new StringBuilder(""); final StringBuilder lstr=new StringBuilder(""); final List<Ability> ables=new Vector<Ability>(); ables.addAll(racialAbilities(null)); final PairVector<String,Integer> cables=culturalAbilities(); Ability A=null; if(cables!=null) { for(int c=0;c<cables.size();c++) { A=CMClass.getAbility(cables.getFirst(c)); if(A!=null) { A.setProficiency(cables.getSecond(c).intValue()); ables.add(A); } } } for(final Iterator<Ability> e=ables.iterator();e.hasNext();) { A=e.next(); str = ((A.classificationCode()&Ability.ALL_ACODES)==Ability.ACODE_LANGUAGE)?lstr:astr; if(A.proficiency()<=0) str.append(A.name()+", "); else str.append(A.name()+"("+A.proficiency()+"%), "); } abilitiesDesc=astr.toString(); if(abilitiesDesc.endsWith(", ")) abilitiesDesc=abilitiesDesc.substring(0,abilitiesDesc.length()-2); languagesDesc=lstr.toString(); if(languagesDesc.endsWith(", ")) languagesDesc=languagesDesc.substring(0,languagesDesc.length()-2); } } protected static String[] CODES={"CLASS","PARMS"}; @Override public int getSaveStatIndex() { return getStatCodes().length; } @Override public String getStat(String code) { switch(getCodeNum(code)) { case 0: return ID(); case 1: return "" + racialParms(); } return ""; } @Override public void setStat(String code, String val) { switch(getCodeNum(code)) { case 0: return; case 1: setRacialParms(val); break; } } @Override public String[] getStatCodes() { return CODES; } @Override public boolean isStat(String code) { return CMParms.indexOf(getStatCodes(), code.toUpperCase().trim()) >= 0; } protected int getCodeNum(String code) { for(int i=0;i<CODES.length;i++) { if(code.equalsIgnoreCase(CODES[i])) return i; } return -1; } public boolean sameAs(Race E) { if(!(E instanceof StdRace)) return false; for(int i=0;i<CODES.length;i++) { if(!E.getStat(CODES[i]).equals(getStat(CODES[i]))) return false; } return true; } }