/* Crimson2 Mud Server * All source written/copyright Ryan Haksi 1995 * * This source code is proprietary. Use in whole or in part without * explicity permission by the author is strictly prohibited * * Current email address(es): cryogen@infoserve.net * Phone number: (604) 591-5295 * * C4 Script Language written/copyright Cam Lesiuk 1995 * Email: clesiuk@engr.uvic.ca */ #include <stdio.h> #include <stdlib.h> #include <time.h> #include "crimson2.h" #include "macro.h" #include "mem.h" #include "str.h" #include "log.h" #include "file.h" #include "queue.h" #include "thing.h" #include "base.h" #include "object.h" #include "char.h" #include "player.h" #include "fight.h" #include "affect.h" #include "effect.h" #include "cmd_inv.h" #include "skill.h" /* the reason that we have Effects, and Affects is to prevent recursion, this allows easy implementation of items that have an Effect (ie could cast a spell), apply the invisibility flag, which is an Affect... get it? put it this way in the old mud it was a real nightmare to make applies that cast a spell, since the spell would in turn create an apply which generally leads to an infinite loop situation */ #define AFFECT_ALLOC_SIZE 4096 /* list of affect flags */ BYTE *affectList[] = { "GROUP", "IMPROVED", "INVIS", "BREATHWATER", "DARKVISION", "SEEINVIS", "REGENERATION", "ENDURANCE", "HIDDEN", "PHASEWALK", "WATERWALK", "VACUUMWALK", "!FIGHTSTART", "DOMINATED", "SNEAK", "SENSELIFE", "PHASEDOOR", "" }; void AffectThing(BYTE mode, THING *thing, WORD aNum, LWORD aValue) { if (mode==AFFECT_FREE) { aValue = -aValue; } switch (aNum) { case AFFECT_NONE: break; case AFFECT_STR: if (thing->tType == TTYPE_PLR) Plr(thing)->pStr += aValue; break; case AFFECT_DEX: if (thing->tType == TTYPE_PLR) Plr(thing)->pDex += aValue; break; case AFFECT_INT: if (thing->tType == TTYPE_PLR) Plr(thing)->pInt += aValue; break; case AFFECT_WIS: if (thing->tType == TTYPE_PLR) Plr(thing)->pWis += aValue; break; case AFFECT_CON: if (thing->tType == TTYPE_PLR) Plr(thing)->pCon += aValue; break; case AFFECT_HIT: if (thing->tType >= TTYPE_CHARACTER) Character(thing)->cHitPMax += aValue; break; case AFFECT_MOVE: if (thing->tType == TTYPE_PLR) Plr(thing)->pMovePMax += aValue; break; case AFFECT_POWER: if (thing->tType == TTYPE_PLR) Plr(thing)->pPowerPMax += aValue; break; case AFFECT_ARMOR: if (thing->tType >= TTYPE_CHARACTER) Character(thing)->cArmor += aValue; break; case AFFECT_HITROLL: if (thing->tType >= TTYPE_CHARACTER) Character(thing)->cHitBonus += aValue; break; case AFFECT_DAMROLL: if (thing->tType >= TTYPE_CHARACTER) Character(thing)->cDamBonus += aValue; break; case AFFECT_RES_PUNCTURE: if (thing->tType >= TTYPE_CHARACTER) Character(thing)->cResist[0] += aValue; break; case AFFECT_RES_SLASH: if (thing->tType >= TTYPE_CHARACTER) Character(thing)->cResist[1] += aValue; break; case AFFECT_RES_CONCUSSIVE: if (thing->tType >= TTYPE_CHARACTER) Character(thing)->cResist[2] += aValue; break; case AFFECT_RES_HEAT: if (thing->tType >= TTYPE_CHARACTER) Character(thing)->cResist[3] += aValue; break; case AFFECT_RES_EMR: if (thing->tType >= TTYPE_CHARACTER) Character(thing)->cResist[4] += aValue; break; case AFFECT_RES_LASER: if (thing->tType >= TTYPE_CHARACTER) Character(thing)->cResist[5] += aValue; break; case AFFECT_RES_PSYCHIC: if (thing->tType >= TTYPE_CHARACTER) Character(thing)->cResist[6] += aValue; break; case AFFECT_RES_ACID: if (thing->tType >= TTYPE_CHARACTER) Character(thing)->cResist[7] += aValue; break; case AFFECT_RES_POISON: if (thing->tType >= TTYPE_CHARACTER) Character(thing)->cResist[8] += aValue; break; case AFFECT_SPEED: /* Make a field in char so that this works against chars */ if (thing->tType >= TTYPE_CHARACTER) Character(thing)->cSpeed += aValue; break; } } /* called by Effect, do not call this directly, Effect should provide error checking */ AFFECT *AffectCreate(THING *thing, WORD aNum, LWORD aValue, WORD aDuration, WORD eType) { AFFECT *affect; AffectThing(AFFECT_CREATE, thing, aNum, aValue); if (aDuration != AD_NONE) { /* create an affect structure and attach it */ MEMALLOC(affect, AFFECT, AFFECT_ALLOC_SIZE); affect->aNum = aNum; affect->aEffect = eType; affect->aDuration = aDuration; affect->aValue = aValue; affect->aNext = Character(thing)->cAffect; Character(thing)->cAffect = affect; } return affect; } /* called by Effect, do not call this directly, Effect should provide error checking */ void AffectReplace(THING *thing, WORD aNum, LWORD aValue, WORD aDuration, WORD eType) { AFFECT *affect; affect = AffectFind(thing, eType); if (affect) { MINSET(affect->aDuration, aDuration); return; } AffectThing(AFFECT_CREATE, thing, aNum, aValue); if (aDuration != AD_NONE) { /* create an affect structure and attach it */ MEMALLOC(affect, AFFECT, AFFECT_ALLOC_SIZE); affect->aNum = aNum; affect->aEffect = eType; affect->aDuration = aDuration; affect->aValue = aValue; affect->aNext = Character(thing)->cAffect; Character(thing)->cAffect = affect; } } /* should never be called unless Unapply was called prior */ void AffectApply(THING *thing, AFFECT *affect) { AffectThing(AFFECT_CREATE, thing, affect->aNum, affect->aValue); if (effectList[affect->aEffect].eProc) (*effectList[affect->aEffect].eProc)(EVENT_APPLY, thing, "", affect->aValue, NULL, affect->aEffect, affect, NULL); } /* Temporarily unapplies an effect, make sure AffectApply is called after */ void AffectUnapply(THING *thing, AFFECT *affect) { AffectThing(AFFECT_FREE, thing, affect->aNum, affect->aValue); if (effectList[affect->aEffect].eProc) (*effectList[affect->aEffect].eProc)(EVENT_UNAPPLY, thing, "", affect->aValue, NULL, affect->aEffect, affect, NULL); } /* Read a single affect from a file, typically a player */ void AffectRead(THING *thing, AFFECT *affect, FILE *file) { LWORD block; if (effectList[affect->aEffect].eProc) block = (*effectList[affect->aEffect].eProc)(EVENT_READ, thing, "", affect->aValue, NULL, affect->aEffect, affect, file); if (!block) { AffectReadPrimitive(thing, affect, file); } } /* Write a single affect to a file, typically a player */ void AffectWrite(THING *thing, AFFECT *affect, FILE *file) { LWORD block; if (effectList[affect->aEffect].eProc) block = (*effectList[affect->aEffect].eProc)(EVENT_WRITE, thing, "", affect->aValue, NULL, affect->aEffect, affect, file); if (!block) { AffectWritePrimitive(thing, affect, file); } } void AffectReadPrimitive(THING *thing, AFFECT *affect, FILE *file) { affect->aNum = FileByteRead(file); fscanf(file, "%hd %ld\n", &affect->aDuration, &affect->aValue); AffectApply(thing, affect); } void AffectWritePrimitive(THING *thing, AFFECT *affect, FILE *file) { fprintf(file, "%s ", effectList[affect->aEffect].eName); FileByteWrite(file, affect->aNum, ' '); fprintf(file, "%hd %ld\n", affect->aDuration, affect->aValue); } void AffectApplyAll(THING *thing) { THING *equip; AFFECT *affect; /* reapply all equips via InvApply(thing, equip); */ for (equip=thing->tContain; equip; equip=equip->tNext) if (equip->tType==TTYPE_OBJ && Obj(equip)->oEquip) InvApply(thing, equip); /* reapply all affects vi AffectApply(thing, affect); */ for (affect=Character(thing)->cAffect; affect; affect=affect->aNext) AffectApply(thing, affect); } void AffectUnapplyAll(THING *thing) { THING *equip; AFFECT *affect; /* unapply all equips via InvApply(thing, equip); */ for (equip=thing->tContain; equip; equip=equip->tNext) if (equip->tType==TTYPE_OBJ && Obj(equip)->oEquip) InvUnapply(thing, equip); /* unapply all affects vi AffectApply(thing, affect); */ for (affect=Character(thing)->cAffect; affect; affect=affect->aNext) AffectUnapply(thing, affect); } /* Turf an effect, dont give any messages */ void AffectFree(THING *thing, AFFECT *affect) { AFFECT *i; if (!thing || !affect) return; AffectThing(AFFECT_FREE, thing, affect->aNum, affect->aValue); if (Character(thing)->cAffect == affect) { Character(thing)->cAffect = affect->aNext; } else { for (i=Character(thing)->cAffect; i&&i->aNext!=affect; i=i->aNext); if (!i) { Log(LOG_ERROR, "AffectFree(affect.c): Pointer not found\n"); } else { i->aNext = affect->aNext; } } if (effectList[affect->aEffect].eProc) (*effectList[affect->aEffect].eProc)(EVENT_FREE, thing, "", affect->aValue, NULL, affect->aEffect, NULL, NULL); MEMFREE(affect, AFFECT); } AFFECT *AffectFind(THING *thing, LWORD effect) { AFFECT *i; for (i=Character(thing)->cAffect; i; i=i->aNext) { if (i->aEffect == effect) break; } return i; } /* Expire an affect noisily */ void AffectRemove(THING *thing, AFFECT *affect) { if (effectList[affect->aEffect].eProc) { ThingSetEvent(thing); (*effectList[affect->aEffect].eProc)(EVENT_UNEFFECT, thing, "", affect->aValue, NULL, affect->aEffect, NULL, NULL); } /* If the thing was moved to a new room or was free'd (killed) then it * will no longer be an event. * Used (among other places) for SpiritWalk which creates a one hit * point mob for the player to use as a body. When SpiritWalk * is called above with the UNEFFECT flag, the thing pointer is * no longer valid for the code below (ie dangling) */ if (ThingIsEvent(thing)) { AffectFree(thing, affect); ThingDeleteEvent(thing); } } /* Called by the other Tick procedures ie MobileTick and PlayerTick */ LWORD AffectTick(THING *thing) { AFFECT *i; AFFECT *next; LWORD damage; for (i=Character(thing)->cAffect; i; i=next) { next = i->aNext; /* Poisoned people take damage every tick */ if (i->aEffect == EFFECT_POISON){ damage = i->aValue; damage -= CharGetResist(thing, FD_POISON)/10; MINSET(damage, 1); /* if its killed by poison whoever its fighting gets the xp */ if (FightDamagePrimitive(Character(thing)->cFight, thing, damage)) return TRUE; } if (i->aDuration > 0) { i->aDuration -= 1; if (i->aDuration ==0) { AffectRemove(thing, i); } } } return FALSE; }