Atavism Blog

Atavism Blog

Here you will find Blog posts about atavism

  • Home
    Home This is where you can find all the blog posts throughout the site.
  • Categories
    Categories Displays a list of categories from this blog.
  • Tags
    Tags Displays a list of tags that have been used in the blog.
  • Bloggers
    Bloggers Search for your favorite blogger from this site.
  • Team Blogs
    Team Blogs Find your favorite team blogs here.
  • Login
    Login Login form
Subscribe to this list via RSS Blog posts tagged in tutorial

Posted by on in Uncategorized
AGIS Combat Traces

Most up-to-date version: AGIS Combat Traces (Google Drive)

Contents: AutoAttackTrace AbilityTrace EffectTrace CoordEffectTrace StatTrace EquipTrace

Hello everyone!

I'm fairly new to Atavism and, like many of you, am developing an MMO starting from scratch. This document started as my attempt to understand how auto-attack works but has evolved somewhat into a decent reference for the combat system. It is not complete, but it covers the basics. I define a trace as a logical chain of events in the code from start to finish. The traces involve files along the chain from player to server to database so they may refer to Java or CSharp files and include Unity, Server, and AGIS code references (AGIS requires a Veteran License). Keep in mind that many of the function references are not written verbatim from the code and are sometimes altered to make it easier to remember what certain variables represent. For example, an ability’s caster is referred to as ‘caster’, ‘obj’, ‘attacker’, and ‘source’ in different areas of the code and I simply wrote ‘attacker’ every time to keep it easy to remember. Sometimes I’ve directly copied comments from the code when they are descriptive. Furthermore, the language is often short-hand and not in complete sentences because I was moving fast. I encourage other developers to expand upon this and other plugin systems so that we can have basic references for everything.

Credits: to the Atavism Online team (Jacques and Andrew) for building the system and providing me permission to post this.

Version: Atavism 2.1.1 (will update if there are major changes, see Google Drive link at the top for most up-to-date version)

Note: This document is too long to fit into a blog entry so you'll only find part of it here and the rest on Google Drive.

AutoAttack Trace

This trace aims to document what happens from the moment a player clicks on a mob to the moment that mob’s hp is decremented. The journey through the code is a long one, but it is due to the level of sophistication of the intervening code that provides many contingencies for failure, result possibilities, and innumerable opportunities for customization.

Cursor.cs

  • Mouseover object click on ‘attackable’ object

  • NetworkAPI sendAttackMessage with object ID (Oid), attacktype (strike), attackstatus (true)

  • Message client “sent strike attack for” Oid (number displayed is not damage amount, it is object id

NetworkAPI.cs

  • SendAttackMesssage (Oid, attackType, attackStatus)

  • AutoAttackMessage sent via Client.Instance.NetworkHelper.SendMessage

CombatPlugin.java

  • AutoAttackHook intercepts msg

  • Obtain attacker id and target id and attackstatus

  • Load CombatInfo for both

  • Lock player and target CombatInfo objects and only unlock when attack is over

  • if status is false or obj dead or no target or target dead, stopAutoAttack() and set COMBAT_TAG_OWNER to next aggressive mob, else obj.setAutoAttack(targetOid)

CombatInfo.java

  • obj.setAutoAttack(targetOid), lock first and unlock when done

  • if newtarget is same as oldtarget, return (we are already attacking)

  • if not, we need to start a new attack, so setCombatState (true) and if not scheduled, schedule the auto attack with schedule(getAttackDelay() which checks attack_speed) as the update interval for CombatInfo object run() method

  • remove the old attacker from our list and if we have a null new target, we want to turn off combat by setCombatState(false), but if we do have a new attacker we want to addAttacker(target, getOwnerOid()) to the list

  • setCombatState sets COMBAT_PROP_COMBATSTATE, and broadcasts message that we are in combat and that weapons are unsheathed.

CombatPlugin.java

  • addAttacker (OID target, OID attacker)

  • lock combatplugin to do this

  • Add player’s OID as one of the attackers to this mob’s attacker hash map or create a new attacker map if it is null

  • make sure this target is tagOwner (current target?)

CombatInfo.java

  • run() method for both attacker and target is scheduled according to their attack_speed (getAttackDelay())

  • Lock the CombatInfo object for attacker and target

  • Get the attack ability id from COMBAT_PROP_AUTOATTACK_ABILITY and continue if successful

  • if target is null or the entity it points to is null, stop scheduling

  • Otherwise CombatPlugin.resolveAutoAttack(this CombatInfo) for both attacker and target and schedule next run() method with getAttackDelay()

CombatPlugin.java

  • resolveAutoAttack(this CombatInfo)

  • Get CombatInfo objects from attacker and target

  • if target is null or attack ability not valid, return

  • Some temp AI code here but the running code does the following: if the attacker isn’t the player (isUser(), COMBAT_PROP_USERFLAG false) then set the maxHealth, curHealth to health-max and health stats and healthPercent calculated as float, but these numbers aren’t used for anything locally currently.

  • Get AgisAbility object ability from abilityID

  • More temp AI code here: If the abilityID is no longer the autoattack (currently not possible), the previous AI code wants the mob to cast a spell, so a message is sent to the client that the mob begins casting the ability name.

  • Duel code: if both attacker and target are players get duelIDs for both and if they are in the same duel, then setDuelID on the autoattack ability (which does nothing as of now ?)

  • AgisAbility.startAbility(ability, info, target, null) finally executes the autoattack for attacker and target (as earlier, delay is based on attack_speed) --> See AbilityTrace

 

Ability Trace

Abilities can range from auto attack to heals to sophisticated high level damage spells and may even be general enough to be completely unrelated to combat if creativity is used.

AgisAbility.java

  • startAbility(AgisAbility ability, CombatInfo source, CombatInfo target, AgisItem item, Point loc)

  • For this ability, generateState(source, target, item, loc) which returns AgisAbilityState obj (see AgisAbilityState constructor for details--if the ability is TargetType.SELF it sets the target to the source=attacker and a message hook is setup in case the attacker is moving so the ability can be interrupted) and updateState() called

AgisAbilityState.java

  • updateState ()

  • source (attacker) and target are already defined from the generateState earlier, so this first locks the attacker and the target and all potentialTargets which may change throughout the course of the ability

  • ActivationState state is INIT by default, enum defined in AgisAbility. Ability can be in INIT, ACTIVATING, CHANNELING, ACTIVATED, COMPLETED, CANCELLED, INTERRUPTED, or FAILED states

  • Depending on state, switch will perform the correct type of update

  • For all updates, a series of checks is performed on the ability to determine whether to go forward. if the checks succeed, AbilityResult.SUCCESS is returned and if they fail, a different AbilityResult is returned and AgisAbility.interruptAbility is called with the result, which resets the states (if fails during init runs init coordinated effects of ability? --> See CoordEffectTrace ) and sends a message.

AgisAbility.java

  • checkAbility(source, target, state)

  • The checks for INIT are if the attacker is ready (not performing another action), if the target type makes sense depending on the ability target type, if the target species makes sense depending on the ability type, if the ability only works on specifically named targets and that target is selected (allows object interaction ?), if the player/target is alive, or if the target should be dead (resurrection), if the target is within range for starting the ability (too close or too far), and finally if the ability has any effect requirements that are active like just having dealt a crit, having last attack dodged, having used a certain ability within the past 5 seconds, etc (works by storing all effects player gets into a list and then listing what numeric effect values an ability requires to activate--can be >1effect required).

  • The checks for other states (including INIT) are if the attacker has/knows the ability, if the ability is a passive ability that shouldn’t be activated, if the attacker requires a tool or reagent in their inventory and if they have it, if the attacker has enough ‘costProp’ (the property required by the ability, usually mana) or vigor (which is like rage in that some abilities add vigor and some need vigor) to use the ability, if the target is within range for continuing the ability (too close or too far), and finally if they are in the correct combat stance (not fully coded).

(CombatMeleeAbility.java)

  • For CombatMeleeAbility, which is the default player auto attack ability, it extends AgisAbility, so retains the same properties as AgisAbility and adds the following: checkAbility does the default checks and then sendAbilityFailMessage with result if failure. Otherwise, checkPosition (not coded yet), then checkEquip to see which weapon type is required and if it is equipped.

  • If ACTIVATING, reset params to default, call changeCoordinatedEffect(“success”) to reset the result parameters (which may become block/partial resist later).

  • The next piece of code is confusing in regards to player vs target, especially the in-line comments. Checks target’s defenses: sets defensiveType to ‘dodged’ (default) and weapType to target’s weaponType (possible error--shouldn’t this be the weapon of the attacker?). Checks if target has a parry effect for this weapon type and if so, does the target also have the skillType required. If so, set defensiveType to ‘parried’.

  • hitChance = CombatHelper.CalcPhysicalHitChance(attacker, target, skillType)

CombatHelper.java

  • CalcPhysicalHitChance(attacker, target, skillType)

  • dexterity taken from CombatPlugin.PHYSICAL_ACCURACY_STAT, targetPerception = 20 (arbitrary, temp?), targetLevel taken, attacker skillLevel set to attacker level * 10 if attacker doesn’t have skill (arbitrary?), and to attacker’s skill if he has it.

  • hitChance = Math.atan((dexterity * skillLevel) - ((targetPerception * targetLevel)/1400) * 0.3) + 0.7

  • This calculation is arbitrary and there is a ‘New Formula’ above it commented out.

(CombatMeleeAbility.java)

  • Next get a random value [0,1] and hitRoll = rand * 100

  • If the rand > 0.95, the ability has missed. Set attackerResult in params to missed = 3, changeCoordinatedEffect(“missed”), get casterResultEffect and targetResultEffect for the miss, which are effects that are later applied to both parties in the event of the miss.

  • If the rand > hitChance, the ability was dodged or parried if the target defensiveType was set to parried earlier. Set attackerResult in params to dodged = 5, changeCoordinatedEffect(“dodged”), get caster/target result effects, similar for parry.

  • There are some other commented-out possibilities including blockChance, critChance

  • Else, the ability has hit successfully and attackerResult = 1. Even if the ability didn’t hit, AbilityResult.SUCCESS is set to continue updating the ability state.

AgisAbilityState.java

  • In updateState(), If AbilityResult.SUCCESS

  • If INIT, setCurrentAction for attacker to AgisAbilityState.INIT, then move to nextState(), run coordinated effects for this state using effect.invoke(getSourceOid(), getTargetOid(), location, this) -->See CoordEffectTrace and then another switch statement controls the new state

  • If ACTIVATING, which is next state, check the activation time and if there is an animation for it, run that (and also check the castingAffinity and send that to the attacker as well ?), then send the casting started message, set the duration of the activating state (getActivationTime()), and then schedule the run() function of AgisAbilityState to execute once that time has elapsed, which simply calls updateState() again to check the state and advance it. This then calls ability.completeActivation.

AgisAbility.java

  • completeActivation(state)

  • Gets attacker, target CombatInfo objs

  • Checks if reagents are required and if consumeReagents is active before removing reagents from inventory via AgisInventoryClient.removeGenericItem(OID, itemID, removeStack?, numToRemove)

  • Checks for a costProp (like mana) and subtracts from that property using CombatInfo.statModifyBaseValue(costProp, -activationCost), does similar with vigor stat except adds or removes (vigor is like building rage with abilities), then sendStatusUpdate() which is not coded.

  • Checks for effects that must be consumed by attacker and target (required effects or debuffs) and removes them with AgisEffect.removeEffect(EffectState existingState).

  • If attacker is player, send message to client that ability was used

  • Activate cooldowns in CooldownMap with Cooldown.activateCooldowns(cooldown collection, CombatInfo obj, quickness value = 100)

  • Smoo only? decrementWeaponUses message broadcasted ?

(CombatMeleeAbility.java)

  • For CombatMeleeAbility, it retains the same properties from completeActivation and adds the following: also setCombatState(true) on the target, (if AREA_ENEMY =AoE ability gets a list of attackable targets in radius with getAoETargets(attacker’s CombatInfo)).

  • For every activation effect, put attackerResult, skillType, hitRoll into params. Get the target of this effect, and next condition checks all AoE targets and finally calls AgisEffect.applyEffect(activationEffect(i), attacker, target, params), (-->See EffectTrace) to all targets. If not AoE, checks if the target is attacker (self) and then applies the effect with the function above before clearing the params.

  • Last, the casterResultEffect and targetResultEffect are applied (-->See EffectTrace) in the same way.

AgisAbilityState.java

  • Next it moves to nextState() which is CHANNELLING or ACTIVATED or (COMPLETED if not persistent) depending on ability and coordinated effects are run for that state -->See CoordEffectTrace.

  • If CHANNELLING, duration of the CHANNELLING state is set to the number of pulses * pulse time and it is scheduled to run again after one pulse time. ability.pulseChannelling(this) is called (which subtracts from costProp the cost per channel pulse) and nextPulse is incremented and scheduled to run after pulse time and updateState() returns so it can repeat until the number of pulses is up before moving to nextState() which is ACTIVATED or COMPLETED depending on ability persistence. For CombatMeleeAbility, AgisEffect.applyEffect(channelEffect, attacker, target) is called, -->See EffectTrace

  • If ACTIVATED, the current action is set to null since the attacker is no longer busy activating/channeling, the ability is added to active abilities via addActiveAbility(this) and update is scheduled for ActivePulseTime.

  • After return pulseActivated(this) is called (which subtracts from costProp the cost for an activePulse) and nextPulse incremented. Then the function schedules for ActivePulseTime and returns indefinitely without progressing to the next state (is this return intentional?). The only way for it to move to completed is for the state to be changed to COMPLETED elsewhere. For CombatMeleeAbility, AgisEffect.applyEffect(activeEffect, attacker, target) is called, -->See EffectTrace

  • If COMPLETED, setCurrentAction to null since we are no longer busy, terminate the casting animation and castingAffinity (?)

  • Finally unlockAll because the ability is finished

...Continued at AGIS Combat Traces (Google Drive)

 

Hits: 5208

Posted by on in Uncategorized
Vitality Stats

For all those who have wanted hunger, warmth and other survival stats in their games, Atavism now provides a new type of stat known as Vitality Stats. These stats allow a change in the value over time (known as a shift) with actions that can be set upon reaching the minimum or maximum value (such as the player/mob dying).

For example, a hunger stat can be set with a max of 100, and a min of 0. The stat can be set to increase by 2 every 6 seconds, with death occurring when hunger reaches 100. If the player doesn’t eat some food (or have some effect applied) that decreases their hunger, they will die in 300 seconds (5 minutes).

There are 3 steps in setting up a Vitality stat: Creating the max stat, creating the vitality stat, and setting the stat on the character template.

Creating the Max Stat

A max-stat needs to be set up to control the maximum value of the vitality stat. Open the Stat plugin in the Atavism Editor and create a new stat with type Base Stat. I generally put the word “max” in the max-stat name to help identify it. Leave Stat Function as none and ignore the Mob Values.

Example max stat:

b2ap3_thumbnail_Blog6-MaxStat.png

Creating the Vitality Stat

The vitality stat setup involves setting the shift data (how much the stat changes by itself over time), the requirements for the shift to occur, and what happens when the min/max values are reached.

To create a vitality stat, open the stat plugin in the Atavism Editor and set its type to Vitality Stat. Set the minimum value you want (generally 0) and set the Max Stat to the stat created in the step above. If the stat should only effect players then tick the Player Only check box (most Vitality stats will be player only, otherwise you will have random mobs and NPC's dying). Set the Shift value to how much the stat will change each update, and if the stat can be reversed (such as breath going back up when you are out of water) then set the Reverse Value. Now set the Shift Interval to how often (in seconds) you want the stat to update.

Note: Reverse mode is not functional yet and will be included in the next update.

Set the On Min Hit and/or On Max Hit to what should happen when the stat reaches either its minimum or maximum. By default the only option at the moment is death. The system is designed for more options to be added in the future.

The last part for the Vitality Stat setup is the requirements. Requirements can be set so stats only update when certain states are true for the player, such as the player being alive, or not in combat. The current version provides only these two options with support for new requirements to be added in. If the stat update is to run all the time, add no requirements. Click Save Data and the only task left is setting up the stat for your character templates.

Example vitality stat:

b2ap3_thumbnail_Blog6-VitalityStat.png

Explanation: The hunger stat goes up by 2 every 10 seconds and when it reaches the max value (which is 100) they will die.

 

Setting the Stat on the Character Template

The stat now needs to be set on each character template that has been created in the Player Character Setup plugin.

Open the Player Character Setup Plugin and for each template scroll down to the new stats and set their values. Be careful when setting the base value for the vitality stat on the character as it can result in instant death (and character creation issues) if it triggers the onMin or onMax events (for example, if the onMax action is death and the starting value for the vitality stat is equal to the max value).

Example stat values on my character template:

b2ap3_thumbnail_Blog6-CharacterStats.png

 

Other ideas

From here you may want to offer ways for players to prevent their death from a vitality stat such as adding items that can be consumed that decrease the stat, or giving them abilities they can activate. These ideas can be achieved using Stat Effects that modify the value of the vitality stat.

As Atavism development continues the plan is to add many new states and onMin/onMax actions to provide better gameplay. One example is warmth that can be increased while near certain objects (fires) and decreases when outside and based on the weather. Any adventurous programmers should be able to achieve results like this already using the base that has been programmed in.

Hits: 2206
0

Posted by on in Uncategorized
Working with movement speed

To Setting and managing the movement speed of players and mobs is an important part of game design,and a highly requested feature by developers. As of release 2.1.1 Atavism users can now easily set and work with movement speeds i their games. Working with movement speed is simple in Atavism, base movement speeds can be set for players and mobs through the Atavism Editor tool, and effects can be created to modify speed through abilities and items. These are covered below.

Setting Base Movement Speed

As of the current release (2.1.1) all mobs share the same movement speed, and player characters can ahve thier speed set by character/race template.

To set mob movement speed go into the Stats plugin in the Atavism Editor, click the Edit button and choose the movement_speed stat. If no stat with that name is listed, you can create it and give it the exact name: movement_speed. The Base Value is the speed the mobs will run at. By default a speed of 7 is suggested.

To set player movement speed go into the Player Character Setup plugin and you will find the movement speed stat listed. As stats are defined for each class/race combination you can have different speeds for different races or classes. Once again, a speed of 7 is the recommended default value. Note that any changes made here will only take effect for new characters, not per-existing ones.

Note: For those interested in Mob Controller development in Unity, the runSpeed variable from the AtavismMobController abstract class is updated from the server and should be used for showing correct movement of players and mobs.

Creating Speed altering effects

We now have the base movement speed for our mobs and players, but what about creating a speed boost or slow effect/ability. To achieve this , Stat Effects are used. To create a speed boost effect go into the Effects plugin and set the Effect Type to Stat. Set Stat 1 to movement speed and give it a value. Make sure the duration is set to greater than 0 or the effect will be removed straight away. In the example below I have created an Effect called Speed Boost which increases the movement speed by 25% and lasts for 10 seconds.

The same idea can be used for Slow Down Effects by using a negative value for the Modification value.

Creating Speed altering Equipment

The other way to alter the speed of a player/mob is to apply a movement speed stat modification to a piece of armor or a weapon. For example, a pair of boots could be given a speed boost of 2 so when equipped the player starts running faster. To achieve this, go into the Items Plugin and select the item you want to add a speed modifier to. At the bottom section, titled Item Effects, click Add Item Effect Type to Stat. Choose the movement speed stat and give it a non-zero value.

As shown, working with movement speed in Atavism is now very easy. To finish up, a handy tip is to make an ability or pair of boots that increases movement speed for any test characters to make traveling around a bit less tedious while testing.

Hits: 2344
0

Intel

Unity

S5 Box

Sign On